正常终止
- 信号处理
- 回收子进程
- 处理被中断的慢系统调用
accept返回前终止
在完成三次握手和accept返回之间(这是一个很小的间隔),客户端重发了RST报文,导致accept失败。这个时候的connect还是可以复用的,所以只要重新连接就行了。
通过套接字选项可以设置RST报文。
此时Posix下errno值会被设置成ECONNABORTED。
因为这个错误是非致命的,所以可以重新启动accept。
只要处理这个错误,并再循环执行accept就可以了。
服务器进程终止
进程终止与主机崩溃不同。
服务进程终止,进程会发送FIN报文,使得TCP处于半关闭状态(服务器到客户端的连接关闭,客户端到服务端的通信还可以继续)。
如果我们的客户端程序没有处理收到的FIN报,而是继续执行客户端任务,比如说继续发送数据和接受数据,就会出现问题:
- 如果继续写这个套接字,就会收到服务端的RST报文,这个报文会对下一次写造成影响
- 因为收到的是RST报文,而不是目的文本,所以read函数会返回错误,这可能导致客户端进程关闭
这样的解决方法可以是使用IO多路复用,比如poll或epoll,来同时监听套接字和read,当套接字发生FIN的时候,去做相应的操作,而不是write之后直接去read。
SIGPIPE信号:
当进程收到一个RST报文的时候,会发送一个SIGPIPE信号,无论对这个信号是忽略,默认,还是捕捉,往这个套接字上的写操作都会触发EPIPE错误。
注意:这是再写,才会触发这个EPIPE错误,那么如果你想通过这个错误来进行相应的处理,你最少需要写两次,第一次触发SIGPIPE信号,第二次引发EPIPE错误。
如果是处理SIGPIPE信号,那么可以做一些我们想要的操作,但是我们不知道是哪个write引发的。
如果想知道是哪个write引发的,就只能去引发EPIPE错误,并处理这个错误。
服务器主机崩溃或客户发送的数据不可达
服务器主机崩溃,不发送任何东西。
如果是目的主机崩溃:这样客户端不知道对方是否挂掉了,就会一直重传报文,一直到内核规定的时间(很长,几分钟),并返回一个错误;ETIMEDOUT。
如果是中间路由崩溃:路由会发送一个ICMP报文,并在客户端引发EHOSTUNREACH 或 ENETUNREACH错误。
如果不想等待几分钟,我们可以手动设置read的超时时间。
或者主动获得服务器的崩溃信息:SO_KEEPALIVE套接字选项或心跳包。
服务器主机崩溃后重启
重启之后,服务器丢失所有TCP信息。当服务器再次收到客户端的报文的时候,就会回复RST报文,表示想重新建立连接。
如果客户端正阻塞读就会返回ECONNRESET错误。
服务器主机关机
服务器进程正在运行,但是主机关机。
init进程先对所有进程发送SIGTERM信号(各进程可以捕获此信号,并马上善后),然后再对所有进程发送SIGKILL信号(此信号不能捕获,杀死所有进程)。
就会跟第三种情况一样。