Open WALL-E opened 7 years ago
客户端处于ESTABLISHED状态时,貌似会忽略服务端重传的SYN-ACK
重新阅读,新的体会,可能是当时没有想到,或是被遗忘了。
当ESTABLISHED队列达到上限的时候,服务端会启动定时器重新发送SYN-ACK。 当SYN-RECV队列达到上限的时候,服务器直接丢弃了客户端发送的SYN包。
下面这篇文章是从内核源码的角度来解释类似的问题 http://blog.csdn.net/bytxl/article/details/46408129
零. 背景知识
如果读者对以下知识点有基本的了解,读起来会很轻松,就像是在做大学物理实验。
一. 系统调用listen
大多数同学第一次接触到backlog参数,是从listen函数开始的。 下面是关于listen函数的man手册摘要
这段文档中有两个地方需要思考:
the queue of pending connections
可以有两种解释 a. 已经完成三次握手的连接(ESTABLISHED) b. 已经完成三次握手的连接(ESTABLISHED) + 未完成三次握手的连接(SYN-RECEIVED) 这两种解释可能都是对的,因为不同的操作系统有不同的实现。 对现代Linux来说,这里的队列特指已经完成三次握手的连接当队列已满的时候,直接返回reset是否合适(ECONNREFUSED) 这样处理有个坏处,就是客户端无法区别是未处理队列满了,还是访问了系统未监听的端口, 所以还是直接忽略这个SYN比较好。 本文档聚焦在第一个问题。 二. 内核参数
有两个内核参数,涉及到TCP三次握手的队列长度配置
net.core.somaxconn [ESTABLISHED] 已完成连接队列 web 应用中 listen 函数的 backlog 默认会被我们内核参数net.core.somaxconn 限制到128
tcp_max_syn_backlog [SYN-RECEIVED] 未完成连接队列 记录的那些尚未收到客户端确认信息的连接请求的最大值。
我们用两个实验来验证一下这两个参数。
三. 实验环境
两台虚拟机
服务端代码 serv.py
不调用accept函数,已完成三次握手的连接可以保持在队列中
客户端代码 cli.py
客户端程序,每隔10s钟,创建一个socket,并连接到服务端。
四. 实验一
1. 运行服务端程序
2. @服务端 查看网络连接
backlog正确设置为2
3. @服务端 修改服务端代码
4. 运行服务端程序
5. @服务端 查看网络连接
backlog设置为128,这并不是我们期望的
再看另外一个内核参数
是的,backlog 默认会被我们内核参数net.core.somaxconn 限制
6. @服务端 修改内核参数
7. 重新运行服务端程序
8. @服务端 查看网络连接
这次,backlog被正确设置为512啦
背景知识
五. 实验二
1. @服务端 运行服务端程序
再打开另一个终端查看
很明显,端口1234已经被监听
2. @客户端 运行客户端程序
3. @服务端 查看连接状态
很明显,端口1234只有3个已完成连接,后端连接都停留在SYN_RECV。
Q:为什么是3个ESTABLISHED连接,而不是两个呢? A:因为Linux实现的是队列长度=backlog+1 参见 《UNIX网络编程》第四章第五节 listen函数
6. backlog队列满了之后,系统怎么处理后续连接请求
服务端运行抓包命令(抓包结果删除部分无用信息)
服务端忽略了客户端返回的ack信息,然后重新发送了6次syn+ack, 连接处于SYN_RECV,一段时间后,自动删除
7. @服务端 查看连接状态
可见,在客户端看来,连接已经建立完成。这中情况下,就可能造成服务端和客户端连接状态不一致的情况。
4. @客户端 中断客户端程序,然后再重启
5. @服务端 查看连接状态
有3个连接处于CLOSE_WAIT,新连接已经无法建立,说明已完成连接包含状态CLOSE_WAIT。 这个CLOSE_WAIT的产生条件,建立连接后,没有被accept关联到文件描述符后,客户端异常关闭
六. 实验三
tcp_max_syn_backlog
, 这个参数很容易和listen函数的backlog混淆。看一下官方解释详细解释请查看 src/linux-3.10.105/Documentation/networking/ip-sysctl.txt
我们继续实验。
1. @服务端 修改两个内核参数值
Q: 为什么关闭SYN Cookie? A: When syncookies are enabled there is no logical maximum length and this sysctl(tcp_max_syn_backlog) setting is ignored.
2. @服务端 运行serv.py
3. @客户端 添加iptables规则
禁止客户端发送RST和ACK
4. @客户端 运行cli.py
修改其中一条打印语句
5. @服务端 查看网络连接
有3个sock已经建立连接,有4个sock处于半连接,符合我们的预期
5. @客户端 查看程序打印的日志
前面7个socket都是每秒建立一个连接,第8个连接经过大约64秒后才建立,说明服务端处于SYN_RECV状态的连接超时被删除,第8个连接才得以完成三次握手。
6. @客户端 查看网络连接状态
第8个连接处于SYN_SENT状态,服务端没有返回SYN/ACK, 可以确认服务器丢弃了客户端发出的SYN包
7. @服务端 查看系统统计信息和日志
可见,系统已经丢弃了很多SYN包
从系统日志中可以看出,系统确实把第8个连接的SYN丢弃啦。
七. 总结
一般不要直接使用网上找到的一些优化参数,而应该深入了解各个参数的细节,知其然,并知其所以然,这样才能把系统优化的工作做好。