Linux下Tcp保活时间默认多少,TCP 三次握手与四次断开--JAVA成长之路

原标题:TCP 三次握手与四次断开--JAVA成长之路

Linux下Tcp保活时间默认多少,TCP 三次握手与四次断开--JAVA成长之路_第1张图片

三次握手建立连接

TCP连接是通过三次握手来连接的。

第一次握手当客户端向服务器发起连接请求时,客户端会发送同步序列标号SYN到服务器,在这里我们设SYN为x,等待服务器确认,这时客户端的状态为SYN_SENT。

第二次握手当服务器收到客户端发送的SYN后,服务器要做的是确认客户端发送过来的SYN,在这里服务器发送确认包ACK,这里的ACK为x+1,意思是说“我收到了你发送的SYN了”,同时,服务器也会向客户端发送一个SYN包,这里我们设SYN为y。这时服务器的状态为SYN_RECV。

一句话,服务器端发送SYN和ACK两个包。

第三次握手客户端收到服务器发送的SYN和ACK包后,需向服务器发送确认包ACK,“我也收到你发送的SYN了,我这就给你发个确认过去,然后我们即能合体了”,这里的ACK为y+1,发送完毕后,客户端和服务器的状态为ESTABLISH,即TCP连接成功。

在三次握手中,客户端和服务器端都发送两个包SYN和ACK,只不过服务器端的两个包是一次性发过来的,客户端的两个包是分两次发送的。

换个易于理解的视角来看为什么要三次握手。客户端和服务端通信前要进行连接,“三次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的。

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。

从客户端的视角来看,我接到了服务端发送过来的响应数据包,说明服务端接收到了我在第一次握手时发送的网络包,并且成功发送了响应数据包,这就说明,服务端的接收、发送能力正常。

而另一方面,我收到了服务端的响应数据包,说明我第一次发送的网络包成功到达服务端,这样,我自己的发送和接收能力也是正常的。

第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的。

第一、二次握手后,服务端并不知道客户端的接收能力以及自己的发送能力是否正常。

而在第三次握手时,服务端收到了客户端对第二次握手作的回应。从服务端的角度,我在第二次握手时的响应数据发送出去了,客户端接收到了。所以,我的发送能力是正常的。而客户端的接收能力也是正常的。

经历了上面的三次握手过程,客户端和服务端都确认了自己的接收、发送能力是正常的。之后就可以正常通信了。

每次都是接收到数据包的一方可以得到一些结论,发送的一方其实没有任何头绪。

我虽然有发包的动作,但是我怎么知道我有没有发出去,而对方有没有接收到呢?

而从上面的过程可以看到,最少是需要三次握手过程的。两次达不到让双方都得出自己、对方的接收、发送能力都正常的结论。

四次挥手关闭连接

当客户端端和B端要断开连接时,需要四次握手,这里称为四次挥手。断开连接请求可以由客户端发出,也可以由服务器端发出,在这里我们客户端向B端请求断开连接。

第一次挥手客户端向服务器端请求断开连接时会向B端发送一个带有FIN标记的报文段,这里的FIN是FINish的意思。

第二次挥手服务器端收到客户端发送的FIN后,服务器端现在可能现在还有数据没有传完,所以服务器端并不会马上向客户端发送FIN,而是先发送一个确认序号ACK,意思是说“你发的断开连接请求我收到了,但是我现在还有数据没有发完,请稍等一下呗”。

第三次挥手当服务器端的事情忙完了,那么此时服务器端就可以断开连接了,此时服务器端向客户端发送FIN序号,意思是这次可以断开连接了。

第四次挥手客户端收到服务器端发送的FIN后,会向服务器端发送确认ACK,然后经过两个MSL时长后断开连接。

MSL是Maximum Segment Lifetime,最大报文段生存时间,2个MSL是报文段发送和接收的最长时间。在RFC 793中定义MSL通常为2分钟,即超过两分钟即认为这个报文已经在网络中被丢弃了。在 Linux 中查看默认的MSL值(60s):[root@DanCentOS65var]# cat /proc/sys/net/ipv4/tcp_fin_timeout60

====

TCP 连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。

当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个 ACK,此时一个方向的连接关闭。

但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个 FIN 段来关闭此方向上的连接。接收方发送 ACK 确认关闭连接。

注意,接收到 FIN 报文的一方只能回复一个 ACK, 它是无法马上返回对方一个 FIN 报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”。

为什么在第四次挥手后会有2个MSL的延时?

假定网络不可靠,那么第四次发送的ACK可能丢失,即服务器端无法收到这个ACK,如果服务器端收不到这个确认ACK,服务器端会定时向客户端重复发送FIN,直到服务器端收到客户端的确认ACK。所以这个2MSL就是用来处理这个可能丢失的ACK的

ISN

三次握手的一个重要功能是客户端和服务端交换 SYN 段里面指明 ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号:ISN = M + F(localhost, localport, remotehost, remoteport)

M 是一个计时器,每隔 4 毫秒加 1。F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出。

文档涉及到的 Linux 内核参数

Linux下Tcp保活时间默认多少,TCP 三次握手与四次断开--JAVA成长之路_第2张图片

