■課題3:inetdを使用しないサーバプログラムの作成
サンプルプログラム(1)のサーバプログラム(server.c)はinetdから起動するものであるが、inetdを使用せずに同じ動作をするデーモン型のサーバプログラムを作成し、実行結果を示すとともに、inetdを使用するサーバプログラムとそうでないものとの実装上の違いを説明せよ。

[デーモン型サーバプログラム]

#include < stdio.h>
#include < sys/types.h>
#include < sys/errno.h>
#include < string.h>
#include < ctype.h>
#include < sys/socket.h>
#include < netinet/in.h>
#include < netdb.h>
#define DATAFILE "/Users/j04017/work/jikken2/tcpip"
#define DELIMITS " ¥t¥n¥r"
char datavalue[256];
int GetLineFromPeer(int p ,char *line) {
   int i;
   
   for(i=0 ; ;) {
   if( read(p, line+i, 1) != 1 ) {
   continue;
   }
   else {
   i++;
   *(line+i) = (char)0;
   }
   if(((char)('¥n')) == (*(line+i-1)))
   return i;
   }
   }
char *GetKeywordData(char *key)
         {
   FILE *fp;
   char buff[256];
   
   if((FILE *)NULL == (fp = fopen(DATAFILE,"r")))
   return (char *)NULL;
   for( ; ;) {
   char *p;
   char *pp;
   fgets(buff,256,fp);
   
   if(feof(fp))
   break;
   
   if(ferror(fp))
   break;
   if((char *)NULL == (p = strtok(buff,DELIMITS)))
   continue;
   if((char *)NULL == (pp = strtok((char *)NULL,DELIMITS)))
   continue;
   
   if( strcmp(p,key) == 0 ) {
   fclose(fp);
   strcpy(datavalue,pp);
   return datavalue;
   }
   }
   fclose(fp);
   return (char *)" ";
   }
int main()
         {
   char RecvBuff[256];
   int sockfd;
   int wait_socket;
   int writer_len;
   struct sockaddr_in r_addr; 
   struct sockaddr_in w_addr;
   
   if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { //IPソケットを生成
   perror("socket");
   exit(1);
   }
   
   
   
   bzero((char *) &r_addr, sizeof(r_addr)); 
   r_addr.sin_family = PF_INET;
   r_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   r_addr.sin_port = htons(5681);
   
   
   if (bind(sockfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0) {
   perror("bind");
   exit(1);
   }
   
   
   if (listen(sockfd, 5) < 0) {
   perror("listen");
   close(sockfd);
   exit(1);
   }
   
   if ((wait_socket = accept(sockfd,(struct sockaddr *)&w_addr, &writer_len)) < 0) {
   perror("accept");
   exit(1);
   }
   
   for( ; ;) {
   char *p;
   char SendBuff[256];
   char *pp;
   
   (void)GetLineFromPeer(wait_socket,RecvBuff);
   if(((char)('=')) == *(RecvBuff)) {
   
   exit(0);
   }
   
   if((char *)NULL != (p = strchr(RecvBuff,'='))) {
   *p = (char)0;
   
   if((char *)NULL == (pp = GetKeywordData(RecvBuff))) {
   sprintf(SendBuff,"%s=¥n",RecvBuff);
   }
   else {
   sprintf(SendBuff,"%s=%s¥n",RecvBuff,pp);
   (void)write(wait_socket,SendBuff,strlen(SendBuff));
   }
   }
   }
   }
<<実行結果>>
[nw0417:~/work/jikken2/tcpip] j04017% myclient
 Connected.
 Input Keyword = warning: this program uses gets(), which is unsafe.
 123
 Keyword = [123] / Data = [456] 
 Input Keyword = abc
   Keyword = [xyz] / Data = [XYZ]
 Input Keyword = yama
   Keyword = [yama] / Data = [kawa]
 Input Keyword = shiro
   Keyword = [shiro] / Data = [kuro]
 Input Keyword = xxxx
   Keyword = [xxxx] / Data = [yyyy ]

[inetdを使用するサーバプログラムとそうでないものの違い]
プログラムにinetdを使用した場合、接続はすべてinetdが行う。
つまり実験3のように、プログラムにbindやacceptやlistenなどを書く必要がない。しかしinetdには欠点もあり、クライアントが要求を送るたびにサーバが立ち上がったり、終了したりする。
よって頻繁なアクセスがある。inetdの場合、サーバの実行や終了に時間がかかるので時間がかかることがある。

[関数の説明]
・socket
   この関数でソケットを作成する。この時点ではソケットに名前は付けられてはいない。
・bind
   この関数を利用することによって、ソケットに名前を付ける。つまりアドレスを与える。
・listen
   最大要求数指定し、接続を待つ。このプログラムだと最大要求数は5となる。
・accept
   listenから接続要素をとりだして、接続が済んだソケットを新規に作成する。
   そして、新しいファイルディスクリプタを割り当てて、そのファイルディスクリプタを返す。