近期开始认真阅读Richard Stevens 的经典著作《Unix Networking Programming :volume 1》3rd。大牛的作品没说的,无论是技术还是文字都是精炼准确的典范。
书中的某些论述和自己平时的认识有较大出入,所以还是以书中提供的代码为基础,作了些简单的实验来印证书中的观点吧。
“5.12——服务器进程终止"
第5章的内容是给予一个简单的并发式C/S模型:client调用connect成功连接服务器后,server进程执行fork,创建子进程为该client提供服务。
本小节所讨论的问题是:在client成功connnect后,若kill掉server端对应的子进程,将会发生什么。
按照书中描述,并经试验抓包证明,步骤如下:
1.server端子进程终止,子进程所打开的socket要被关闭,这导致其向client发送FIN。
2.SIGCHLD信号被发送至server父进程,由其正确处理。
3.client端收到FIN后,响应一个ACK,这样就完成了TCP连接终止的前一半工作。
此时运行netstat -an命令,可以观察到client端socket状态为CLOSE_WAIT,server子进程的socket状态为FIN_WAIT2。将观察结果 与书中图2-5相对照,可看出这是与图2.5相一致的。(注意在图2-5中主动断开的是client,而实验中主动断开的是server).
4.如果现在client端向socket写数据,会有什么结果?这一操作是允许的,抓包证明,所写数据依然通过这个状态为CLOES_WAIT的socket发送出来。
5.服务器接收到客户的数据后,由于先前打开socket的进程已终止,所以向客户响应一个RST。
6.客户进程接收到RST后,连接彻底关闭。
7.若此时client对socket进行读操作,都会发生错误,并将错误码(ECONNRESET)写入全局变量errno;
8.若此时client对socket进行写操作,内核向该进程发送SIGPIPE信号,该信号的默认处理是终止进程。
实验的基础是书中提供的源代码 tcpserv04.c 和tcpcli01.c,并对tcpcli01.c作了如下改动(红色部分为添加的代码,"//"的是被删除的代码)
书中的某些论述和自己平时的认识有较大出入,所以还是以书中提供的代码为基础,作了些简单的实验来印证书中的观点吧。
“5.12——服务器进程终止"
第5章的内容是给予一个简单的并发式C/S模型:client调用connect成功连接服务器后,server进程执行fork,创建子进程为该client提供服务。
本小节所讨论的问题是:在client成功connnect后,若kill掉server端对应的子进程,将会发生什么。
按照书中描述,并经试验抓包证明,步骤如下:
1.server端子进程终止,子进程所打开的socket要被关闭,这导致其向client发送FIN。
2.SIGCHLD信号被发送至server父进程,由其正确处理。
3.client端收到FIN后,响应一个ACK,这样就完成了TCP连接终止的前一半工作。
此时运行netstat -an命令,可以观察到client端socket状态为CLOSE_WAIT,server子进程的socket状态为FIN_WAIT2。将观察结果 与书中图2-5相对照,可看出这是与图2.5相一致的。(注意在图2-5中主动断开的是client,而实验中主动断开的是server).
4.如果现在client端向socket写数据,会有什么结果?这一操作是允许的,抓包证明,所写数据依然通过这个状态为CLOES_WAIT的socket发送出来。
5.服务器接收到客户的数据后,由于先前打开socket的进程已终止,所以向客户响应一个RST。
6.客户进程接收到RST后,连接彻底关闭。
7.若此时client对socket进行读操作,都会发生错误,并将错误码(ECONNRESET)写入全局变量errno;
8.若此时client对socket进行写操作,内核向该进程发送SIGPIPE信号,该信号的默认处理是终止进程。
实验的基础是书中提供的源代码 tcpserv04.c 和tcpcli01.c,并对tcpcli01.c作了如下改动(红色部分为添加的代码,"//"的是被删除的代码)
void stc_cli(FILE *fp ,int sockfd)
{
.............................
while ( Fgets (sendline , MAXLINE , fp) != NULL )
{
Writen(sockfd , sendline , strlen (sendline));
sleep(1 ); //延时,以确保收到RST
if (Readline(sockfd , recvline , MAXLINE) == 0 )
{
// err_quit("str_cli: server terminated prematurely");
Readline(sockfd , recvline , MAXLINE);
if(errno=ECONNRESET)
printf("read error:connect reset by peer " );
Writen(sockfd , sendline , strlen (sendline));
// return ;
}
Fputs (recvline , stdout);
}
}
void sig_pipe (int signo)
{
printf("you have written to a socket who has received RST.Forbidden !/n");
exit(1);
}
int main(int argc, char **argv)
{
.......................
Signal(SIGPIPE,sig_pipe);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
str_cli(stdin, sockfd); /* do it all */
exit(0);
}
{
.............................
while ( Fgets (sendline , MAXLINE , fp) != NULL )
{
Writen(sockfd , sendline , strlen (sendline));
sleep(1 ); //延时,以确保收到RST
if (Readline(sockfd , recvline , MAXLINE) == 0 )
{
// err_quit("str_cli: server terminated prematurely");
Readline(sockfd , recvline , MAXLINE);
if(errno=ECONNRESET)
printf("read error:connect reset by peer " );
Writen(sockfd , sendline , strlen (sendline));
// return ;
}
Fputs (recvline , stdout);
}
}
void sig_pipe (int signo)
{
printf("you have written to a socket who has received RST.Forbidden !/n");
exit(1);
}
int main(int argc, char **argv)
{
.......................
Signal(SIGPIPE,sig_pipe);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
str_cli(stdin, sockfd); /* do it all */
exit(0);
}