UNP第五章描述了在客户端和服务器连接建立之后会出现的几种情况,并给出了解决方案,做一个简单的总结。
先给出这个简单的回射C/S程序的模型如下图。
1、客户端和服务器正常终止连接
客户端从标准输入键入EOF便可以正常终止客户端程序,此间发生了几件事。
- 客户TCP向服务器TCP发送FIN,服务器TCP向客户TCP回送ACK。
- 客户程序调用exit终止,内核关闭客户程序打开的套接口。
- 服务器TCP收到FIN后,子程序调用exit终止,内核关闭子程序打开的描述字。
- 子进程终止时向父进程发送SIGCHLD信号。
- 服务器TCP向客户TCP发送FIN,客户TCP回送ACK。此时连接完全终止。
如果子进程发送的SIGCHLD信号被父进程忽略(即无信号处理函数),那么子进程将进入僵死状态,即僵尸进程(僵尸进程存在的目的是维护子进程的信息,以便父进程在以后某个时候获取)。为了解决这个问题,必须捕捉这个信号并将进程wait或waitpid掉(关于信号处理将在另一篇文章中介绍)。
2、服务器进程终止
如果服务器的进程被杀死,此时会出现这么几件事:
- 服务器TCP发送FIN给客户TCP,客户TCP回送ACK。
- 如果此时客户端再给服务器发送数据(服务器TCP发送FIN来向客户TCP表明关闭连接,而客户TCP并不知道此时的服务器进程已经终止了),会受到服务器TCP发送的RST(因此此时套接口进程已经终止了)。
- 对于服务器TCP发送来的RST,客户不会看到(原因是,客户程序在发送数据之后调用readline会马上返回,继而阻塞在fgets上(标准输入),那么套接口的信息便被忽略了)。
问题所在:程序只在用户输入时候阻塞,而实际上套接口也需要阻塞,否则必然会有信息被忽略。这个问题将调用select和poll函数来解决。
3、服务器主机崩溃
当服务器意外崩溃时(相当于被人拔了网线),客户端不会得到任何的通知,于是客户端TCP会不断地重传数据,直到超时。如果想在客户不主动发送数据的情况下,也不需要等待超时时间而知道服务器崩溃的消息,那么需要设置SO_KEEPALIVE套接口选项。
4、服务器主机崩溃后重启
当前种情况下服务器重启,重启之后的服务器已经丢失了之前的所有连接,因此对客户端发送来的数据只会回送RST,这将导致客户端返回。