TCP协议同样是运输层的协议,掌握TCP重点要关注这几个问题:顺序问题、丢包问题、连接维护、流量控制、拥塞控制。先解析下TCP报文段结构,相比于UDP要复杂很多。

  • 首先还是两个端口号,对应着具体的应用进程。
  • 序号指的是包的序号,为了解决包乱序问题。
  • 发出去的包应该有确认,如果接收方没有收到就应该重新发送,直到送达。确认序号解决的是丢包问题。
  • 由于TCP选项字段的原因,TCP首部长度是可变的。通常选项字段为空,所以TCP首部字段长度一般是20字节。
  • 窗口大小用于流量控制,用来指示接收方愿意接收的字节数量。
  • 选项字段用于发送方与接收方协商最大报文段长度或者在高速网络环境下用作窗口调节因子。
  • 标志字段由6个状态位组成,SYN是发起一个连接、ACK是回复、RST是重新连接、FIN是结束连接。URG表示报文段里存在着被发送端的上层实体置为“紧急” 的数据,紧急数据的最后一个字节由16b位的紧急数据指针字段指出。实际使用中,PSH、URG和紧急数据指针用不到。

所有的问题起于连接,先关注连接管理问题。TCP的连接建立分为三步,通常称为三次握手。具体的的状态是:

客户端收到服务端发送的 SYN 和 ACK 之后,发送ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 对ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

为什么不是两次、四次或者更多次?

  比如AB要建立连接,A发起一个连接但连接请求没有响应,可能包丢了、包绕弯路了、超时了等等,A于是继续给B发包,终于B收到了请求包但A并不知道,所以A可能还会继续发。B收到包之后,知道了A存在并且要和它建立连接。如果B不想建立连接,A重试一会后放弃,连接建立失败没有问题。如果B愿意建立连接,那就会发应答包给A。同样这个应答包会和请求包出现一样的问题,那么对于B不能认为连接建立好了。

  B发送的应答包可能会发送多次,只要有一次到达A,那么A就认为连接已经建立了,因为A的消息有去有回,相当于两次握手。假如这时候连接已经建立,并且做了简单通信后,结束了连接。上面讲了A要建立连接的时候,可能会发送多个请求包。有的请求包绕了一大圈又回来了,B 会认为这也是一个正常的的请求,再次建立连接,但这个连接不会正常接发数据、也不会终结,B就一直等待。A发的消息有去有回,但B发出的消息没有回,所以两次握手肯定不行。其实三次握手中,A发给B回复的回复也会丢、绕路或者B挂了,按照这个逻辑B还要再发个确认就变成了四次握手,这样不停套娃多达几百次也可以,但这也不能保证就可靠了。TCP连接的设计原则是双发的消息都有去有回就基本可以了。

  大部分情况下,A和B建立连接后会A会马上发送数据,如果A发给B的应答丢了,A后续发送的数据到达时,B可以认为连接已经建立;又或者B挂了,那么直接报错返回B不可达信息都没问题。如果A就是不发送数据,客户端可以开启keeplive机制,发送探活包。服务端设置规定时间,超时了就主动关闭,释放资源。

  TCP把数据看成一个无结构、有序的字节流,一个报文段的序号是该报文段首字节的字节流编号。三次握手除了确保双方消息有去有回,最重要的是解决tcp包的序号问题。A、B双方互相告知对方包的起始序号。又会有个疑问,为什么一定要交换序号,不能从序号1开始发包吗?假设A给B发3个包,序号为1、2、3,但3包可能绕路了,没有正常到达B,A又重新发送。后来A掉线了,重新连上B后又从1开始,发送两个包1、2,但上次出问题的包3又回来了,发给了B,B认为这就是A要发给它的下一个包,于是出现错误。因此每个连接都要有不同的序号,序号的起始序号每4ms加一,等遇到重复的序号要4个多小时。比如3号包绕路了,就要隔4个多小时连接的起始序号才是3,这么长时间3号包已经无效了,因为IP包头里有TTL(生存时间)。

再来看四次挥手,前两次和握手连接差不多。同样将发送接收方用AB表示。当B进入CLOSED-WAIT,A进入FIN-WAIT2状态时,如果B突然挂了,那么A会一直处于这个状态,TCP本身没有对这种个状态进行处理。在Linux中可以调整 tcp_fin_timeout 参数,设置超时时间。

  如果一切正常,进行第三次挥手A收到B的主动关闭请求并发送确认后进入TIME-WAIT状态,为什么需要这个状态呢?假设A发送完确认消息后直接关闭,如果B没收到这个确认消息,那B会重发结果就是B关闭失败。还有一种情况是A的端口空出来,但B还保持原来状态不知道A的情况。如果 A 的端口被一个新的应用占用了,这个新的应用会收到上个连接中 B 发过来的包,数据接收错误。所以A要进入TIME-WAIT状态,并且TCP设置了等待时间2MSL(报文段最大生存时间),任何报文在网络上存在超过这个时间将被丢弃。协议规定 MSL 为 2 分钟,实际应用中常用的是 30秒,1 分钟和 2 分钟等。还有一种情况是超过了2MSL,B依然没有收到对FIN的ACK。这时即便A收到了B重发的FIN,A会直接发送RST,表示重新开始挥手。

