TCP/IPでソケット通信をする際に、クライアントとサーバ間でコネクションを張ります。
いわゆる3WAYハンドシェークがコネクション確立手順です。
クライアント側からSYNパケットを送信し、サーバはSYN,ACKを送信、
再度クライアントからACKを送信するのが、3WAYハンドシェークです。
ここで、サーバ側が何らかの理由で、ダウン、もしくはネットワークから切り離されて
いる場合、パケットの送信はどんな様子になるのでしょうか?
エコーサーバIPアドレス&ポート:192.168.11.6:9877
エコークライアントIPアドレス:192.168.11.11
エコーサーバ、クライアントを立ち上げます。
ここで、エコーサーバが稼働しているホストのLANケーブルを引き抜いておきます。
エコークライアントを起動し(192.168.11.6でサーバを指定)、wiresharkでパケットをダンプしてみると、
当然、サーバからのSYN,ACKは見当たらず、その代わり、クライアントから
SYNパケットが再送信されている様子が見えます⬇。
Man page of TCPに
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/tcp.7.html
SYNパケット再送回数の設定項目がありました。
アクティブな TCP 接続に初期 SYN の再送を試みる最大回数。
/proc/sys/net/ipv4/tcp_syn_retries
実験の環境では、5と設定されているにも関わらず(つまり5回再送)、
再送パケットとして検出しているのは、4件。
キャプチャ漏れか...?
キャプチャ漏れか?はともかく(いいのかよ?)、再送間隔が徐々に長くなっている様子が見えます。
No3が送信されてから、約1秒後にNo4が再送され、その後、2秒後にNo5が再送、
ついで、4秒後にNo9が再送信されています。
間隔が随時伸びるのはどういう意図でかは、未調査です。。。いずれまた。
echoサーバ
#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);
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
echoクライアント
#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(9877);
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"
void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE];
while (Fgets(sendline, MAXLINE, fp) != NULL) {
Writen(sockfd, sendline, strlen(sendline));
if (Readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
Fputs(recvline, stdout);
}
}