リチャードスティーブンス氏の著作より、簡単なTCP daytimeクライアントとサーバのコード。

  • daytimetcpcli.c
#include    "unp.h"

int
main(int argc, char **argv)
{     
     int  socket_fd;
     int  read_bytes;
     char recvline[MAXLINE + 1];
     struct sockaddr_in servaddr; /* インターネットソケットアドレス構造体 */

     if (argc != 2)
          err_quit("usage: a.out ");

     /* TCPソケットの作成。インターネット(AF_INET)のストリーム(SOCK_STREAM)
      * ソケット、つまりTCPソケットを作成する。
      */
     if ( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
          err_sys("socket error");

     /* サーバのIPアドレスとポートの指定 */
     memset(&servaddr, 0, sizeof(servaddr));
     servaddr.sin_family = AF_INET;     /* アドレスファミリの指定 */
     servaddr.sin_port   = htons(13);   /* ポート番号をホストバイトオーダーから
                                         * ネットワークバイトオーダーに変換(host
                                         * to network short)する。13はdaytime se
                                         * rverのウェルノウンポート。
                                         */
     /* 引数で指定したIPアドレスを変換(presentation to numeric)して構造体に格納する */
     if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
          err_quit("inet_pton error for %s", argv[1]);


     /* ソケットアドレス構造体で指定したサーバとのTCPコネクションを確立する。
      * &serveraddrをキャストしている理由についてはP.60を参照のこと。
      */
     if (connect(socket_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
          err_sys("connect error");

     /* サーバからの応答をread()で読み取り、標準出力に書き出す。TCPはレコード境
      * 界を持たないバイトストリームプロトコルであるため、使用には注意が必要。
      */
     while ( (read_bytes = read(socket_fd, recvline, MAXLINE)) > 0) {
          /* ゼロは他方のエンドのコネクションクローズ。負の数はエラー。 */

          recvline[read_bytes] = '\0';  /* null terminate */
          if (fputs(recvline, stdout) == EOF)
               err_sys("fputs error");
     }
     if (read_bytes < 0)
          err_sys("read error");

     /* TCPソケットもファイルディスクリプタなのでexit()によりクローズされる。 */
     exit(0);
}

こちらはサーバ。先頭が大文字の関数は、エラー処理を内包したラッパ関数であることを意味する。

  • daytimetcpsrv.c
#include    "unp.h"
#include    

int
main(int argc, char **argv)
{
     int    listen_fd;
     int    connected_fd;
     char   buff[MAXLINE];
     time_t ticks;
     struct sockaddr_in servaddr;


     listen_fd = Socket(AF_INET, SOCK_STREAM, 0);

     memset(&servaddr, 0, sizeof(servaddr));
     servaddr.sin_family      = AF_INET;
     servaddr.sin_port        = htons(13);  /* daytime server */

     /* 接続を待つIPアドレスの指定。 INADDR_ANYは、どのインターフェースに宛てた
      * クライアントからのコネクションでも受け付ける事を意味する。
      * INADDR_ANY = (in_addr_t) 0x00000000
      */
     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

     /* ソケットにローカルアドレスservaddrを割り当てる。伝統的にこの処理は「ソケ
      * ットに名前をつける」と呼ばれる。
      */
     Bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

     /* listen()を呼び出し、ソケットをリスニングソケットに変換する。これにより、
      * カーネルがクライアントからのコネクションを受け付けるようになる。
      * socket, bind, listenという3段階の処理は、全てのTCPサーバがリスニングデ
      * ィスクリプタを用意する為の通常の手段である。
      */
     Listen(listen_fd, LISTENQ);

     /* クライアントからのコネクションを受け付け、応答を返す。尚、このサーバは同
      * 時に1クライアントしか扱えない事に留意すること。
      */
     for ( ; ; ) {
          /* accept()は、接続要求キューから要求を取り出し、接続済みソケットを作
           * 成し、そのソケットのディスクリプタを返す。元のソケットは影響を受け
           * ない。尚、ここではクライアントプロセスのプロトコルアドレスの取得は
           * 行わない(NULL, NULL)
           */
          connected_fd = Accept(listen_fd, (struct sockaddr *) NULL, NULL);

          ticks = time(NULL);
          snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));

          /* 接続済みディスクリプタに値を書き込む。これによりクライアントへデー
           * タが送信される。
           */
          Write(connected_fd, buff, strlen(buff));

          /* クライアントとのコネクションをクローズする。これによりTCPコネクショ
           * ンの通常終了シーケンス(双方向のFIN等)が起動される。
           */
          Close(connected_fd);
     }
}

サーバ側でクライアントのIPアドレスとポートを出力するには、daytimetcpsrv.cを以下のように変更する。

 --- intro/daytimetcpsrv.c.org     1997-05-09 06:00:59.000000000 +0900
 +++ intro/daytimetcpsrv.c         1997-06-06 20:40:57.000000000 +0900
 @@ -5,7 +5,8 @@ int
  main(int argc, char **argv)
  {
         int                     listenfd, connfd;
 -       struct sockaddr_in      servaddr;
 +       socklen_t               len;
 +       struct sockaddr_in      servaddr, cliaddr;
         char                    buff[MAXLINE];
         time_t                  ticks;
 
 @@ -21,7 +22,11 @@ main(int argc, char **argv)
         Listen(listenfd, LISTENQ);
 
         for ( ; ; ) {
 -               connfd = Accept(listenfd, (struct sockaddr *) NULL, NULL);
 +               len = sizeof(cliaddr);
 +               connfd = Accept(listenfd, (struct sockaddr *) &cliaddr, &len);
 +               printf("connection from %s, port %d\n",
 +                          Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
 +                          ntohs(cliaddr.sin_port));
 
          ticks = time(NULL);
          snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));