TCP/IP サーバー・プログラムの例を紹介しよう。
TCP/IP通信ではあらかじめクライアントからの要求に応答するサーバーとしての
プログラムを待機しておかなくてはならない。
UNIX や SNA であれば起動側から受動側のプログラムを通信上で起動することができるが、
一般的な TCP/IPでは通信の相手側のプログラムを起動する機能はないので通信を開始する事前に
サーバー・プログラムを起動しておかなければならない。
このようなサーバー・プログラムの JOB のことを「サーバー・デーモン」と呼ぶ。
(サーバー・デーモンとは UNIX から使用されていた用語である)
次に TCP/IPで通信するには ソケット(Socket) と呼ばれる通信用の識別子によって通信を行う。
つまり Socket識別子とは TCP/IPの通信用のハンドルのことである。
TCP/IPによるサーバー・デーモンの処理の手順は概ね次のステップに従って行う。
- socket 関数によって Socket識別子を生成する。
- setsockopt 関数によって Socket識別子のオプションを設定する。
- bind 関数によって Socket を PORT に割り当てる。
- listen 関数によってクライアントからの要求信号を待機する。
- accept 関数によってクライアント要求を受け入れて新たにクライアントとの
通信用の Socket識別子を派生する。 - recv または send 関数によって通信を開始する。
- close 関数によって Socketをクローズして終了する。
【 SOCKSVR: SOCKET による TCP/IPサーバー・デーモン ( C/400) 】
/********************************************************************** /* /* SOCKSVR : TEST SOCKET SERVER /* /********************************************************************** #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <errno.h> #define TRUE 0 #define FALSE -1 #define TCP_LEN 1492 typedef struct { int BYTESPRO; int BYTESAVL; char MSGID[7]; char RESRVD; char EXCPDATA[100]; } ERRSTRUCTURE; /* Define the error return structure */ ERRSTRUCTURE errcode;/* Error Code Structure for RCVMSG */ int PORT = 400; void INZSR(void); void err_log(char* msg, int opt, char* function, int line); void main(void){ int sockfd, len, rc, newfd, on = 1; struct sockaddr_in iaddr; char buff[48]; char errmsg[128]; printf("** SOCKSVR **n"); getchar(); INZSR(); /*-------------------------------------------------------*/ /* socket : Get a socket descriptor */ /*-------------------------------------------------------*/ if((sockfd = socket(AF_INET, SOCK_STREAM, 0 )) < 0){ perror("SOCKET"); getchar(); exit(0); } printf("* SOCKET OKn"); if((rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on))) < 0){ perror("SOCKOPT"); getchar(); exit(0); } printf("* SOCKOPT OKn"); /*-------------------------------------------------------*/ /* bind : bind address to the socket */ /*-------------------------------------------------------*/ memset(&iaddr, 0x00, sizeof(struct sockaddr_in)); iaddr.sin_family = AF_INET; iaddr.sin_port = htons(PORT); /* PORT no set*/ iaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* any address OK */ if(bind(sockfd, (struct sockaddr *)&iaddr, sizeof(iaddr)) < 0){ perror("BIND"); getchar(); exit(0); } printf("* BIND OKn"); /*---------------------------------------------------------*/ /* listen : wait for new client's call */ /* Up to 5 clients can be queued */ /*---------------------------------------------------------*/ rc = listen(sockfd, 10); if(rc < 0){/* LISTEN */ perror("LISTEN"); getchar(); exit(0); }/* LISTEN */ printf("* LISTEN OKn"); /*---------------------------------------------------------*/ /* accept : accept new client's request */ /* from parent, should block */ /*---------------------------------------------------------*/ newfd = accept(sockfd, (struct sockaddr *)NULL, NULL); if(newfd < 0){/*failed*/ perror("ACCEPT"); getchar(); exit(0); }/*failed*/ printf("* ACCEPT OKn"); /*---------------------------------------------------------*/ /* RECEV : receive message from the client */ /*---------------------------------------------------------* len = 48; memset(buff, 0, sizeof(buff)); rc = recv(newfd, buff, TCP_LEN, 0); if(rc < 0){ perror("RECV"); getchar(); exit(0); } printf("* RECV OK = %sn", buff); /*---------------------------------------------------------* /* SEND : send the message to the client * /*---------------------------------------------------------* memset(buff, 0, sizeof(buff)); strcpy(buff, "* SOCK SERVER *"); rc = send(newfd, buff, len, 0); if(rc < 0){ perror("SEND"); getchar(); exit(0); } printf("* SEND OKn"); close(sockfd); close(newfd); printf("**************************************n"); printf("* SOCKSVR SUCCESSFULLY COMPLETE ! *n"); printf("**************************************n"); getchar(); return; } /****************/ void INZSR(void) /****************/ { errcode.BYTESPRO = errcode.BYTESAVL = 0; } /********************************************************/ void err_log(char* msg, int opt, char* err_at, int line) /********************************************************/ { fprintf(stderr, "%sn", msg); fprintf(stderr, " ->ERR AT = %s, LINE = %dn", err_at, line); if(opt == TRUE) fprintf(stderr, "%sn", strerror(errno)); }
【 解説 】
コンパイルは
CRTBNDC MYOBJLIB/SOCKSVR SRCFILE(MYSRCLIB/QCSRC) AUT(*ALL)
TCP/IPの通信ストリームは常に 1492 バイト以下である必要がある。
また TCP/IPの送受信は SNA のように確実なものではなく、指定したバイト数が
一回の送受信操作だけで完結するものではない。
特に受信は一回の受信だけですべてのバイト数を受信できない場合があるので不足分を
再読み込みする必要があることを忘れてはならない。
社内などでユーザー自身がプロトコルを作成する必要がある場合は、送信ストリームの先頭に
これから送信するストリームの長さを付加するようにしておいて受信側では、それによって全体の
ストリーム長を把握してから受信において不足分があれば、再読み込みするように努めなければならない。
TCP/IPは非常に単純な構造であって SNA のように OS400 がかなりの部分を面倒を見てくれるような
高度な機能はないのである。
1492 バイトを超えるようなストリームの送受信もユーザーの手で分割や再組み立てを行う必要がある。