在前面TCP套接字通信中有一个关于服务端关闭但是客户端没有断开的问题, 虽然这个问题已经得到解决了, 而且也分析过了, 但是本节还是想继续以问题为例来分析异常.
问题重现
这个问题的重现可以使用TCP套接字之通信中的代码, 也可以用TCP套接字之SIGCHLD中的代码. 这里我就使用后者.
服务端完整代码 : service.c
服务端运行 :
./a.out 1 8080 192.168.1.16
客服端完整代码 : client.c
客户端运行 :
./a.out 2 8080 192.168.1.16
现将服务端ctrl+c中断, 然后客户端接收到了FIN
并且立马回了ACK确认报文, 但是客户端还处于阻塞并且发送数据时, 收到的确实对端的RST报文. 但是客户端收到RST后还是继续阻塞.
问题分析
正常的挥手过程中, 主动关闭方在收到ACK确认报文后进入FIN-WAIT2
, 这个阶段没有收到对端的FIN
理论上还可以接收数据只是不能发送了. 上面问题就在这里.
- 因为服务端立马就被杀死了, 原本应该等到FIN后才关闭, 但是对端还要等待终端输入数据才能送过去; 终端等待时间太长, 服务端等不了就直接被迫关闭进入
TIME_WAIT
阶段. 所以客户端很久在发送数据过来, 而对端已经关闭, 所以根本不认识这个连接直接回了RST. - 对端第一次收到了RST之后还是没有退出, 因为此时循环又回到
write
函数, 被阻塞. 内核向进程发送了SIGPIPE信号, 希望终止客服端但是客户端默认忽略了该信号继续阻塞直到不阻塞后退出, 这就是还要输入第二次的原因.
解决问题
问题就出现在write函数阻塞, 进程忽略SIGPIPE信号. 方法有多种
- 捕捉SIGPIPE信号, 但是还是会被阻塞, 现象和之前一样, 但是我们至少捕捉到了.
- 采用
select
,poll
,epoll
等I/O复用函数 - 采用异步I/O
总结
虽然本节与前面的部分地方是重复的, 但是本节是从异常的方面分析该问题的, 侧重点不太一样.