个人主页:小则又沐风
个人专栏:
• [数据结构](https://blog.csdn.net/jiaomorning/category_13115833.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=13115833&sharerefer=PC&sharesource=jiaomorning&sharefrom=from_link "数据结构")
• [竞赛专栏](https://blog.csdn.net/jiaomorning/category_13117056.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=13117056&sharerefer=PC&sharesource=jiaomorning&sharefrom=from_link "竞赛专栏")
• [C语言](https://blog.csdn.net/jiaomorning/category_13115832.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=13115832&sharerefer=PC&sharesource=jiaomorning&sharefrom=from_linkhttps://blog.csdn.net/jiaomorning/category_13115832.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=13115832&sharerefer=PC&sharesource=jiaomorning&sharefrom=from_link "C语言")
• [C++](https://blog.csdn.net/jiaomorning/category_13141438.html "C++")
• [Linux](https://blog.csdn.net/jiaomorning/category_13157216.html "Linux")
• [OJ项目](https://blog.csdn.net/jiaomorning/category_13197107.html "OJ项目")
• [MySQL](https://blog.csdn.net/jiaomorning/category_13202545.html "MySQL")•[GIT](https://blog.csdn.net/jiaomorning/category_13205841.html "GIT")
前言:
我们熟知的传输层协议有 UDP 和 TCP。
UDP 我们知道他的优点就是简单高效。
相较于UDP,TCP的优点就是连接更加稳定。但是我们这些概念只是道听途说,并不知道TCP连接的稳定性到底是怎么保证的。
今天我们就深入到 TCP 的原理中,逐步剖析 TCP 的核心。
我们先来看这一张图片

我们从上述的这张图片中,我们可以知道的是:
我们在进行通信的时候,我们调用的 write、read 这种的系统调用是直接从网络中获取到的信息吗?
不是的.
它们只是从自己的本地缓冲区中拷贝过来的。但是是什么把信息从网络中转移到缓冲区中的呢?
这就是我们今天的介绍的对象了----传输层(TCP)
我们知道在网络通信中TCP扮演的对象就是一个搬运工呗.
但是这个搬运工是怎么知道从哪里来到哪里去.对方需要我搬多少,以及搬运工是否半路走丢等等这些问题呢?
TCP协议段格式

我们来逐步的分析TCP协议
源/目的端口号
总的来说就是:
源端口号记录的是发送方的,目的端口号记录的就是目的地的.
我们来看这样的例子.
为什么唐僧总是说这样的话:
贫僧自东土大唐而来,要前往西天取经.
这句话看起来简单,但是她的信息量都有什么?
源端口号:东土大唐
目的端口:西天
32位序号/确认序号
这个序号的设计是保证TCP协议稳定性的基础.
需要知道的是UDP协议是面向数据报的.但是TCP是面向字节流的.
也就是说TCP传输的数据是按照字节为单位的.
用一句话形象地来形容,就像一条河流一样。
但是与杂乱无序的河流相比TCP将自己的每一个字节都进行了编号.
那么这样的设计有什么作用呢?
首先为什么我们将这个设计和可靠性挂钩呢?
在32位序号中,发送方会指出自己发送数据的区间,例如:1000-1199
如果对方的确是收到了上面的数据的话,就会在确认序号中发送1200表示1200序号之前的数据我都收到了,之后你就从1200发送就可以了.
如果我们发送的数据丢失了呢?
发送:1000-1200 1200-1400 1400-1600
收到:1000-1200 1400-1600
那么确认序号就是1200,我们就需要从1200开始重新发送数据
(后面在讲解滑动窗口的时候我们还会进行讲解的)
另外这样的设计还有的优点就是,对于数据我们知道了他的编号的话,数据的顺序不会杂乱了.
防止重复数据包:网络延迟导致旧包迟到,接收方通过序号判断这包是不是过期重复包,丢弃重复数据
4 位首部长度(重点)
可以看到的是在TCP的协议中,有一个名为选项的空间,但是这个空间的大小是不确定的,那么我们这样就无法正确的把数据和报头分隔开来,所以我们就需要借助4位首部长度来解决上述的问题.
实际上这个就是4位的二进制.但是他的单位是4字节.他的作用就是用来统计当前报文中头部的长度的.
需要知道的是一个TCP空报头是20字节的.
所以向对方发送一个空报头4位首部就是20/4=5;
5对应的二进制是0101.
4位首部的取值范围是0-15
所以我们计算的结果的范围就是0-60
但是一个空报头的大小就是20所以实际上的范围就是20-60
6 位标志位
在通信的过程中,会有不同的信息类型,在TCP中,对不同类型的信息进行了标志.
- URG:紧急指针有效
- ACK:确认序号有效(所有数据传输阶段的报文都必须置 1)
- PSH:提示接收端立即将数据从 TCP 缓冲区推送给应用层
- RST:强制重置连接
- SYN:请求建立连接
- FIN:请求关闭连接
TCP和UDP的区别
造成TCP和UDP有区别的关键就是:
TCP是面向字节流,TCP只对传输的信息将报头和报文进行了分割,但是没有做报文之间的分隔,所以对TCP报文进行分割是应用层的工作.
UDP是面向数据报的,UDP中的一个信息就是存储了UDP报文的大小,并且UDP的报头长度是一个固定的8字节.所以我们可以说的就是UDP报文是"粒粒分明"的.
TCP 可靠性核心:确认应答与超时重传
都说TCP协议可靠性高,但是是什么样的设计来支撑起他的可靠性呢?
那就是TCP的应答机制了
我们在传输信息的时候我们最害怕的是什么?
那就是我们的数据在传输的时候数据丢失了,更加可怕的的就是我们还不知道数据是否丢了
举一个例子:
张三在路上遇到了李四.
张三说:你好啊,李四,这么巧.
李四收到之后说:你好啊.张三.
张三说:你吃饭了吗?要不要一起去吃饭?
张三的这句话传送过去之后,张三是认为对方收到的,但是如果李四没听到呢,会发生什么?
对于李四来说当他回答完你好啊之后,李四就认为通话结束了.
张三却不知道李四没收到.这就很尴尬了.
如果张三丢失的信息不是一个询问的,而是一个类似这样的:我在...等你,我们一起吃饭吧.
那么张三以为李四收到了,去约定的地点等待结果就是没有后续了.
这就是数据丢失造成的后果了.
那么为了应对这个情况,TCP规定在收到信息之后要进行确认应答.
准确的就是将报头中的标志位标上ACK,因为还有确认序号的存在,我们不仅可以让对方知道我们收到了信息,还可以让对方知道我们收到了那些的信息.
这样的机制保证了我们最新信息之前的信息都是没有丢失的(准确的来说是确认序号之前的),但是对于最新的信息却不能保证他的可靠性.
这就是确认应答机制.
但是还是出现了一个问题:
确认应答是真的收到了数据,但是数据真正丢失的话,对方不知道有数据过来就不会进行ACK,那么发送方是怎么知道数据丢失了呢?
这就是我们下面将要讲解的超时重传的机制了
我们回想一下日常情况下,你在和对方说话的时候,对方没有对你的话进行回答.
你的反应是:
说话->等待对方回答->对方没有回答->你继续等待->思考对方是不是没有听到->重复自己刚才的话
类似的道理,当我们的发送方在规定的时间内没有收到ACK,就会对信息进行重发.
当然是有两种情况的:
- 接收方根本没有收到数据
- 接收方收到了数据,但是他发送的ACK丢失了
这都被归类位数据丢失.发送方都没有收到ACK,都会进行重传.
总结
本文深入剖析了 TCP 协议的核心原理。首先介绍了 TCP 与 UDP 的基本差异,随后详细讲解了 TCP 协议段格式中的关键字段,包括源/目的端口号、32 位序号与确认序号、4 位首部长度以及 6 位标志位,并对比了 TCP 与 UDP 在面向字节流和面向数据报上的本质区别。最后重点阐述了 TCP 可靠性的两大核心机制------确认应答与超时重传.
