TCP三向握手

您到底有多了解? -阿里巴巴技术团队的最佳做法

任希军是阿里巴巴中间件技术团队的成员。 最近,他遇到了一个客户端通信服务器的问题,该问题一直在引发异常。 但令他沮丧的是,尽管搜寻互联网上的信息并反复尝试查找原因,但他找不到任何有助于解释这两个队列或如何观察其指标的东西。

他无所畏惧,自负不下。 他写了这篇文章来记录他如何识别和解决该问题。

一个烦人的问题

在Java中,客户端和服务器使用套接字进行通信。 在这种情况下,将使用NIO服务器。 发生以下状态:

·间歇执行三向握手以在客户端和服务器之间建立连接,但是侦听套接字没有响应。

·然后,该问题同时在许多其他连接中发生。

·NIO选择器未销毁并重新创建。 使用的始终是第一个。

·启动程序时出现问题,此后间歇出现。

概述:TCP三向握手如何工作?

我做的第一件事是提醒自己建立TCP连接时三向握手的标准过程。 标准过程如下:

1.客户端将SYN数据包发送到服务器以发起握手。

2.收到此消息后,服务器将SYN-ACK数据包发送到客户端。

3.最后,客户端将ACK数据包发送到服务器,以表明它已收到服务器的SYN-ACK数据包。 (到此为止,已经通过客户端的端口56911建立了到服务器的连接。)

TCP三向握手过程

快速修复

从对问题的描述来看,这听起来与在建立TCP连接期间TCP完整连接队列(或接受队列,将在后面讨论)充满时类似。 为了确认这一点,我通过netstat -s |检查了队列的溢出统计信息。 egrep“听”。

667399 times the listen queue of a socket overflowed

经过三遍检查,我发现该值在不断增加。 很明显,服务器上的接受队列已溢出。

然后可以看到操作系统如何处理溢出。

# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0

如果tcp_abort_on_overflow为0,则在三向握手的第三步中,如果接受队列已满,则服务器将丢弃客户端发送的ACK数据包,因为它假定尚未在服务器端建立连接。

为了证明该异常与完整的连接队列有关,我首先将tcp_abort_on_overflow更改为1。如果在第三步中完整的连接队列已满,则服务器将向客户端发送一个重置数据包,指示它应同时结束两个握手过程和连接。 (实际上未在服务器端建立连接。)

然后,我进行了测试,发现客户端中存在许多“对等连接重置”异常。 我们得出的结论是,完整的连接队列的溢出又导致了客户端错误,这有助于我们快速确定问题的关键部分。

开发团队查看了Java源代码,发现套接字的待办事项的默认值为50(此值控制整个连接队列的大小,稍后将进行详细介绍)。 我增加了该值,然后再次运行它,经过12个小时的压力测试后,我注意到该错误不再显示了,并且溢出也没有增加。

因此,就这么简单。 TCP三向握手发生后,连接队列完全溢出,只有进入此队列后,服务器才能从侦听更改为接受。 待办事项的默认值为50,这很容易溢出。 如果溢出,则在握手的第三步,服务器将忽略客户端发送的ACK数据包。 服务器将定期重复第二步(将SYN-ACK数据包发送到客户端)。 如果连接未排队,则会导致异常。

但是,尽管我们解决了问题,但我仍然不满意。 我想将整个遭遇作为学习经验,所以我进一步研究了这个问题。

深入研究:TCP握手过程和队列

