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;
}