源代码在这一篇文章里:

https://mp.csdn.net/editor/html/117086464

目录

疑症 1 :TCP 的三次握手、四次挥手

疑症 2 : TCP 连接的初始化序列号能否固定

疑症 3 : 初始化连接的 SYN 超时问题

疑症 4 : TCP 的 Peer 两端同时断开连接

疑症 5 : 四次挥手能不能变成三次挥手呢?

6. 疑症 6 : TCP 的头号疼症 TIME_WAIT 状态

原文链接:

https://cloud.tencent.com/developer/article/1004327

点击查看更多通信与专业知识


说到TCP协议,相信大家都比较熟悉了,对于TCP协议总能说个一二三来,但是TCP协议又是一个非常复杂的协议,其中有不少细节点让人头疼点。本文就是来说说这些头疼点的,浅谈一些TCP的疑难杂症。那么从哪说起呢?当然是从三次握手和四次挥手说起啦,可能大家都知道TCP是三次交互完成连接的建立,四次交互来断开一个连接,那为什么是三次握手和四次挥手呢?反过来不行吗?

疑症 1 :TCP 的三次握手、四次挥手

下面两图大家再熟悉不过了,TCP的三次握手和四次挥手见下面左边的”TCP建立连接”、”TCP数据传送”、”TCP断开连接”时序图和右边的”TCP协议状态机”

TCP三次握手、四次挥手时序图

TCP协议状态机

要弄清TCP建立连接需要几次交互才行,我们需要弄清建立连接进行初始化的目标是什么。TCP进行握手初始化一个连接的目标是:分配资源、初始化序列号(通知peer对端我的初始序列号是多少),知道初始化连接的目标,那么要达成这个目标的过程就简单了,握手过程可以简化为下面的四次交互:

1 ) clien 端首先发送一个 SYN 包告诉 Server 端我的初始序列号是 X。

2 ) Server 端收到 SYN 包后回复给 client 一个 ACK 确认包,告诉 client 说我收到了。

3 ) 接着 Server 端也需要告诉 client 端自己的初始序列号,于是 Server 也发送一个 SYN 包告诉 client 我的初始序列号是Y。

4 ) Client 收到后,回复 Server 一个 ACK 确认包说我知道了。

整个过程4次交互即可完成初始化,但是,细心的同学会发现两个问题:

1. Server发送SYN包是作为发起连接的SYN包,还是作为响应发起者的SYN包呢?怎么区分?比较容易引起混淆

2.Server的ACK确认包和接下来的SYN包可以合成一个SYN ACK包一起发送的,没必要分别单独发送,这样省了一次交互同时也解决了问题1. 这样TCP建立一个连接,三次握手在进行最少次交互的情况下完成了Peer两端的资源分配和初始化序列号的交换。

大部分情况下建立连接需要三次握手,也不一定都是三次,有可能出现四次握手来建立连接的。如下图,当Peer两端同时发起SYN来建立连接的时候,就出现了四次握手来建立连接(对于有些TCP/IP的实现,可能不支持这种同时打开的情况)。

在三次握手过程中,细心的同学可能会有以下疑问:

(2). 初始化序列号X、Y是可以是写死固定的吗,为什么不能呢?

(3). 假如Client发送一个SYN包给Server后就挂了或是不管了,这个时候这个连接处于什么状态呢?会超时吗?为什么呢?

TCP进行断开连接的目标是:回收资源、终止数据传输。由于TCP是全双工的,需要Peer两端分别各自拆除自己通向Peer对端的方向的通信信道。这样需要四次挥手来分别拆除通信信道,就比较清晰明了了。

1)Client 发送一个FIN包来告诉 Server 我已经没数据需要发给 Server了。

2)Server 收到后回复一个 ACK 确认包说我知道了。

3)然后 server 在自己也没数据发送给client后,Server 也发送一个 FIN 包给 Client 告诉 Client 我也已经没数据发给client 了。

4)Client 收到后,就会回复一个 ACK 确认包说我知道了。

到此,四次挥手,这个TCP连接就可以完全拆除了。在四次挥手的过程中,细心的同学可能会有以下疑问:

(4). Client和Server同时发起断开连接的FIN包会怎么样呢,TCP状态是怎么转移的?

(5). 左侧图中的四次挥手过程中,Server端的ACK确认包能不能和接下来的FIN包合并成一个包呢,这样四次挥手就变成三次挥手了。