参考资料:《趣谈网络协议》刘超

     《计算机网络:自顶向下方法》原书第六版 陈鸣译

详解TCP一:三次握手、四次挥手的更多相关文章

  1. 详解 TCP的三次握手四次挥手

    本文转载来自https://blog.csdn.net/qzcsu/article/details/72861891 背景描述 通过上一篇中网络模型中的IP层的介绍,我们知道网络层,可以实现两个主机之 ...

  2. 端口状态 LISTENING、ESTABLISHED、TIME_WAIT及CLOSE_WAIT详解,以及三次握手四次挥手,滑动窗口(整理转发)

    网上查了一下端口状态的资料,我下面总结了一下,自己学习学习: TCP状态转移要点 TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死 ...

  3. 详解TCP的三次握手四次断开

    本文将分别讲解经典的TCP协议建立连接(所谓的“3次握手”)和断开连接(所谓的“4次挥手”)的过程. 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提 ...

  4. 在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

    如果对网络工程基础不牢,建议通读<细说OSI七层协议模型及OSI参考模型中的数据封装过程?> 下面就是TCP/IP(Transmission Control Protoco/Interne ...

  5. [na]TCP的三次握手四次挥手/SYN泛洪

    1.TCP报文格式 上图中有几个字段需要重点介绍下: (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记. (2)确认序号:Ack序号,占32位, ...

  6. 救救孩子吧,到现在还搞不懂TCP的三次握手四次挥手

    本文在个人技术博客同步发布,详情可用力戳 亦可扫描屏幕右侧二维码关注个人公众号,公众号内有个人联系方式,等你来撩...   前几天发了一个朋友圈,发现暗恋已久的女生给我点了个赞,于是我当晚辗转反侧.彻 ...

  7. TCP/IP三次握手四次挥手

    本文通过图来梳理TCP-IP协议相关知识.TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道.如图所示,给出了TCP通信过程的示意图. TCP 三次握手四次挥手 主要包括三部 ...

  8. TCP协议—三次握手四次挥手的原理<转>

    三次握手四次挥手的原理   TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接.在TCP/IP协议中,TCP 协议提供可靠的连接服务,连接是通过三次握手进行初始化的.三 ...

  9. 第6章 传输层(详解TCP的三次握手与四次挥手)

    第6章 传输层 传输层简介 传输层为网络应用程序提供了一个接口,并且能够对网络传输提供了可选的错误检测.流量控制和验证功能.TCP/IP传输层包含很多有用的协议,能够提供数据在网络传输所需的必要寻址信 ...

  10. 通俗了解TCP/IP三次握手四次挥手

    前言: tcp/ip通信机制是计算机中很重要的一个知识点,不是一句两句就能解释清楚的,需要反复推敲其中的玄妙. 通俗理解: 但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?我 ...

随机推荐

  1. npm: no such file or directory, scandir '.../node_modules/node-sass/vendor'

    运行vue报错 npm run dev 解决办法,运行:npm rebuild node-sass

  2. 向强大的SVG迈进

    作者:凹凸曼 - 暖暖 SVG 即 Scalable Vector Graphics 可缩放矢量图形,使用XML格式定义图形. 一.SVG印象 SVG 的应用十分广泛,得益于 SVG 强大的各种特性. ...

  3. 深度解剖dubbo源码---01dubbo的架构原理-探索.mp4

    02内核解剖-dubbo自己的SPI实现.mp4 https://blog.csdn.net/prestigeding/article/details/80795708 https://segment ...

  4. 二分查找法demo

    正文 中午闲着有点时间,做个demo睡觉去,这个例子网上应该都有,自己只是敲一下给自己做个记录. public static void main(String[] args) { int[] whit ...

  5. 【原】二进制部署 k8s 1.18.3

    二进制部署 k8s 1.18.3 1.相关前置信息 1.1 版本信息 kube_version: v1.18.3 etcd_version: v3.4.9 flannel: v0.12.0 cored ...

  6. Idea集成git常用命令

    git status --查看文件状态   untracked: 未跟踪 一般为新增文件  git add 状态改为staged git add +文件 git add -A +路径  修改过的未被跟 ...

  7. cv2.VideoCapture 图像旋转问题

    使用cv2.VideoCapture()时发现,分解后的图片均顺时针旋转90度, 为了重新转回来使用np.rot90(mat, 1)即逆时针将矩阵旋转90度. 大功告成!!!

  8. Python抓取国家医疗费用数据:国家名、人均开销

    前言 整个世界正被大流行困扰着,不同国家拿出了不同的应对策略,也取得了不同效果.这也是本文的脑洞来源,打算研究一下各国在医疗基础设置上的开支,对几个国家的医疗费用进行数据可视化. 由于没有找到最近一年 ...

  9. django项目常见报错集

    1.mysqlclient 目前不支持高版本python3 django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or new ...

  10. Docker环境下Java应用的最大内存和堆内存的设置

    Docker环境下Java应用的最大内存和堆内存的设置 1.  设置应用允许使用的最大内存 通过docker run(创建一个新的容器并运行)命令中设置-m来进行设置.案例如下所示. docker r ...