[an error occurred while processing this directive] [an error occurred while processing this directive]

サーバ・クライアントのサンプル

TCP/IP ついて、 とりあえずコピー & ペーストしたら動く、ソケット (socket, connect, bindとか) のサンプルプログラムです。 サーバとクライアントの両方を含んでいます。

使い方

ダウンロードして、コンパイルして、サーバとクライアントの二つを実行します。 同じマシンで二つ実行しても大丈夫です。 動作としては、サーバを先に立ち上げておき、それにクライアントが接続します。 接続されると、サーバはクライアントにデータを送ります。 送り終わると、双方接続を閉じて終了します。
$ gcc socket_sample.c -o socket_sample
ターミナルを二つ立ち上げて、一つで
$ ./socket_sample 8011
として、サーバを起動します。(8011はポート番号で、好きな番号を指定してください)
その上で、
$ ./socket_sample 8011 127.0.0.1
として、クライアントを起動してサーバに接続します。 8011 は先ほどと同じポート番号を指定してください。 127.0.0.1 というところは、サーバの IP アドレスを指定します。 サーバとクライアントを同じマシンで立ち上げる場合は、 127.0.0.1 で大丈夫です。
うまくいくと、データが 100回転送され、それぞれの転送の バンド幅が表示されます。

ソースコード

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <netinet/ip.h>
#include <netdb.h>

#define LEN    1024 * 1024 * 4
#define TRIALS 100

#define MY_RCVBUF 1024 * 10


/***************************************************/

/* basic network functions come here: */

/* サーバ・クライアント のモード*/
enum {
  MODE_SERVER = 10,
  MODE_CLIENT = 11,
};


/* コマンドラインからの設定項目 */
struct config {
  int mode;
  int port;
  char *hostname;
};


void error_exit(char *cp){
  /* エラーを出力して終了 */
  perror(cp);
  exit(1);
}


double get_dtime(void){
  /* 時間を double 型で返す */
  struct timeval tv;
  struct timezone tz;
  gettimeofday(&tv, &tz);
  return (double)(tv.tv_sec) + (double)(tv.tv_usec) * 0.001 * 0.001;
}


void usage(char *cmd){
  /* 使い方を出力 */
  printf("Usage: %s [port] ( [server_name] )\n", cmd);
  printf("  example: %s 5011             ... for server\n", cmd);
  printf("           %s 5011 10.0.127.13 ... for client\n", cmd);
  exit(1);
}


void read_config(struct config *config, int args, char **argv){
  /* コマンドラインから設定を読み込む */
  assert(config != NULL);
  
  if(args == 2){
    // only port is specified -> server mode
    config->mode = MODE_SERVER;    
    config->port = atoi(argv[1]);
  } else if(args == 3){
    // only port is specified -> server mode
    config->mode = MODE_CLIENT;
    config->port = atoi(argv[1]);
    config->hostname = argv[2];
  } else {
    usage(argv[0]);
  }
}


/***************************************************/

int prepare_server_socket(int port){
  /* サーバソケットの準備 */
  int ret;
  int sock;
  struct sockaddr_in sa;

  /* ソケット生成 */
  sock = socket(PF_INET, SOCK_STREAM, 0);
  
  /* bind: ソケットをポートに割り当てる*/
  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = INADDR_ANY;
  sa.sin_port = htons(port);
  ret = bind(sock, (struct sockaddr*)&sa, sizeof(sa));
  if(ret == -1) error_exit("bind");
  
  /* listen: 待ち受けを始める */
  ret = listen(sock, 5);
  if(ret == -1) error_exit("listen");
  return sock;
}


int close_server_socket(int server_sock){
  /* ソケットのクローズ */
  close(server_sock);
}


int do_accept(int server_sock){
  /* 接続を受け付け、ソケットを返す */
  int sock = accept(server_sock, NULL, NULL);
  if(sock == -1) error_exit("accpet");
  return sock;
}


void close_sock(int sock){
  /* ソケットを閉じる */
  close(sock);
}


int do_connect(char *hostaddr, int port){
  /* サーバに connect する。*/
  /* ホスト名ではなく、IP アドレスを指定してください。*/
  int ret;
  int sock;
  struct sockaddr_in sa;

  sock = socket(PF_INET, SOCK_STREAM, 0);
  sa.sin_family = AF_INET;
  printf("connect to %s\n", hostaddr);
  inet_aton(hostaddr, &(sa.sin_addr)); 
  sa.sin_port = htons(port);

  ret = connect(sock, (struct sockaddr *)&sa, sizeof(sa));
  if(ret == -1) error_exit("connect");
  return sock;
}


void do_send(int sock, int size, char *buf){
  /* size の量のデータを送る */
  /* WAITALL を指定することで、指定した量送られるまで関数が返らない */
  int ret = send(sock, buf, size, MSG_WAITALL);
  if(ret != size)
    error_exit("Send interrupted");  
}


void do_recv(int sock, int size, char *buf){
  /* size の量のデータを受信する */
  /* WAITALL を指定することで、指定した量受け取るまで関数が返らない */
  int ret = recv(sock, buf, size, MSG_WAITALL);
  if(ret != size)
    error_exit("Recv interrupted");
}

/*
受信バッファの値を設定
void set_rcvbuf(int sock, int bufsize){
  
  int ret;
  int i;
  getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&i, 4);
  printf("original RCVBUF: %d\n", i);

  ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
		       (char *)&bufsize, sizeof(bufsize));
  if(ret < 0)
    error_exit("setsockopt");
}
*/

/***************************************************/

void client_core(int sock){
  int i;
  char buf[LEN];
  double d0, d1, bandwidth;
  
  //  set_rcvbuf(sock, MY_RCVBUF);
  
  for(i = 0; i < TRIALS; i++){
    d0 = get_dtime();
    do_recv(sock, LEN, buf);
    d1 = get_dtime();
    bandwidth = (double)LEN * 8 / (d1-d0) / 1000 / 1000;
    printf("client: %d, bandwidth: %f Mbps\n", i, bandwidth);
  }
}


void server_core(int sock){
  int i;
  char buf[LEN];

  for(i = 0; i < TRIALS; i++){
    do_send(sock, LEN, buf);
    printf("server: %d\n", i);
  }
}



/***************************************************/

int server(struct config *cfg){
  /* 何回でも接続を受け付け、データを送信する */
  int server_sock = prepare_server_socket(cfg->port);
  for(;;){
    int sock = do_accept(server_sock);
    server_core(sock);
    close_sock(sock);
  }
  close_server_sock(server_sock);
}


int client(struct config *cfg){
  /* サーバに一回だけ接続し、データを受信し、終了する。*/
  int sock = do_connect(cfg->hostname, cfg->port);
  client_core(sock);
  close_sock(sock);
}


/***************************************************/

int main(int args, char **argv){
  int mode;
  struct config cfg;

  read_config(&cfg, args, argv);

  switch(cfg.mode){
  case MODE_SERVER:
    server(&cfg);
    break;

  case MODE_CLIENT:
    client(&cfg);
    break;
    
  default:
    assert(0);
  }
  
  return 0;
}

雑多な話題

[an error occurred while processing this directive]