筑波大学 システム情報系 情報工学域 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2019/2019-06-12
/echo-server-pthread.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2019/
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: */ 6: #include <stdio.h> 7: #include <stdlib.h> /* exit() */ 8: #include <sys/types.h> /* socket(), time() */ 9: #include <sys/socket.h> /* socket() */ 10: #include <netinet/in.h> /* struct sockaddr_in */ 11: #include <pthread.h> /* pthread_create */ 12: #include <netdb.h> /* getnameinfo() */ 13: #include <string.h> /* strlen() */ 14: #include <unistd.h> /* getpid(), gethostname() */ 15: 16: struct echo_server_thread_arg; 17: 18: extern void echo_server( int portno, int ip_version ); 19: extern void *echo_server_thread( struct echo_server_thread_arg *arg ); 20: 21: extern void echo_receive_request_and_send_reply( int com ); 22: extern int echo_receive_request( char *line, size_t size, FILE *in ); 23: extern void echo_send_reply( char *line, FILE *out ); 24: extern void print_my_host_port( int portno ); 25: extern void tcp_sockaddr_print( int com ); 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 = 46; /* Both IPv4 and IPv6 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: tcp_sockaddr_print( acc ); 64: while( 1 ) 65: { 66: printf("[%d] accepting incoming connections (acc==%d) ...\n", 67: getpid(),acc ); 68: if( (com = accept( acc,0,0 )) < 0 ) 69: { 70: perror("accept"); 71: exit( -1 ); 72: } 73: tcp_peeraddr_print( com ); 74: arg = malloc( sizeof(struct echo_server_thread_arg) ); 75: if( arg == NULL ) 76: { 77: perror("malloc()"); 78: exit( -1 ); 79: } 80: arg->com = com; 81: if( pthread_create( &worker, NULL, (void *)echo_server_thread, (void *)arg) 82: != 0 ) 83: { 84: perror("pthread_create()"); 85: exit( 1 ); 86: } 87: pthread_detach( worker ); 88: } 89: } 90:
このプログラムの特徴を以下にまとめる。
91: void * 92: echo_server_thread( struct echo_server_thread_arg *arg ) 93: { 94: echo_receive_request_and_send_reply( arg->com ); 95: free( arg ); 96: return( NULL ); 97: } 98: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 2019-06-10 15:54:19.003067000 +0900 --- echo-server-pthread.c 2019-06-10 16:20:54.182336000 +0900 *************** *** 1,21 **** /* ! echo-server-fork-fdopen.c -- 受け取った文字列をそのまま返すサーバ(fork版) ! ~yas/syspro/ipc/echo-server-fork-fdopen.c */ #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,23 ---- /* ! echo-server-pthread.c -- 受け取った文字列をそのまま返すサーバ(pthread版) ! ~yas/syspro/ipc/echo-server-pthread.c */ #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 ) *************** *** 56,62 **** tcp_sockaddr_print( acc ); while( 1 ) { - delete_zombie(); printf("[%d] accepting incoming connections (acc==%d) ...\n", getpid(),acc ); if( (com = accept( acc,0,0 )) < 0 ) --- 63,68 ---- *************** *** 65,98 **** 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 --- 71,99 ---- 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 aloe30.local 1231
[16505] accepting (fd==4) to [::]:1231
[16505] accepting incoming connections (acc==4) ...
[16505] connection (fd==5) from [2001:2f8:3a:1711::230:39]:53138
[16505] accepting incoming connections (acc==4) ...
[16505] received (fd==5) 5 bytes, [012
]
[16505] connection (fd==7) from [2001:2f8:3a:1711::230:40]:63507
[16505] accepting incoming connections (acc==4) ...
[16505] received (fd==7) 5 bytes, [abc
]
[16505] received (fd==7) 5 bytes, [def
]
[16505] received (fd==5) 5 bytes, [345
]
[16505] connection (fd==5) closed.
[16505] connection (fd==7) closed.
^C
$
クライアント側(その1)。
$ telnet aloe30 1231
Trying 2001:2f8:3a:1711::230:30...
Connected to aloe30.
Escape character is '^]'.
012
012
345
345
^]
telnet> ^D
Connection closed.
$
クライアント側(その2)。
$ telnet aloe30 1231
Trying 2001:2f8:3a:1711::230:30...
Connected to aloe30.
Escape character is ^]'.
abc
abc
def
def
^]
telnet> ^D
Connection closed.
$
'