ネットワーク・プログラミング TCPサーバ
全体の位置づけ オペレーションシステム(Linux) ソケットプログラミング プロセス カーネルとシステムコール プロセス間通信 スレッド クライアントーサーバ 非同期I/Oとマルチタスク
プロセスは立上って、待っていなければいけない。 1.1 TCPエコーサーバ プロセスは立上って、待っていなければいけない。 1. socket()を実行 (ソケット作成) ↓ 2. bind()を実行 (ソケットにサーバのポート番号を割当る) 3. listen()を実行 (接続要求をキューに入れる) 4. accept()を実行 (接続要求毎に新しいソケットを作成) 5. send()とrecv()を実行 (通信を行う) 6. close()を実行 (ソケット削除) TCPEchoClient TCPEchoServer socket() bind() listen() accept() サービス受付開始 socket() コネクション設定 connect() サービス開始 Echo this send() recv() recv() Echo this send() コネクション開放 close() close()
bind()によって、指定したポート番号に割当られる 1.2 TCPエコーサーバ システムコール1 サーバのアドレス ソケットにポート番号を割当る int bind(int socket, struct sockaddr *localAddress, unsigned int addressLength) 戻値:成功: 0、失敗: -1 AP AP ソケット ソケット ポート番号は適当に割当られる bind()によって、指定したポート番号に割当られる 1 2 65535 1 2 7 65535 TCP TCP IP IP クライアント サーバ
1.3 TCPエコーサーバ システムコール2 接続要求をキューに入る 接続要求毎に新しいソケットを作成 接続要求を同時に受入れる最大値 接続要求をキューに入る int listen(int socket, int queueLimit) 戻値:成功: 0、失敗: -1 接続要求毎に新しいソケットを作成 int accept(int socket, struct sockaddr *clientAddress, unsigned int *addressLength) 戻値:成功: ソケット識別子、失敗: -1 クライアントのアドレス クライアント1用ソケット 接続要求に対するソケット クライアント2用ソケット ユーザプロセス listen() accept() accept() queueLimit番目 カーネル TCP accept()待ちキュー クライアント1 クライアント2
1.4 TCPエコーサーバ プログラム その1 TCPEchoServer.c その1 TCPEchoServer #define MAXPENDING 5 /* 最大キュー長 */ void DieWithError(char *errorMessage); /* エラー処理 */ void HandleTCPClient(int clntSocket); /* TCPクライアント処理 */ int main(int argc, char *argv[]) { int servSock; /* サーバのソケット識別子 */ int clntSock; /* クライアントのソケット識別子 */ struct sockaddr_in echoServAddr; /* ローカルアドレス */ struct sockaddr_in echoClntAddr; /* クライアントアドレス */ unsigned short echoServPort; /* サーバポート */ unsigned int clntLen; /* クライアントアドレス構造体長 */ echoServPort = atoi(argv[1]); /* 引数1はローカルポート番号 */ /* 接続要求に対するソケットを作成 */ if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithError("socket() failed"); /* ローカルアドレス構造体を作る */ memset(&echoServAddr, 0, sizeof(echoServAddr)); /*構造体を初期化*/ echoServAddr.sin_family = AF_INET; /* インタネットアドレス族 */ echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /*ワイルドカード*/ echoServAddr.sin_port = htons(echoServPort); /* ローカルポート */ プロセスは待機中 TCPEchoClient TCPEchoServer Yahoo Yahoo 受信インタフェースを指定しない [oida@rpc261 socket]$ gcc -o TCPEchoServer TCPEchoServer.c HandleTCPClient.c DieWithError.c [oida@rpc261 socket]$ ./TCPEchoServer 5000 Handling client 150.43.220.22 サーバのポート番号
1.5 TCPエコーサーバ プログラム その2 TCPEchoServer.c その2 /* ローカルアドレスにバインドする */ if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("bind() failed"); /* クライアントからの接続要求を待つ */ if (listen(servSock, MAXPENDING) < 0) DieWithError("listen() failed"); for (;;) /* 処理を繰返す */ { /* 入出力パラメータのサイズをセットする */ clntLen = sizeof(echoClntAddr); /* 接続要求に対して新しいソケットを作成 */ if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr)); /* TCPクライアント処理関数を呼ぶ */ HandleTCPClient(clntSock); } サーバのアドレス 接続要求を同時に受入れる最大値 クライアントのアドレス 接続要求に対するソケット
からのメッセージ或いは、closeを待つ 1.6 TCPエコーサーバ プログラム その3 HandleTCPClient() #define RCVBUFSIZE 32 /* Size of receive buffer */ void DieWithError(char *errorMessage); /* エラー処理 */ void HandleTCPClient(int clntSocket) { char echoBuffer[RCVBUFSIZE]; /* エコーバッファ */ int recvMsgSize; /* 受信メッセージ長 */ if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) /* クライアントからメッセージ受信 */ DieWithError("recv() failed"); while (recvMsgSize > 0) /* 0は転送終了を意味する */ if (send(clntSocket, echoBuffer, recvMsgSize, 0) != recvMsgSize) /* メッセージをクライアントに返す */ DieWithError("send() failed"); if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) /* 受信データがあるか確認する */ } close(clntSocket); /* クライアントソケットを終了する */ クライアント用ソケット識別子 一度に受信可能なバイト数(32) 受信状態で、クライアント からのメッセージ或いは、closeを待つ
宿題9 TCPEchoClient.c, TCPEchoServer.c, HandleTCPClient.c, DieWithError.cを使って、TCPEchoClientとTCPEchoServerを作成し、プログラムの実行結果を報告せよ(可能であれば、クライアントとサーバは異なるコンピュータ上で実行せよ)。 表紙に氏名と学籍番号を書く プログラムの実行結果を書く 実施した内容を説明する文章を書く レポートの締切は次の週の水曜日18:00 TCPEchoClient TCPEchoServer Yahoo Yahoo Jazz Jazz サーバ用とクライアント用にTeraTermを二つ立ち上げる [oida@rpc261 socket]$ gcc -o TCPEchoServer TCPEchoServer.c HandleTCPClient.c DieWithError.c [oida@rpc261 socket]$ ./TCPEchoServer 5000 Handling client 150.43.220.22 同じコンピュータ上で同じポート番号は使えない [oida@rpc261 socket]$ gcc -o TCPEchoClient TCPEchoClient.c DieWithError.c [oida@rpc261 socket]$ ./TCPEchoClient 150.43.220.22 "Yahoo" 5000 Received: Yahoo [oida@rpc261 socket]$ ./TCPEchoClient 150.43.220. 22 "Jazz" 5000 Received: Jazz [oida@rpc261 socket]$ サーバのIPアドレス /sbin/ifconfig サーバのポート番号