(6). 四次挥手过程中,首先断开连接的一端,在回复最后一个ACK后,为什么要进行TIME_WAIT呢(超时设置是 2*MSL,RFC793定义了MSL为2分钟,Linux设置成了30s),在TIME_WAIT的时候又不能释放资源,白白让资源占用那么长时间,能不能省了TIME_WAIT呢,为什么?

疑症 2 : TCP 连接的初始化序列号能否固定

如果初始化序列号(缩写为ISN:Inital Sequence Number)可以固定,我们来看看会出现什么问题。假设ISN固定是1,Client和Server建立好一条TCP连接后,Client连续给Server发了10个包,这10个包不知怎么被链路上的路由器缓存了(路由器会毫无先兆地缓存或者丢弃任何的数据包),这个时候碰巧Client挂掉了,然后Client用同样的端口号重新连上Server,Client又连续给Server发了几个包,假设这个时候Client的序列号变成了5。接着,之前被路由器缓存的10个数据包全部被路由到Server端了,Server给Client回复确认号10,这个时候,Client整个都不好了,这是什么情况?我的序列号才到5,你怎么给我的确认号是10了,整个都乱了。

RFC793中,建议ISN和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始,这需要4小时才会产生ISN的回绕问题,这几乎可以保证每个新连接的ISN不会和旧的连接的ISN产生冲突。这种递增方式的ISN,很容易让攻击者猜测到TCP连接的ISN,现在的实现大多是在一个基准值的基础上进行随机的。

疑症 3 : 初始化连接的 SYN 超时问题

Client发送SYN包给Server后挂了,Server回给Client的SYN-ACK一直没收到Client的ACK确认,这个时候这个连接既没建立起来,也不能算失败。这就需要一个超时时间让Server将这个连接断开,否则这个连接就会一直占用Server的SYN连接队列中的一个位置,大量这样的连接就会将Server的SYN连接队列耗尽,让正常的连接无法得到处理。

目前,Linux下默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个连接。由于,SYN超时需要63秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的SYN包给Server(俗称 SYN flood 攻击),用于耗尽Server的SYN队列。对于应对SYN 过多的问题,linux提供了几个TCP参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 来调整应对。

疑症 4 : TCP 的 Peer 两端同时断开连接

由上面的”TCP协议状态机 “图可以看出,TCP的Peer端在收到对端的FIN包前发出了FIN包,那么该Peer的状态就变成了FIN_WAIT1,Peer在FIN_WAIT1状态下收到对端Peer对自己FIN包的ACK包的话,那么Peer状态就变成FIN_WAIT2,Peer在FIN_WAIT2下收到对端Peer的FIN包,在确认已经收到了对端Peer全部的Data数据包后,就响应一个ACK给对端Peer,然后自己进入TIME_WAIT状态。

但是如果Peer在FIN_WAIT1状态下首先收到对端Peer的FIN包的话,那么该Peer在确认已经收到了对端Peer全部的Data数据包后,就响应一个ACK给对端Peer,然后自己进入CLOSEING状态,Peer在CLOSEING状态下收到自己的FIN包的ACK包的话,那么就进入TIME WAIT 状态。于是,TCP的Peer两端同时发起FIN包进行断开连接,那么两端Peer可能出现完全一样的状态转移 FIN_WAIT1——>CLOSEING——->TIME_WAIT,也就会Client和Server最后同时进入TIME_WAIT状态。

同时关闭连接的状态转移如下图所示:

疑症 5 : 四次挥手能不能变成三次挥手呢?

答案是可能的。

TCP是全双工通信,Cliet在自己已经不会在有新的数据要发送给Server后,可以发送FIN信号告知Server,这边已经终止Client到对端Server那边的数据传输。但是,这个时候对端Server可以继续往Client这边发送数据包。于是,两端数据传输的终止在时序上是独立并且可能会相隔比较长的时间,这个时候就必须最少需要2+2 = 4 次挥手来完全终止这个连接。但是,如果Server在收到Client的FIN包后,在也没数据需要发送给Client了,那么对Client的ACK包和Server自己的FIN包就可以合并成为一个包发送过去,这样四次挥手就可以变成三次了(似乎linux协议栈就是这样实现的)

6. 疑症 6 : TCP 的头号疼症 TIME_WAIT 状态

