筑波大学 システム情報系 情報工学域 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2018/2018-06-06
/echo-server-pthread.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2018/
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: #include <unistd.h> /* getpid(), gethostname() */ 16: 17: struct echo_server_thread_arg; 18: 19: extern void echo_server( int portno, int ip_version ); 20: extern void *echo_server_thread( struct echo_server_thread_arg *arg ); 21: 22: extern void echo_receive_request_and_send_reply( int com ); 23: extern int echo_receive_request( char *line, size_t size, FILE *in ); 24: extern void echo_send_reply( char *line, FILE *out ); 25: extern void print_my_host_port( int portno ); 26: extern void tcp_peeraddr_print( int com ); 27: extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); 28: extern int tcp_acc_port( int portno, int pf ); 29: extern int fdopen_sock( int sock, FILE **inp, FILE **outp ); 30: 31: int 32: main( int argc, char *argv[] ) 33: { 34: int portno, ip_version; 35: 36: if( !(argc == 2 || argc==3) ) { 37: fprintf(stderr,"Usage: %s portno {ipversion}\n",argv[0] ); 38: exit( 1 ); 39: } 40: portno = strtol( argv[1],0,10 ); 41: if( argc == 3 ) 42: ip_version = strtol( argv[2],0,10 ); 43: else 44: ip_version = 4; /* IPv4 by default */ 45: echo_server( portno, ip_version ); 46: } 47:main() は、 forkなし版 や fork版 とほとんど同じである。
48: struct echo_server_thread_arg { 49: int com; 50: }; 51: 52: void 53: echo_server( int portno, int ip_version ) 54: { 55: int acc,com ; 56: pthread_t worker ; 57: struct echo_server_thread_arg *arg; 58: 59: acc = tcp_acc_port( portno, ip_version ); 60: if( acc<0 ) 61: exit( 1 ); 62: print_my_host_port( portno ); 63: while( 1 ) 64: { 65: printf("[%d] accepting incoming connections (acc==%d) ...\n", 66: getpid(),acc ); 67: if( (com = accept( acc,0,0 )) < 0 ) 68: { 69: perror("accept"); 70: exit( -1 ); 71: } 72: tcp_peeraddr_print( com ); 73: arg = malloc( sizeof(struct echo_server_thread_arg) ); 74: if( arg == NULL ) 75: { 76: perror("malloc()"); 77: exit( -1 ); 78: } 79: arg->com = com; 80: if( pthread_create( &worker, NULL, (void *)echo_server_thread, (void *)arg) 81: != 0 ) 82: { 83: perror("pthread_create()"); 84: exit( 1 ); 85: } 86: pthread_detach( worker ); 87: } 88: } 89:
このプログラムの特徴を以下にまとめる。
90: void * 91: echo_server_thread( struct echo_server_thread_arg *arg ) 92: { 93: echo_receive_request_and_send_reply( arg->com ); 94: free( arg ); 95: return( NULL ); 96: } 97: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 2018-07-30 11:25:38.000000000 +0900 --- echo-server-pthread.c 2018-07-30 12:19:33.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() */ #include <unistd.h> /* getpid(), gethostname() */ extern void echo_server( int portno, int ip_version ); ! extern void delete_zombie(); extern void echo_receive_request_and_send_reply( int com ); extern int echo_receive_request( char *line, size_t size, FILE *in ); extern void echo_send_reply( char *line, FILE *out ); --- 1,24 ---- /* ! 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() */ #include <unistd.h> /* getpid(), gethostname() */ + struct echo_server_thread_arg; + extern void echo_server( int portno, int ip_version ); ! extern void *echo_server_thread( struct echo_server_thread_arg *arg ); ! extern void echo_receive_request_and_send_reply( int com ); extern int echo_receive_request( char *line, size_t size, FILE *in ); extern void echo_send_reply( char *line, FILE *out ); *************** *** 43,53 **** 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 ) --- 45,60 ---- 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 ) *************** *** 55,61 **** print_my_host_port( portno ); while( 1 ) { - delete_zombie(); printf("[%d] accepting incoming connections (acc==%d) ...\n", getpid(),acc ); if( (com = accept( acc,0,0 )) < 0 ) --- 62,67 ---- *************** *** 64,97 **** 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 --- 70,98 ---- 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 crocus39.coins.tsukuba.ac.jp 1231
[612] accepting incoming connections (acc==3) ...
[612] connection (fd==4) from 130.158.86.247:50567
[612] accepting incoming connections (acc==3) ...
[612] received (fd==4) 5 bytes, [012
]
[612] connection (fd==6) from 130.158.86.248:50482
[612] accepting incoming connections (acc==3) ...
[612] received (fd==6) 5 bytes, [abc
]
[612] received (fd==6) 5 bytes, [def
]
[612] received (fd==4) 5 bytes, [345
]
[612] connection (fd==4) closed.
[612] connection (fd==6) closed.
^C
$
クライアント側(その1)。
$ telnet crocus39.coins.tsukuba.ac.jp 1231
Trying 130.158.86.249...
Connected to crocus39.coins.tsukuba.ac.jp.
Escape character is '^]'.
012
012
345
345
^]
telnet> ^D
Connection closed.
$
クライアント側(その2)。
$ telnet crocus39.coins.tsukuba.ac.jp 1231
Trying 130.158.86.249...
Connected to crocus39.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc
abc
def
def
^]
telnet> ^D
Connection closed.
$