菜鸟笔记
提升您的技术认知

《unix网络编程》tcp服务器的几种常见状况分析-ag真人游戏

《unix网络编程》(10)wait/waitpid处理僵死进程(sigchld信号)该文章中介绍了客户正常终止时,由于父进程使用wait处理sigchld信号时,阻塞于accept,内核会使得accept返回一个eintr错误(被中断的系统调用)。

         另一种情形也会导致accept返回非致命的错误:三次握手完成后,客户tcp发送一个rst(复位);此时服务器端即将调用accept。

启动客户和服务器,客户端发送”test1“正常回显,然后杀死服务器子进程,模拟服务器进程崩溃。其过程如下:

(1)kill子进程,子进程所有描述符关闭,导致向客户发fin,客户响应一个ack。这是tcp连接终止的前部分。

(2)sigchld信号发到父进程,被正确处理。

(3)客户没其他问题,但是客户阻塞于fgets调用上,等待从终端输入。

(4)此时,在客户端键入”test2 after kill”:客户tcp把数据发送到服务器,tcp允许这样做,因为客户收到fin指标是服务器进程关闭了连接的服务器端,从而不再发数据(半连接)。fin的接收没有告知客户tcp服务器进程终止。服务器收到数据,由于之前的连接已经终止,所以响应一个rst

(5)然而客户看不到rst,因为它调用write后立即调用read,而read的是第2步中的服务器的fin,所以read立即返回0。客户没想到会收到eof,所以以出错信息退出(服务器过早终止)。

        该例子的问题在于:fin到达套接字,客户阻塞于fgets。客户实际上对应两个描述符——套接字和用户输入,它不能如我们编写的客户端那样阻塞于这两个中的一个,而应该阻塞于其中任何一个输入上。这正是select和poll的目的之一。

         如上边小节中,往一个已经接收fin的套接中写是允许的,接收到fin仅仅代表对方不再发送数据。但是在收到rst后继续写操作,内核会向进程发送sigpipe信号;其默认行为是终止进程。对于这个信号的处理我们通常忽略即可。无论进程捕获该信号并从其处理函数返回,还是忽略,写操作都会返回epipe错误。

必须在不同主机运行服务器和客户机才能模拟该情形。

启动服务器、客户机;从网络中断开服务器。此时过程:

(1)服务器崩溃后,不会发送任何东西。

(2)客户键入文本,由write写入内核,再由客户tcp发送。客户阻塞于read,等待回射。

(3)客户tcp持续重传数据试图收到服务器的ack。一般会等待数分钟才放弃重传。客户最终放弃(假设此段时间内服务器没恢复)。既然客户阻塞于read,所有返回错误etimeout。然而如果某个中间路由判定服务器不可达,会响应一个目的不可达icmp消息,那么返回的错误时ehostunreach或enetunreach。

         尽管最终客户最终知道服务器不可达或崩溃,但已经过了数分钟。所用方法是对read设置一个超时

        上述的情形要给服务器发数据才知道服务器崩溃。如果不主动发数据还想检测服务器主机崩溃,就要采用so_keepalive套接字选项

服务器崩溃后重启,其tcp丢失了崩溃前的所有连接信息,因此对所受到的来自客户的数据响应一个rst;客户收到rst时正阻塞于read,所以会返回econnreset错误。

客户要检测服务器崩溃与否,又不主动发数据,就需要采用so_keepalive套接字选项或某些客户/服务器心搏函数

unix系统关机,init进程向所有进程发送sigterm信号(可被捕获),等待5-20秒,然后给还在运行进程发sigkill信号(不能捕获)。

如果不捕获sigterm,那么服务器将由sigkill终止。当服务器子进程终止时,和上边“服务器进程终止”一节中一样,正如那节中所说,要在客户端使用select后poll使得服务器一经终止,客户就能检测到

网站地图