net.ipv4.tcp_max_syn_backlog该参数决定了系统中处于 SYN_RECV 状态的 TCP 连接数量。SYN_RECV 状态指的是当系统收到 SYN 后,作了 SYN+ACK 响应后等待对方回复三次握手阶段中的最后一个 ACK 的阶段。

net.ipv4.tcp_syncookies该参数表示是否打开 TCP 同步标签(SYN_COOKIES),内核必须开启并编译 CONFIG_SYN_COOKIES,SYN_COOKIES 可以防止一个套接字在有过多试图连接到达时引起过载。默认值 0 表示关闭。当该参数被设置为 1 且 SYN_RECV 队列满了之后,内核会对 SYN 包的回复做一定的修改,即,在响应的 SYN+ACK 包中,初始的序列号是由源 IP + Port、目的 IP + Port 及时间这五个参数共同计算出一个值组成精心组装的 TCP 包。由于 ACK 包中确认的序列号并不是之前计算出的值,恶意攻击者无法响应或误判,而请求者会根据收到的 SYN+ACK 包做正确的响应。启用 net.ipv4.tcp_syncookies 后,会忽略 net.ipv4.tcp_max_syn_backlog。

net.ipv4.tcp_synack_retries该参数指明了处于 SYN_RECV 状态时重传 SYN+ACK 包的次数。

net.ipv4.tcp_abort_on_overflow设置该参数为 1 时,当系统在短时间内收到了大量的请求,而相关的应用程序未能处理时,就会发送 Reset 包直接终止这些链接。建议通过优化应用程序的效率来提高处理能力,而不是简单地 Reset。默认值:0。

net.core.somaxconn该参数定义了系统中每一个端口最大的监听队列的长度,是个全局参数。该参数和 net.ipv4.tcp_max_syn_backlog 有关联,后者指的是还在三次握手的半连接的上限,该参数指的是处于 ESTABLISHED 的数量上限。若您的 ECS 实例业务负载很高,则有必要调高该参数。listen(2) 函数中的参数 backlog 同样是指明监听的端口处于 ESTABLISHED 的数量上限,当 backlog 大于 net.core.somaxconn时,以 net.core.somaxconn 参数为准。

net.core.netdev_max_backlog当内核处理速度比网卡接收速度慢时,这部分多出来的包就会被保存在网卡的接收队列上,而该参数说明了这个队列的数量上限。

查看 tcp 队列溢出[root@feicuiyanpin-dev-a ~]# netstat -s |egrep "listen|LISTEN" 1244 times the listen queue of a socket overflowed 4078 SYNs to LISTEN sockets dropped

overflowed 表示全连接队列溢出的次数sockets dropped 表示半连接队列溢出的次数[root@feicuiyanpin-dev-a ~]# ss -lntState Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 100 *:8235 *:* LISTEN 0 100 *:8011 *:*

第三列的 Send-Q 表示 listen 端口上的全连接队列最大为 100第二列的 Recv-Q 表示 全连接队列当前使用了多少

查看全连接,半连接队列大小全连接队列的大小取决于 min(backlog, somaxconn)backlog 是在 socket 创建的时候传入的somaxconn 是一个 os 级别的系统参数 (/proc/sys/net/core/somaxconn)在日常开发中,往往使用servlet容器作为服务端,所以我们也需要关注容器的连接队列大小。tomcat 中的 backlog 叫做 acceptCountjetty 中的 backlog 叫做 acceptQueueSize半连接队列的大小取决于 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。

在 Linux 中,第一次握手的 SYN 超时重传次数,是如下内核参数指定的:cat /proc/sys/net/ipv4/tcp_syn_retries6

TCP 第二次握手 SYN、ACK 包的最大重传次数是通过 tcp_synack_retries 内核参数限制的cat /proc/sys/net/ipv4/tcp_synack_retries2

TCP 建立连接后的数据包传输,最大超时重传次数是由 tcp_retries2 指定,默认值是 15 次cat /proc/sys/net/ipv4/tcp_retries215

如果客户端不发送数据,什么时候才会断开处于 ESTABLISHED 状态的连接这里涉及 TCP 的 保活机制。这个机制的原理是这样的:定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:net.ipv4.tcp_keepalive_time=7200net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9/proc/sys/net/ipv4/tcp_keepalive_time /proc/sys/net/ipv4/tcp_keepalive_intvl /proc/sys/net/ipv4/tcp_keepalive_probes tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。tcp_keepalive_time + (tcp_keepalive_intvl * tcp_keepalive_probes)7200 + ( 75 * 9 ) = 7875s [2 小时 11 分 15 秒]

支持 SACK

TCP 三次握手时协商开启了选择性确认 SACK,因此一旦数据包丢失并收到重复 ACK ,即使在丢失数据包之后还成功接收了其他数据包,也只需要重传丢失的数据包。如果不启用 SACK,就必须重传丢失包之后的每个数据包。如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。/proc/sys/net/ipv4/tcp_sack返回搜狐,查看更多

责任编辑:

你可能感兴趣的:(Linux下Tcp保活时间默认多少,TCP 三次握手与四次断开--JAVA成长之路)