システムプログラム(第8週): Pthreadによる複数のクライアントに対するサービスの同時提供

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2011/2011-06-15/echo-server-pthread.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2011/
http://www.coins.tsukuba.ac.jp/~yas/

Ptheadとは

Pthread とは、C言語から使えるスレッド・ライブラリである。 オペレーティングシステムIIの資料 に簡単な説明がある。

echo-server-pthread.c

fork版 では、クライアントから接続要 求を受け付けるたびに、新しいプロセスを作っていた。以下の 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:	            if( (com = accept( acc,0,0 )) < 0 )
  58:	            {
  59:	                perror("accept");
  60:	                exit( -1 );
  61:	            }
  62:	            tcp_peeraddr_print( com );
  63:	            arg = malloc( sizeof(struct echo_server_thread_arg) );
  64:	            if( arg == NULL )
  65:	            {
  66:	                perror("malloc()");
  67:	                exit( -1 );
  68:	            }
  69:	            arg->com = com;
  70:	            if( pthread_create( &worker, NULL, (void *)echo_server_thread, (void *)arg)
  71:	                != 0 )
  72:	            {
  73:	                perror("pthread_create()");
  74:	                exit( 1 );
  75:	            }
  76:	            pthread_detach( worker );
  77:	        }
  78:	}
  79:	

このプログラムの特徴を以下にまとめる。

  80:	void *
  81:	echo_server_thread( struct echo_server_thread_arg *arg )
  82:	{
  83:	        echo_receive_request_and_send_reply( arg->com );
  84:	        free( arg );
  85:	        return( NULL );
  86:	}

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   2010-06-07 21:03:44.000000000 +0900
 --- echo-server-pthread.c       2010-06-07 22:14:39.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,88 ****
	 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();
	     if( (com = accept( acc,0,0 )) < 0 )
	     {
		 perror("accept");
		 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
 --- 38,88 ----
	 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 )
	 {
	     if( (com = accept( acc,0,0 )) < 0 )
	     {
		 perror("accept");
		 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 を使うとプログラムが動かないことがある。

$ ./echo-server-pthread  1231 [←]
run telnet cosmos10(v6) 1231 
[15826] connection (fd==4) from 130.158.86.151:62141
[15826] received (fd==4) 5 bytes, [012
]
[15826] connection (fd==6) from 130.158.86.151:62142
[15826] received (fd==6) 5 bytes, [abc
]
[15826] received (fd==6) 5 bytes, [def
]
[15826] connection (fd==6) closed.
[15826] received (fd==4) 5 bytes, [345
]
[15826] connection (fd==4) 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> ^D
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> ^D
Connection closed.
$ []

Last updated: 2011/06/13 17:26:04
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>