TCP/IPソケット通信では、ソケットを作成しなければ何も始まりません。
その後、用途に応じて作成したソケットへオプションを与えるのですが、
今回は、オプションl_onoff = 1、SO_LINGER = 0をソケットに与えたときの動作を調査してみます。
調査環境
サーバ:Ubuntu 12.04LTS(192.168.11.6)
クライアント:Mac OS X Lion(192.168.11.11)
動作概要
サーバ:
クライアントからの接続を受付け、子プロセスを生成する。
子プロセスで、クライアントより受信した文字列を、クライアント側にエコーバックする。
クライアント:
サーバとのコネクション確立後、ユーザの入力を待ち合わせる。
入力された文字列を、サーバへ送信、その後サーバからエコーバックされた文字列を
標準出力へ出力。
今回 EOF文字(Ctrl+D)が端末より入力され、ユーザ入力処理が完了したとき、
ソケットオプション l_onoff(=1)、SO_LINGER(=0)をソケットに設定。
ソケットオプション l_onoff、SO_LINGERの解説
l_onoffに1を設定した場合、ソケットオプションにSO_LINGER(time(sec) >= 0)を設定することが可能になります。
SO_LINGER(=0)を設定した場合、クライアント側でコネクション解放時、TCPは通常のコネクション解放ではなく、
RSTパケットをサーバへ送りつけてコネクションの解放を行います。
ソケットオプションの解説に関しては、
Man page of SOCKETを参照しました。
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/socket.7.html
結果
No.6〜8は、コネクション確立時の3Wayハンドシェーク
No.9〜12は、サーバ、クライアント間のデータのやり取り([PSH,ACK], [ACK})
No.13は、クライアントユーザ端末でEOFを受信後、ソケットオプションl_onoff、SO_LINGER設定後、コネクションクローズ。
その後サーバへいきなり、RSTパケットを送りつけています。
通常の終了シーケンスの場合、
No.23〜No.25で終了シーケンスが走っています。
エコークライアント
#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
struct linger ling;
// 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);
servaddr.sin_port = htons(9877);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
str_cli(stdin, sockfd); /* do it all */
ling.l_onoff = 1;
ling.l_linger = 0;
Setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
exit(0);
}
エコーサーバ
#include "unp.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
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);
for ( ; ; ) {
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
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"
void
str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again:
while ( (n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}