曲谱网 > 知识库 >

导航导航

TCP 三次握手与四次断开--JAVA成长之路

发布日期:2021-01-18 15:50:11编辑:音乐人

三次握手建立连接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 内核参数
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 droppedoverflowed 表示全连接队列溢出的次数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_retries6TCP 第二次握手 SYN、ACK 包的最大重传次数是通过 tcp_synack_retries 内核参数限制的cat /proc/sys/net/ipv4/tcp_synack_retries2TCP 建立连接后的数据包传输,最大超时重传次数是由 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 秒]支持 SACKTCP 三次握手时协商开启了选择性确认 SACK,因此一旦数据包丢失并收到重复 ACK ,即使在丢失数据包之后还成功接收了其他数据包,也只需要重传丢失的数据包。如果不启用 SACK,就必须重传丢失包之后的每个数据包。如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。/proc/sys/net/ipv4/tcp_sack

大家都在看

最新资讯

推荐专题

儿童歌曲大全 儿童故事大全 卡农钢琴曲谱 天空之城钢琴曲谱 梦中的婚礼钢琴曲谱 梁祝》钢琴曲谱 童年的回忆钢琴曲谱 彩云追月钢琴曲谱 康定情歌钢琴曲谱 水边的阿狄丽娜钢琴曲谱 渔舟唱晚古筝曲谱 云水禅心古筝曲谱 高山流水古筝曲谱 浏阳河古筝曲谱 南泥湾古筝曲谱 梅花三弄古筝曲谱 笑傲江湖古筝曲谱 青花瓷古筝曲谱 月光下的凤尾竹葫芦丝曲谱 婚誓葫芦丝曲谱 荷塘月色葫芦丝曲谱 映山红葫芦丝简谱 军港之夜葫芦丝简谱 青花瓷葫芦丝简谱 蝴蝶泉边葫芦丝曲谱 美丽的神话葫芦丝曲谱 致爱丽丝电子琴谱 小苹果 电子琴谱 天空之城 电子琴谱 婚礼进行曲 电子琴谱 茉莉花 电子琴谱 红河谷曲谱