正常情况是啥?一方发送FIN,另一方相应,四次挥手,断开连接。但是就像生活一样,服务器也总有意外,以下是《Unix网络编程》中提到的服务器的四种常见的意外情况。客户端在服务器端出现意外情况下是什么状况,又该该如何处理呢?
首先给出整个网络的基本流程图。客户端从标准输入读入数据,发往服务器端,服务器接收到数据在发往客户端,正常情况下客户端将数据写标准输出,如此循环。其实就是个简单的echo服务器模型,《Unix网络编程》整本书都以此模型作为基础,下面将讨论服务器出错情况下,客户端的情况。
1. 服务器端进程终止:比如kill掉某一个服务端子进程(处理和该客户端连接的子进程,此时父进程和客户端程序之间已经没有了联系)
服务器端子进程崩溃,会发送FIN到客户端,客户端相应ACK到服务器端,服务器端进入FIN_WAIT2状态,客户端进入CLOSE_WAIT状态,也就是四次挥手释放连接的前一部分完成。等待着客户端发送FIN,也就是调用close函数完成第二部分的连接释放。客户端此时恰恰阻塞在fget调用上,我们再在客户端终端输入一段文字,write发往客户端之后进入read阻塞状态,此时会收到服务端的FIN,read返回值为0(表示EOF),此时会打印出“服务器过早终止”,然后客户端程序退出。另一方面,write到服务端的数据因为没有对应的服务端子进程,因此会返回客户端RST,只是RST在客户端已经无法接收到而已———客户端程序已经关闭。
这个例子有个问题,当FIN到达套接字时,程序正阻塞于fget系统调用,因此未能及时感知服务端子进程发送的终止信号。这也是后面引入IO复用的目的之一:一旦服务端子进程被杀死,客户端能立刻感知到。
2. SIGPIPE信号
如果客户不理会read函数读写出现的错误,反而写入更多的数据呢?比如收到RST信号时候继续写入数据,此时内核会向进程发出EPIPE的错误。
3. 服务端主机崩溃:可以关机来模拟
在这种情况下,服务端并不向客户端发出任何东西,客户端并不知道服务端主机崩溃。
此时我们在客户端发送一行文字到服务端,然后在read调用,打开tcpdump观察,会发现文字进入TCP的重传阶段,时间长达9分钟(来自:Unix网络编程),最后终于放弃,最后发送给客户端一个“网络/主机不可达的错误”。
4. 主机崩溃后重启:重启主机来模拟
客户端同样不知道服务端的主机崩溃并模拟的情况,此时发送一行文字到服务端,服务端由于已经丢失了之前的描述符,因此会回应RST,客户端此时正阻塞在read调用上,因此会返回ECONNRST错误。
5. 服务器主机关机,比如ubuntu的shutdown命令,情况同1
6. 拔掉网线的情况,情况同3.
7. 拔掉网线再插上。分两种情况:
1)如果在客户端发送数据到服务器的过程中网线一直都是断开没有插上,则会向客户端返回网络不可达,情况同6.
2)网线重新插上,此时会正常通信.
客户端如何尽快感知服务端