要说明TIME_WAIT的问题,需要解答以下几个问题:

  • Peer两端,哪一端会进入TIME_WAIT呢?为什么?相信大家都知道,TCP主动关闭连接的那一方会最后进入TIME_WAIT。那么怎么界定主动关闭方呢?是否主动关闭是由FIN包的先后决定的,就是在自己没收到对端Peer的FIN包之前自己发出了FIN包,那么自己就是主动关闭连接的那一方。对于疑症(4) 中描述的情况,那么Peer两边都是主动关闭的一方,两边都会进入TIME_WAIT。为什么是主动关闭的一方进行TIME_WAIT呢,被动关闭的进入TIME_WAIT可以不呢?我们来看看TCP四次挥手可以简单分为下面三个过程过程一.主动关闭方发送FIN; 过程二.被动关闭方收到主动关闭方的FIN后发送该FIN的ACK,被动关闭方发送FIN; 过程三.主动关闭方收到被动关闭方的FIN后发送该FIN的ACK,被动关闭方等待自己FIN的ACK问题就在过程三中,据TCP协议规范,不对ACK进行ACK,如果主动关闭方不进入TIME_WAIT,那么主动关闭方在发送完ACK就走了的话,如果最后发送的ACK在路由过程中丢掉了,最后没能到被动关闭方,这个时候被动关闭方没收到自己FIN的ACK就不能关闭连接,接着被动关闭方会超时重发FIN包,但是这个时候已经没有对端会给该FIN回ACK,被动关闭方就无法正常关闭连接了,所以主动关闭方需要进入TIME_WAIT以便能够重发丢掉的被动关闭方FIN的ACK。
  • TIME_WAIT状态是用来解决或避免什么问题呢?

TIME_WAIT主要是用来解决以下几个问题:

1)上面解释为什么主动关闭方需要进入TIME_WAIT状态中提到的: 主动关闭方需要进入TIME_WAIT以便能够重发丢掉的被动关闭方FIN包的ACK。如果主动关闭方不进入TIME_WAIT,那么在主动关闭方对被动关闭方FIN包的ACK丢失了的时候,被动关闭方由于没收到自己FIN的ACK,会进行重传FIN包,这个FIN包到主动关闭方后,由于这个连接已经不存在于主动关闭方了,这个时候主动关闭方无法识别这个FIN包,协议栈会认为对方疯了,都还没建立连接你给我来个FIN包?,于是回复一个RST包给被动关闭方,被动关闭方就会收到一个错误(我们见的比较多的:connect reset by peer,这里顺便说下 Broken pipe,在收到RST包的时候,还往这个连接写数据,就会收到 Broken pipe错误了),原本应该正常关闭的连接,给我来个错误,很难让人接受。2)防止已经断开的连接1中在链路中残留的FIN包终止掉新的连接2(重用了连接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口)),这个概率比较低,因为涉及到一个匹配问题,迟到的FIN分段的序列号必须落在连接2的一方的期望序列号范围之内,虽然概率低,但是确实可能发生,因为初始序列号都是随机产生的,并且这个序列号是32位的,会回绕。3)防止链路上已经关闭的连接的残余数据包(a lost duplicate packet or a wandering duplicate packet) 干扰正常的数据包,造成数据流的不正常。这个问题和2)类似。

- TIME_WAIT会带来哪些问题呢?

TIME_WAIT带来的问题注意是源于:一个连接进入TIME_WAIT状态后需要等待2*MSL(一般是1到4分钟)那么长的时间才能断开连接释放连接占用的资源,会造成以下问题

1) 作为服务器,短时间内关闭了大量的Client连接,就会造成服务器上出现大量的TIME_WAIT连接,占据大量的tuple,严重消耗着服务器的资源。

2) 作为客户端,短时间内大量的短连接,会大量消耗的Client机器的端口,毕竟端口只有65535个,端口被耗尽了,后续就无法在发起新的连接了。

( 由于上面两个问题,作为客户端需要连本机的一个服务的时候,首选UNIX域套接字而不是TCP )

TIME_WAIT很令人头疼,很多问题是由TIME_WAIT造成的,但是TIME_WAIT又不是多余的不能简单将TIME_WAIT去掉,那么怎么来解决或缓解TIME_WAIT问题呢?可以进行TIME_WAIT的快速回收和重用来缓解TIME_WAIT的问题。有没一些清掉TIME_WAIT的技巧呢?

