TCP 数据传输过程图
参考:http://blog.youkuaiyun.com/whuslei/article/details/6667471/
运行服务端(为每个客户端fork子进程服务) 和 一个客户端,可以看到 服务端的父进程和子进程
TCP服务器进程突然终止,服务端关闭连接
如下图,找到服务端子进程的pid, 直接kill掉
kill 子进程后,子进程相关的打开描述符都会被关闭,这就导致向客户端发送一个FIN, 而客户TCP则响应以一个ACK, 这是TCP连接终止(四次握手)的前半部分。
在服务端,设置了进程kill掉的信号,当$kill后,会立即看到
signal发送给服务端父进程,得到正确处理(可以看到child terminated 被打印出来)
服务端主动关闭连接,会向客户端发送一个FIN, 客户端接收来自服务端的FIN并响应一个ACK, 但自己是阻塞在fgets调用上,此时客户端还没有发送FIN。
john@ubuntu:~$ netstat -a | grep 9877
tcp 0 0 *:9877 *:* LISTEN
tcp 0 0 localhost:9877 localhost:59644 FIN_WAIT2
tcp 1 0 localhost:59644 localhost:9877 CLOSE_WAIT
服务端子进程 FIN_WAIT2(主动关闭的一方收到对方的ACK后,等待对方发送FIN)
客户端 CLOSE_WAIT (被动关闭的一方,发送了FIN后处于 CLOSE_WAIT 状态,等待关闭连接)
客户端仍在运行阻塞在fgets上,我们接着输入一段文本,给服务端,发现接收不到任何结果了
sudo tcpdump -i lo # tcpdump查看数据包
在完成tcp结束连接(四次握手)的前半部分后,客户端可以继续发送数据(而此时服务端已经结束)
因为客户端收到服务端发送过来的FIN后,服务端并没有告诉客户端自己已经终止了, 所以客户端可以发送数据,只是得不到响应而已。
所以我们想要的是:一旦服务器进程死掉了,客户端应该立刻收到FIN,并且自己不再发送数据
服务器主机崩溃
服务端和客户端成功连接,能相互发送数据,此时若服务器突然不可达(如网络中断了)。这时客户端发送数据,将阻塞在read调用(一直等待服务端应答。。。)上。
客户TCP持续重传数据分节,源自Berkeley的实现重传该数据分节12次,共等待约9分钟后,才放弃重传。
客户端最终知道 服务器不可达,显然9分钟比较长,我们可以在read调用设置一个超时。
当然如果客户端没有给服务器发送数据,怎么知道服务器已经不可达了呢? SO_KEEPALIVE套接字?
服务器关机
unix系统关机 会产生信号,服务器主机会关闭其所有打开描述符,其所有子进程也将关闭。这样之后的情况将如同TCP服务器进程突然终止一样,我们仍然需要使得服务器进程的终止一旦发生,客户端能够立刻检测到