服务器重复发送SYN ACK 和 TCP_DEFER_ACCEPT设置
现象:
以下为其他网站提供,和我遇到的情况一样。
就是服务器老是重复发送 SYN, ACK。
4414.229553 client -> server TCP 62464 > http [SYN] Seq=0 Win=65535 Len=0 MSS=1452 WS=3 TSV=116730231 TSER=0
4414.229633 server -> client TCP http > 62464 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=2406364374 TSER=116730231 WS=6
4414.263330 client -> server TCP 62464 > http [ACK] Seq=1 Ack=1 Win=524280 Len=0 TSV=116730231 TSER=2406364374
4418.812859 server -> client TCP http > 62464 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=2406365520 TSER=116730231 WS=6
4418.892176 client -> server TCP [TCP Dup ACK 778#1] 62464 > http [ACK] Seq=1 Ack=1 Win=524280 Len=0 TSV=116730278 TSER=2406365520
4424.812864 server -> client TCP http > 62464 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=2406367020 TSER=116730278 WS=6
4424.891240 client -> server TCP [TCP Dup ACK 778#2] 62464 > http [ACK] Seq=1 Ack=1 Win=524280 Len=0 TSV=116730337 TSER=2406367020
可能的问题:供参考
OK, I don't have a full answer, but I've learned a lot since this issue first came up. I'll share my insights here.
- Firstly, the problem was triggered by Google Chrome opening a number of sockets (6 in my tests) to any website you open with it. It does this to kickstart downloading the various elements of a website in parallel. For simple websites that don't have many items, like mine, some of these pre-opened sockets will sit idle. I've read that other modern browsers do this as well.
- 首先,该问题是在使用Chrome 浏览器通过建立多个socket 连接(我的测试是6个socket连接)打开网页时出现的,它启动并行下载各种网页内容,而简单的网页站点没有很多内容,就行我的站点,其他的预打开的socket 就被闲置。我看了其他的浏览器也都是同样的。
- The client triggering the connection-tracking problem probably has a broken home-router or something, because the idle sockets tend to disappear without any FIN or RST packets being sent to tear them down.
- 客户端触发这个连接问题,可能是有一个有问题的家庭路由器或者其他问题,因为这些闲置的socket 会莫名的消失而不发任何的fin 或者RST 包。
- The repeating of the SYN+ACK packets seems to be normal TCP behaviour, I've been able to witness it on various third-party webservers by running a packet dump and then doing something like
telnet www.website.com 80
and not sending any data. It could still be a Linux peculiarity though, as the servers I tested may well all run Linux. This behaviour also happened without any iptables-rules loaded, so it's really kernel-related. - 这重复的SYN+ACK 报可能是个正常的行为,我已经通过telnet 其他第三方网站而不发数据(telnet www.website.com 80 ),并抓包验证了它。它可能是linux 系统特有的,我测试的都是在linux 系统上,并且没有任何的防火墙规则设置,因此可能和内核相关。
- After sending a few SYN+ACK packets without response, the Linux kernel will tear down its side of the connection.
iptables
connection-tracking doesn't seem to share this logic, so there the connection will remain in theESTABLISHED
state until it times out. The default timeout of this is 5 days (!). I'm planning to reduce this to something more sensible such as a few hours. - 发送一些SYN+ACK 包之后,没有回应。这linux 内核将断开它这边的tcp 连接。防火墙连接没有采用这个处理方式,因此它的tcp 连接仍然保持ESTABLISHED 状态一直到超时,默认超时是5 天,我计划减小这个值到一个比较合理的值几个小时。
- Repeating the SYN+ACKs after receiving ACK is not completely default behaviour, since I didn't see it when I opened a listening port with
nc -l ###
and connected to that. So it might be a certain TCP option Apache sets on its listening socket. Or it might be something entirely unique to Apache. Most other daemons announce themselves immediately after connection, so they are no valid test cases for this. The connection needs to do a 3-way handshake and then immediately fall idle. - 服务器收到ack 后重复发送SYN+ACKs 也不完全是个默认行为,我在用 nc -l 打开一个监听端口,并且连接它,没出现该情况。因此它可能是某个tcp 选项被apache 设置在监听socket。或者一些apache 独特的东西造成的。大多数其他守护程序会立即变成connection 状态。因此这没有有效的测试案例:这连接需要在做一个tcp 三次握手,并且立即空闲。
以上为网上的一些分析,我结合自己的情况。基本可以肯定我的路由器有问题。因为我在配置好端口映射之后,外网可以访问路由器的远程web 管理页面,而空闲一段时间或过一晚上,第二天发现外网的远程web 管理页面无法连接,其他的映射端口也无法连接。内网抓包发现和以上描述的现象。
===============================================================================
咳,刚写完就找到最终答案了:
监听socket 使用了TCP_DEFER_ACCEPT选项。
————————————————————————————————————————————————————————————————
1. 设置TCP_DEFER_ACCEPT
int val = 10; // time_out
if (setsockopt(sock_descriptor, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val))== -1)
{
perror("setsockopt");
exit(1);
}
2. TCP_DEFER_ACCEPT的效果
正常的tcp三次握手过程:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
设置TCP_DEFER_ACCEPT后
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,连接并不进入ESTABLISHED状态,而是在第一个真正有数据的包到达后才进入ESTABLISHED,完成连接的建立。
TCP_DEFER_ACCEPT的超时
在第三次握手时候,如果客户段迟迟不发送数据,服务器 连接将一直处于syn_recv状态。此时内核会重传 syn_ack ,重传的次数可以通过 sysctl -w net.ipv4.tcp_synack_retries=3来设置,如果3次重传后,客户端依然没有数据,在等待 设置TCP_DEFER_ACCEPT时候指定的超时时间后(这个时间单位为s,可是测试看来并不精准的执行),系统将回收连接,并不对客户端发出rst或者fin包。
posted on 2015-09-18 23:20 littleKing163 阅读(1287) 评论(0) 编辑 收藏 举报