深入浅出TCP/UDP 原理-TCP篇(1)及完整MATLAB实现UDP通信相关推荐

  1. 深入浅出TCP/UDP 原理-UDP篇(2)及完整MATLAB实现UDP通信

    目录 调试代码在文末 写在前面 3. UDP疑难杂症 3.1 UDP的传输方式:面向报文 3.2 UDP数据包的发送和接收问题 3.3 UDP丢包问题 3.4 UDP冗余传输 4 UDP真的比TCP要 ...

  2. 网络7层架构+TCP/IP原理

    7 层模型主要包括: 1.  物理层:主要定义物理设备标准,如网线的接口类型.光纤的接口类型.各种传输介质的传输速率 等.它的主要作用是传输比特流(就是由 1.0 转化为电流强弱来进行传输,到达目的地 ...

  3. JaveEE UDP 与 TCP 原理

    这篇博客真的很详细很详细很详细,不打算试试看吗 > .o 文章目录 JaveEE & UDP 与 TCP 原理 1. 应用层协议(自定义组织格式) 2. 传输层UDP协议 2.1 数据报 ...

  4. 了解TCP协议,IP协议、ICMP协议和ARP协议(TCP报文,TCP的分成管理,TCP与UDP,TCP的三次握手四次挥手原理)

    文章目录 了解TCP/IP协议 TCP报文格式 TCP/IP 的分层管理 TCP与UDP TCP的三次握手与四次挥手 为什么要三次握手? 为什么要四次挥手? IP数据包格式 ICMP协议 ICMP协议 ...

  5. 网络协议分析 | 传输层 :史上最全UDP、TCP协议详解,一篇通~

    文章目录 UDP 概念 格式 UDP如何实现可靠传输 基于UDP的应用层知名协议 TCP 概念 格式 保证TCP可靠性的八种机制 确认应答.延时应答与捎带应答 超时重传 滑动窗口 滑动窗口协议 后退n ...

  6. Python中的TCP的客户端UDP学习----第一篇博客

    Python中的TCP的客户端&UDP学习--第一篇博客 PS: 每日的怼人句子"我真想把我的脑子放到你的身体里,让你感受一下智慧的光芒" 先说UDP流程 发送: 创建套接 ...

  7. 【网络原理】UDP和TCP协议重点知识汇总

    目录 1.UDP协议: 2.TCP协议: 1.UDP协议: UDP协议的特点:无连接.不可靠传输.面向数据报和全双工. UDP报文最大长度是2个字节,2个字节表示的范围就是0~65535,也就是64k ...

  8. 阿里终面:怎么用 UDP 实现 TCP?

    本篇来自于我的一次真实面试经历. 背景 本题是我在面试中,技术总监问我的一道真题,当时答得不太好,所以把它揪出来总结了下.后来问了下总监,总监说这是阿里的面试题.. 其实面试官主要是想让我说出 UDP ...

  9. TCP/IP***原理分析总结

    TCP/IP***原理分析总结 作者: 出处:IT168  ( 20 ) 砖  ( 24 ) 好  评论 ( 0 ) 条 进入论坛 更新时间:2006-03-30 16:14 关 键 词:TCP  I ...

最新文章

  1. 配置隧道模式的IPSec.×××
  2. Matplotlib常见图形绘制(折线图、散点图、柱状图、直方图、饼图)
  3. 高可用高性能负载均衡软件HAproxy详解指南-第三章:HAproxy实例
  4. vue中使用百度地图为啥是空白_vue中使用腾讯地图(尝试篇)
  5. AC日记——食物链 codevs 1047
  6. asp.net 服务器控件的 ID,ClientID,UniqueID 的区别
  7. 一年中最后一个月的最后一天说说_新的一年又开始了说说 年底最后一天的心情说说...
  8. calc() ---一个会计算的css属性
  9. final关键字/abstract关键字
  10. 12 个非常有用的 JavaScript 技巧
  11. SharePoint 站点出现Http 503 错误
  12. 文件选择器看这个就够了—轻量级、支持多种文件类型
  13. Unity手游开发与实战
  14. 第七章 面向对象分析
  15. android 进入三方应用动画,Android 动画各种实现总结分享 AndroidAnimationExercise
  16. 密码库LibTomCrypt学习记录——(2)分组密码算法的工作模式
  17. 由入门C语言题目浅析gets()函数的用法
  18. json格式转csv格式
  19. 艺术留学|工业设计专业2019大学新排名
  20. 计算机光驱启动设置,bios设置光驱启动图文教程

热门文章

  1. 空场景在安卓上的渲染消耗问题
  2. 2018.8.14笔记
  3. C# lambda表达式及初始化器
  4. Redis过期策略及实现原理
  5. 金蝶CLOUD星空云tempdb过大处理方法
  6. QTP的那些事--调用外部的文件的方法
  7. 从Powershell 入侵脚本学到的如何执行后台runspace~
  8. SQL查询存在一个表而不在另一个表中的数据
  9. 第十八章 8string类insert成员函数的使用
  10. Perl学习笔记(2)