(来源: http//www.cnxct.com/something-about-phpfpm-s-backlog/

如上所示,有两个队列:SYN队列(或不完整的连接队列)和接受队列(或完整的连接队列)。

在三向握手中,服务器从客户端接收到SYN数据包后,将连接信息放入SYN队列中,并将SYN-ACK数据包发送回客户端。

然后,服务器从客户端接收ACK数据包。 如果接受队列未满,则应该从SYN队列中删除信息并将其放入接受队列,或者按照tcp_abort_on_overflow指示执行。

此时,如果接受队列已满且tcp_abort_on_overflow为0,则服务器将在一定时间后再次向客户端发送SYN-ACK数据包(换句话说,它重复握手的第二步)。 如果客户端经历了短暂的超时,则很容易遇到客户端异常。

在我们的操作系统中,第二步默认情况下重试两次(CentOS为5次)。

net.ipv4.tcp_synack_retries = 2

一种新方法

上面详述的解决方案有点令人困惑,您可能想知道是否有更快或更简单的方法来解决这些问题。 让我们先来看一些有用的命令。

指令

netstat –s

[root@server ~]# netstat -s | egrep "listen|LISTEN"
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored

在这里,例如667399表示接受队列溢出的次数。 每隔几秒钟执行一次此命令,如果数量增加,则接受队列必须已满。

ss命令

[root@server ~]# ss -lnt
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 50 *:3306 *:*

此处,第二列中的Send-Q值为50,表示侦听端口(第三列)上的接受队列最多为50。 第一列Recv-Q指示当前正在使用的接受队列的数量。

接受队列的大小取决于min(backlog,somaxconn)。 创建套接字时将传递积压,因此somaxconn是操作系统级别的系统参数。

此时,我们可以与我们的代码建立联系。 例如,当Java创建ServerSocket时,它将允许您传递积压的值。

(来源: https : //docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html

SYN队列的大小取决于max(64,/ proc / sys / net / ipv4 / tcp_max_syn_backlog),不同版本的OS可能不同。

netstat命令

就像ss命令一样,也可以通过netstat命令显示Send-Q和Recv-Q。 但是,如果连接未处于“侦听”状态,则Recv-Q表示接收到的数据仍在高速缓存中,并且尚未被进程读取。 该值表示该进程尚未读取的字节。 发送是发送队列中尚未被远程主机确认的字节数。

$netstat -tn
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp0 0 100.81.180.187:8182 10.183.199.10:15260 SYN_RECV
tcp0 0 100.81.180.187:43511 10.137.67.18:19796 TIME_WAIT
tcp0 0 100.81.180.187:2376 100.81.183.84:42459 ESTABLISHED

重要的是要注意,netstat -tn显示的Recv-Q数据与接受队列或SYN队列无关。 这里必须强调这一点,以免将其与ss -lnt所示的Recv-Q数据混淆。

例如,以下netstat -t可以看到Recv-Q积累了很多数据,这通常是CPU处理失败引起的。

验证程序

要验证上述详细信息,请将Java中的积压值更改为10(值越小,越容易溢出),然后继续运行压力测试。 然后,客户端开始报告异常,然后可以通过服务器上的ss命令观察以下情况。

Fri May 5 13:50:23 CST 2017
Recv-Q Send-QLocal Address:Port Peer Address:Port
11 10 *:3306 *:*

在这里我们可以看到端口3306上的服务接受队列最多为10个,但是队列中现在有11个连接。 必须有一个不能排队并且将溢出的队列。 同时,溢出的值确实不断增加。

在Tomcat和Nginx中接受队列大小

Tomcat默认为临时连接。 在Ali-Tomcat中,待办事项的默认值(在Tomcat中为“接受计数”)为200。在Apache Tomcat中,默认值为100。

#ss -lnt
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 100 *:8080 *:*

在Nginx中,待办事项的默认值为511。

$sudo ss -lnt
State Recv-Q Send-Q Local Address:PortPeer Address:Port
LISTEN 0 511 *:8085 *:*
LISTEN 0 511 *:8085 *:*

Nginx在多进程模式下运行,因此有多个8085,这意味着多个进程都在侦听同一端口,以避免上下文切换并提高性能。

摘要

一旦发生溢出,CPU和线程状态看起来很正常,但是压力并没有增加。 从客户端的角度来看,响应时间(网络+队列+服务时间)很高,但是考虑到服务器日志中的真实服务时间,它实际上很短。 在某些框架(例如JDK和Netty)中,积压的默认值很小,这在某些情况下可能会导致性能问题。

我希望本文能帮助您了解建立TCP连接时SYN队列和accept队列的概念,原理和功能。 接受队列和SYN队列的溢出问题很容易被忽略,但这是至关重要的,尤其是在使用临时连接(例如Nginx和PHP,尽管它们也支持持久连接)的情况下。

(任喜军任喜军的原创文章)

阿里巴巴科技

关于阿里巴巴最新技术的第一手和深入信息→Facebook: “阿里巴巴技术” 。 Twitter: “阿里巴巴技术”

参考

http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

http://www.cnblogs.com/zengkefu/p/5606696.html

http://www.cnxct.com/something-about-phpfpm-s-backlog/

http://jaseywang.me/2014/07/20/tcp-queue-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/

http://jin-yang.github.io/blog/network-synack-queue.html#

http://blog.chinaunix.net/uid-20662820-id-4154399.html

From: https://hackernoon.com/tcp-three-way-handshake-4161eb8aba32

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值