筑波大学 システム情報系 情報工学域 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2012/2012-06-06
/echo-server-pthread.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2012/
http://www.coins.tsukuba.ac.jp/~yas/
echo-server-pthread.c
では、プロセスではなく
スレッドを作っている。
1: 2: /* 3: echo-server-pthread.c -- 受け取った文字列をそのまま返すサーバ(pthread版) 4: ~yas/syspro/ipc/echo-server-pthread.c 5: Created on 2002/02/04 21:17:04 6: */ 7: #include <stdio.h> 8: #include <stdlib.h> /* exit() */ 9: #include <sys/types.h> /* socket(), time() */ 10: #include <sys/socket.h> /* socket() */ 11: #include <netinet/in.h> /* struct sockaddr_in */ 12: #include <pthread.h> /* pthread_create */ 13: #include <netdb.h> /* getnameinfo() */ 14: #include <string.h> /* strlen() */ 15: 16: struct echo_server_thread_arg; 17: extern void echo_server( int portno, int ip_version ); 18: extern void echo_receive_request_and_send_reply( int com ); 19: extern void *echo_server_thread( struct echo_server_thread_arg *arg ); 20: extern void print_my_host_port( int portno ); 21: extern void tcp_peeraddr_print( int com ); 22: extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); 23: extern int tcp_acc_port( int portno, int pf ); 24: extern int fdopen_sock( int sock, FILE **inp, FILE **outp ); 25: 26: main( int argc, char *argv[] ) 27: { 28: int portno, ip_version; 29: if( !(argc == 2 || argc==3) ) { 30: fprintf(stderr,"Usage: %s portno {ipversion}\n",argv[0] ); 31: exit( 1 ); 32: } 33: portno = strtol( argv[1],0,10 ); 34: if( argc == 3 ) 35: ip_version = strtol( argv[2],0,10 ); 36: else 37: ip_version = 4; /* IPv4 by default */ 38: echo_server( portno, ip_version ); 39: } 40:main() は、 forkなし版 や fork版 とほとんど同じである。
41: struct echo_server_thread_arg { 42: int com; 43: }; 44: 45: void 46: echo_server( int portno, int ip_version ) 47: { 48: int acc,com ; 49: pthread_t worker ; 50: struct echo_server_thread_arg *arg; 51: acc = tcp_acc_port( portno, ip_version ); 52: if( acc<0 ) 53: exit( -1 ); 54: print_my_host_port( portno ); 55: while( 1 ) 56: { 57: printf("[%d] accepting incoming connections (fd==%d) ...\n",getpid(),acc ); 58: if( (com = accept( acc,0,0 )) < 0 ) 59: { 60: perror("accept"); 61: exit( -1 ); 62: } 63: tcp_peeraddr_print( com ); 64: arg = malloc( sizeof(struct echo_server_thread_arg) ); 65: if( arg == NULL ) 66: { 67: perror("malloc()"); 68: exit( -1 ); 69: } 70: arg->com = com; 71: if( pthread_create( &worker, NULL, (void *)echo_server_thread, (void *)arg) 72: != 0 ) 73: { 74: perror("pthread_create()"); 75: exit( 1 ); 76: } 77: pthread_detach( worker ); 78: } 79: } 80:
このプログラムの特徴を以下にまとめる。
81: void * 82: echo_server_thread( struct echo_server_thread_arg *arg ) 83: { 84: echo_receive_request_and_send_reply( arg->com ); 85: free( arg ); 86: return( NULL ); 87: } 88:echo_server_thread() は、スレッドが生成された時に最初に呼ばれる関数であ る。引数の構造体から com を取り出し、 echo_receive_request_and_send_reply() を呼んでいる。それからリターンし た後には、引数の構造体を free している。
他の関数は、fork版、forkなし版とと同じである。
$ diff -c echo-server-fork-fdopen.c echo-server-pthread.c *** echo-server-fork-fdopen.c 2012-06-04 15:05:44.000000000 +0900 --- echo-server-pthread.c 2012-06-04 15:07:08.000000000 +0900 *************** *** 1,22 **** /* ! echo-server-fork-fdopen.c -- 受け取った文字列をそのまま返すサーバ(fork版) ! ~yas/syspro/ipc/echo-server-fork-fdopen.c ! Created on 2004/05/09 19:57:07 */ #include <stdio.h> #include <stdlib.h> /* exit() */ ! #include <sys/types.h> /* socket(), wait4() */ #include <sys/socket.h> /* socket() */ #include <netinet/in.h> /* struct sockaddr_in */ ! #include <sys/resource.h> /* wait4() */ ! #include <sys/wait.h> /* wait4() */ #include <netdb.h> /* getnameinfo() */ #include <string.h> /* strlen() */ extern void echo_server( int portno, int ip_version ); - extern void delete_zombie(); extern void echo_receive_request_and_send_reply( int com ); extern void print_my_host_port( int portno ); extern void tcp_peeraddr_print( int com ); extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); --- 1,22 ---- /* ! echo-server-pthread.c -- 受け取った文字列をそのまま返すサーバ(pthread版) ! ~yas/syspro/ipc/echo-server-pthread.c ! Created on 2002/02/04 21:17:04 */ #include <stdio.h> #include <stdlib.h> /* exit() */ ! #include <sys/types.h> /* socket(), time() */ #include <sys/socket.h> /* socket() */ #include <netinet/in.h> /* struct sockaddr_in */ ! #include <pthread.h> /* pthread_create */ #include <netdb.h> /* getnameinfo() */ #include <string.h> /* strlen() */ + struct echo_server_thread_arg; extern void echo_server( int portno, int ip_version ); extern void echo_receive_request_and_send_reply( int com ); + extern void *echo_server_thread( struct echo_server_thread_arg *arg ); extern void print_my_host_port( int portno ); extern void tcp_peeraddr_print( int com ); extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); *************** *** 38,55 **** echo_server( portno, ip_version ); } void echo_server( int portno, int ip_version ) { int acc,com ; ! pid_t child_pid ; acc = tcp_acc_port( portno, ip_version ); if( acc<0 ) exit( -1 ); print_my_host_port( portno ); while( 1 ) { - delete_zombie(); printf("[%d] accepting incoming connections (fd==%d) ...\n",getpid(),acc ); if( (com = accept( acc,0,0 )) < 0 ) { --- 38,59 ---- echo_server( portno, ip_version ); } + struct echo_server_thread_arg { + int com; + }; + void echo_server( int portno, int ip_version ) { int acc,com ; ! pthread_t worker ; ! struct echo_server_thread_arg *arg; acc = tcp_acc_port( portno, ip_version ); if( acc<0 ) exit( -1 ); print_my_host_port( portno ); while( 1 ) { printf("[%d] accepting incoming connections (fd==%d) ...\n",getpid(),acc ); if( (com = accept( acc,0,0 )) < 0 ) { *************** *** 57,89 **** exit( -1 ); } tcp_peeraddr_print( com ); ! if( (child_pid=fork()) > 0 ) /* parent */ { ! close( com ); } ! else if( child_pid == 0 ) /* child */ { ! close( acc ); ! echo_receive_request_and_send_reply( com ); ! exit( 0 ); } ! else ! { ! perror("fork"); ! exit( -1 ); ! } } } ! void ! delete_zombie() { ! pid_t pid ; ! while( (pid=wait4(-1,0,WNOHANG,0)) >0 ) ! { ! printf("[%d] zombi %d deleted.\n",getpid(),pid ); ! continue; ! } } #define BUFFERSIZE 1024 --- 61,89 ---- exit( -1 ); } tcp_peeraddr_print( com ); ! arg = malloc( sizeof(struct echo_server_thread_arg) ); ! if( arg == NULL ) { ! perror("malloc()"); ! exit( -1 ); } ! arg->com = com; ! if( pthread_create( &worker, NULL, (void *)echo_server_thread, (void *)arg) ! != 0 ) { ! perror("pthread_create()"); ! exit( 1 ); } ! pthread_detach( worker ); } } ! void * ! echo_server_thread( struct echo_server_thread_arg *arg ) { ! echo_receive_request_and_send_reply( arg->com ); ! free( arg ); ! return( NULL ); } #define BUFFERSIZE 1024実行例。
サーバ側。サーバは、終了しないので、最後に、^C を押して、割り 込みを掛けて終了させる。
注意:全員がポート番号 1231 を使うとプログラムが動かないことがある。
$ cp ~yas/syspro/ipc/echo-server-pthread.c .
$ make echo-server-pthread
cc echo-server-pthread.c -o echo-server-pthread
$ ./echo-server-pthread
Usage: ./echo-server-pthread portno {ipversion}
$ ./echo-server-pthread 1231
run telnet cosmos10(v6) 1231
[17385] accepting incoming connections (fd==3) ...
[17385] connection (fd==4) from 130.158.86.150:57810
[17385] accepting incoming connections (fd==3) ...
[17385] received (fd==4) 5 bytes, [012
]
[17385] connection (fd==6) from 130.158.86.1:54729
[17385] accepting incoming connections (fd==3) ...
[17385] received (fd==6) 5 bytes, [abc
]
[17385] received (fd==6) 5 bytes, [def
]
[17385] received (fd==4) 5 bytes, [345
]
[17385] connection (fd==4) closed.
[17385] connection (fd==6) closed.
^C
$
クライアント側(その1)。
$ telnet cosmos10 1231
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
012
012
345
345
^]
telnet> quit
Connection closed.
$
クライアント側(その2)。
$ telnet cosmos10 1231
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc
abc
def
def
^]
telnet> quit
Connection closed.
$