本試行のサーバはクライアントからリクエストを受け付けた場合、
forkし、子プロセスがクライアントからのサービス要求に応えます。
シンプルな平行サーバの挙動
テスト環境
Ubuntu 12.04 LTS
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
クライアントIPアドレス:192.168.11.11
サーバIPアドレス:192.168.11.6
サーバ側ファイヤウォールで、port 9877をオープンしておく
この平行サーバは、クライアントからの入力を、単純にエコーバック。
Forkにより、子プロセスを生成しListenソケットをクローズ、
クライアントからの入力をそのままクライアントへエコーバックする。
平行サーバ
#include "unp.h"
#include "unistd.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
Signal(SIGCHLD, sig_chld);
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
Close(connfd);
}
Close(connfd); /* parent closes connected socket */
}
}
サーバ側9877ポートへ向けて、ターミナルで入力した文字列を送信します。
テストクライアント
#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
str_cli(stdin, sockfd); /* do it all */
exit(0);
}
結果
パケットキャプチャソフトwiresharkでのキャプチャログ
淡色表示されているパケット N0.8、9は3wayハンドシェーク、No.39、40、41はコネクションクローズシーケンス
No.10〜No.24、クライアント、サーバ間のデータ通信
平行サーバの親プロセスが走行する前に子プロセスでコネクションをクローズした場合どうなるか?
サーバ親プロセスに制御が移る前に、子プロセスでListenソケット、 Connectionソケットをクローズした時、
クローズシーケンスが走るかを調査してみます。
検証のため、平行サーバの親プロセスで、コネクションクローズ前に待機時間を600秒設けます。
平行サーバ(改造)
#include "unp.h"
#include "unistd.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
Signal(SIGCHLD, sig_chld);
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
Close(connfd);
}
sleep(600);
Close(connfd); /* parent closes connected socket */
}
}
結果
パケットキャプチャソフトwiresharkでのキャプチャログ
淡色表示されているパケット N0.15、16は3wayハンドシェーク、No.40はクライアントからのクローズパケット[FIN,ACK]。
サーバからは、[FIN,ACK]は送信されていない。
親プロセスで、ソケットファイルディスクリプタを保持しているため、
子プロセスでソケットをクローズしても、クローズシーケンス[FIN,ACK]がサーバから送信されていません。