红联Linux门户
Linux帮助

由于TCP强制复位 客户端无法收到应答

发布时间:2008-08-27 20:44:19来源:红联作者:Limits
在我的开发中有如下情形[1],在TCP连接中,客户端发送一个请求给服务器,服务器进行必要的处理工作,然后将应答结果发送给客户端。

引用:
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。
文章评论

共有 1 条评论

  1. garybo 于 2008-09-25 17:08:41发表:

    可以尝试一下在close socket前调用 shutdown(fd, SHUT_RDWR)