筑波大学 システム情報系 情報工学域
新城 靖
<yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2017/2017-06-07
/echo-server-pthread.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2017/
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 2017-07-31 11:25:38.000000000 +0900
--- echo-server-pthread.c 2017-07-31 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.
$