引用:client server
-------------RQ---------------->
<------------RS-----------------
[1]
如果一个RQ的处理,要求关闭SOCKET时,客户端无法收到服务器的应答。客户端只发现服务器关闭了TCP连接,具体的流程如下[2]
引用:client server
-------------RQ-12------------->
send Error Result
<----------RS-12--X------------(没有)
close SOCKET
[2]
通过抓包发现如下协议流程[3],服务器的应答没有发送给客户端,服务器使用了一个RST,强制关闭了一个SOCKET。
服务器在处理RQ13的时候处理出错,要返回出错信息,关闭TCP。
引用:client server
-------------RQ-12------------->
-------------RQ-13------------->
-------------RQ-14------------->
<------------RS-12-------------
-------------RQ-15------------->
-------------RQ-16------------->
<------------RST--------------(TCP RST)
[3]
不太明白服务器为什么会用RST强制去关闭TCP连接。写了个简单的测试程序,测试TCP的关闭,想模拟出这种情况。我的系统中使用的SOCKET是非阻塞方式工作,没有什么特殊的TCP选项,系统大都会使用FIN正常关闭TCP。在如下情况发现了用RST关闭SOCKET的情形:
引用:client server
<-----create tcp connection----->
-----------send data------------>
no recv
close SOCKET
<------------RST--------------(TCP RST)
感觉只要是SOCKET的接收缓冲区中有数据的话,此时关闭SOCKET时会使用RST结束TCP。下面是linux-2.6.25.10\net\ipv4\tcp.c的void tcp_close(struct sock *sk, long timeout)代码。
引用:* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
if (data_was_unread) {
NET_INC_STATS_USER(LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, GFP_KERNEL);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
sk->sk_prot->disconnect(sk, 0);
NET_INC_STATS_USER(LINUX_MIB_TCPABORTONDATA);
} else if (tcp_close_state(sk)) {
/* We FIN if the application ate all the data before
* zapping the connection.
为改进性能,客户端没有等到前一个RQ完成,收到RS后,再发送下一个RQ,造成了服务器缓冲区有内容,此时服务器关闭TCP发送了RST,由于TCP数据的延迟发送,发送缓冲区也被废弃掉了,所以客户端只见到了RST。
garybo 于 2008-09-25 17:08:41发表:
可以尝试一下在close socket前调用 shutdown(fd, SHUT_RDWR)