ソケット通信を終了するのに、close、shutdownいずれかのAPIをコールします。
この両者のAPIの違いは、別途記載するとして、今回はshutdownについて実験してみます。
このAPIへ与える引数は、次の二つ。
ソケットディスクリプタと、SHUT_RD/SHUT_RW/SHUT_RDWRのうち何れかの引数です。
ここでは、SHUT_RDをshutdownへ与えたときの動作を見てみます。
SHUT_RDは、コネクションの読み出し側をクローズする指定です。
パケットのダンプを行うので、クライアント側でwiresharkを利用します。
実験環境
クライアント Mac OS Lion(192.168.11.11)
サーバ Ubuntu 12.04LTS(192.168.11.6:9877)
クライアントプログラム動作(プログラム:Number Stream Client/str_cli)
サーバへアクセスした後、サーバから送信される数字(0〜9)を受信する。
100個の数字を受信したのち、shutdown(SHUT_RD)をコール。
その後、またサーバからのデータ受信を待ち受ける。
サーバプログラム動作(プログラム:Number Stream Server/str_echo)
クライアントからの接続を受け付けた後、子プロセスを生成。
連続的にランダムに0〜9までの数字を生成し、クライアントへ生成する。
クライアントからshutdown(SHUT_RD)を受信しても無視してクライアントへの
送信動作は継続する。
結果
パケットNo.10、11、12はコネクション確立時の3wayハンドシェーク。
No13〜No16は、サーバからクライアントへのデータ送信シーケンス(サーバ:PSH,ACK クライアント:ACK)。
No17は、shutdown(SHUT_RD)を発行したことで発生するパケット。サーバへ通知。
No18は、サーバからクライアントへのデータ送信(PSH,ACK)
No19は、クライアント側は既にコネクションの受信側を閉じているため、RSTで応答をしています。
Number Stream Client
#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);
}
str_cli
#include "unp.h"
#include "sum.h"
void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE];
struct args args;
struct result result;
int cnt = 0;
while (1){
if(cnt >= 100) break;
memset(&result, 0, sizeof(result));
if (Readn(sockfd, &result, sizeof(result)) == 0)
err_quit("str_cli: server terminated prematurely");
printf("%d\n", result.sum);
cnt++;
}
shutdown(sockfd, SHUT_RD);
while(1){
memset(&result, 0, sizeof(result));
if (Readn(sockfd, &result, sizeof(result)) == 0)
err_quit("str_cli: server terminated prematurely");
printf("%d\n", result.sum);
cnt++;
}
}
Number Stream Server
#include "unp.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(9877);
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 */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
str_echo
#include "unp.h"d
#include "sum.h"
#define NUM_INDEX (10)
void
str_echo(int sockfd)
{
ssize_t n;
struct args args;
struct result result;
int num;
for ( ; ; ) {
num = rand() % NUM_INDEX;
result.sum = num;
Writen(sockfd, &result, sizeof(result));
}
}