🌲TCP协议的概念
TCP(TransmissionControlProtocol 传输控制协议)是一种面向连接的、可靠的、面向字节流,双全工的传输层通信协议。
这几个特点在我们前面写得TCP服务器和客户端的搭建中,代码能够直观的感受到,但是这个可靠传输我们是无法直观的感受到的,但是这个特性其实是TCP最核心的部分!!!
本文章就来谈谈关于可靠传输具体是怎么回事!
🚩TCP协议段格式
- 源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
- 32位序号/32位确认号:后面详细讲;
- 4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15 * 4 = 60
- 保留(6位):未来某天TCP需要扩展一些新功能,就可以使用这个保留位来表示
- 6位标志位: URG:紧急指针是否有效 ACK:确认号是否有效 PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走 RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段 SYN:请求建立连接;我们把携带SYN标识的称为同步报文段 FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
- 16位窗口大小:后面再说
- 16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光包含TCP首部,也包含TCP数据部分
- 16位紧急指针:标识哪部分数据是紧急数据
- 选项:可理解为是可选项,可以选择加还是不加(这块可有可无就对TCP的报头产生影响),如果选项完全没有,tcp报头长度就是20个字节(5行,一行4个字节),如果选项拉满,tcp报头最长是60个字节。最大长度是60,去掉固定的20,剩下选项部分最多40个字节。
🚩TCP的特性
-
TCP提供一种面向连接的, 可靠的字节流服务;
-
在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP;
-
TCP使用校验和, 确认和重传机制来保证可靠传输;
-
TCP使用累积确认
-
TCP使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制
上述特性,在下面的TCP原理里面回进行一一介绍
🌳TCP原理
TCP对数据传输提供的管控机制,主要体现在两个方面:安全和效率。
这些机制和多线程的设计原则类似:保证数据传输安全的前提下,尽可能的提高传输效率。
下面我们会讲述10个TCP的核心机制!!!(当然TCP的机制不止10个)
🚩确认应答机制
对于TCP来说,要解决一个很重要的问题,就是可靠传输。确认应答就是保证TCP可靠性的核心机制。
所谓可靠传输,不是说你发送方能够100%的把数据发送给接收方,但是会尽可能,尤其是能够让发送方知道,接收方是否收到数据。
确认应答机制图示如下:
单看这一幅图还是比较懵逼的,接下来我为大家解答一下
我们知道TCP是属于可靠传输,它就为了解决UDP不可靠传输而发明的。我们有了确认应答机制后,我们每发送一个消息,都能收到对方的一个回应,确保自己知道自己的消息发过去了。
就像一个小伙子给他女神发消息说"我请你吃饭好吗?",然后收到了女神的回复"好啊好啊",这样的回应就是确认应答。这个"好啊好啊"就是应答数据,称为"应答报文"。
要想知道对方是否已经收到,核心的机制就是靠对方给你回应,给你一个应答。
在这个传输过程中了,我们用上述提到的ACK来表示请求和应答报文
-
ACK=0,表示是发送报文
-
ACK=1,表示是应答报文
但是光有确认应答还是不够,比如出现以下情况
小伙子对女神说"我请你吃饭好吗?",然后这时候女生还没有回复,然后小伙又发了一句"做我女朋友好吗?",这时候女神回消息了,回了两句为"好啊好啊","滚"。此时就有两条应答报文,那么女神给我回的两条报文是按照发送的顺序来传输的,也不难理解此时的含义(吃饭,不做女朋友)。
但是在网络传输过程中,会出现一种情况,叫做"后发先至",也就是后发的消息反而先到,先发的消息反而后到。
如果此时出现了"后发先至",小伙子对女神说"我请你吃饭好吗?",然后这时候女生还没有回复,然后小伙又发了一句"做我女朋友好吗?",这时候女神回消息了,回了两句为"滚","好啊好啊"
那么这时候的小伙就懵了,小伙就想
- 女神是现在不想吃饭,给我发了一句滚,她还是愿意做为我的女朋友的
- 女神不想做我女朋友,但是想和一起吃饭
这时候的小伙也就迷茫了,女神到底什么意思呢?
针对不同的应答报文(ack)我们引入的序号和确认序号来区分不同的ack报文。
注意:后发先至是网络通信中客观存在的,改变不了。
我们引入一个序号和确认序号。只要能够对传输数据进行编号,并且让应答报文的编号和发送数据的编号,能够一一对应,即使出现后发先至,也不影响对于传输意思的理解
在上述的32位序号和32位确认序号,此时32位序号就是发送数据的序号,32位确认序号就是给应答报文用到(ack为1的时候才有效),这样的数据就可以根据确认序号区分出要应答哪个上面的报文了。
实际上TCP的序号和确认序号并非我们说的这么简单,实际真实的tcp序号不是按照"一条两条"方式来编号的,由于TCP是面向字节流的,所以是按照字节来编号的,每个字节都会分配一个序号。
比如说TCP载荷中的数据为1000字节:
32位序号:
32位确认序号:
图片示例:
🚩超时重传
TCP最核心的功能就是可靠传输,可靠传输之所以能达成,主要是依靠"确认应答"机制,这里是通过应答报文来通知发送方,我已经收到请求,这是一切顺利的情况,如果不顺利呢?假如出现"丢包"呢??
丢包是指数据在传输过程中,被丢弃,无法到达对端,也是客观存在的随机事件。
那为什么会出现丢包,是因为在网络传输过程中,里面错综复杂,A给B传输数据,会经过多个路由器和交换机,这些路由器交换机又不止是给A和B提供数据传输服务,整个网络是非常繁忙的。若其中一个路由器/交换机过于繁忙!!!
这么复杂的网络,是不知道哪个设备出现丢包,什么时候出现丢包,所以丢包是客观存在的事件,是随机事件。网络环境本身是不可靠的,但是TCP就是要在不可靠的环境中,构造出可靠的通信方式。
那么就引入了超时重传机制,就是用来应对网络出现丢包的情况。正常情况下,TCP就是通过确认应答来知道数据是否被对端收到了。假如A给B传输数据,若过程中出现丢包了,那么B不会收到A发来的数据,此时B也不可能给出任何应答。A就可以根据"是否收到了ACK"来区分是否出现丢包。
当A从发送数据之后,到正常收到ACK,在这中间肯定也需要一定的时间,A就会进行等待,如果等待时间超过了某个阈值,还没有收到ACK,此时就可以认为出现丢包了。
那么主机A没有收到ACK有两种情况:
- 主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B(数据丢了)
- 主机B收到主机A的数据之后,做出应答后,应答报文没有到达主机A(ack丢了)
这两种情况都当成第一种情况处理,客户端会进行重传数据
情况一:
情况二:
站在主机A的角度,A是无法区分是数据丢了,还是ACK丢了,A能看到的都是没有收到ACK。A做的事情就是触发重传。
但是呢,对于情况一,重传数据就好,但是如果出现第二种情况,那么这些数据不是相同了吗,不就出现数据重复了吗?很明显,这是不科学的。
TCP接收方,会针对收到的数据进行去重,会按照序号来进行去重。
这时候我们可以利用前面提到的序列号,其实我们这里的主机B这里接收就像一个优先级的阻塞队列,我们会对传来的数据按照序列号进行排序,如果序列号相同,该队列还可以起到一个去重的效果
那么我们又会想超时的时间如何确定?
- 最理想的情况下,找到一个最小的时间,保证 "确认应答一定能在这个时间内返回"。
- 但是这个时间的长短,随着网络环境的不同,是有差异的。
- 如果超时时间设的太长,会影响整体的重传效率;
- 如果超时时间设的太短,有可能会频繁发送重复的包;
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间
- Linux中(BSD Unix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。
- 如果重发一次之后,仍然得不到应答,等待 2*500ms 后再进行重传。
- 如果仍然得不到应答,等待 4*500ms 进行重传。依次类推,以指数形式递增。
- 累计到一定的重传次数,TCP认为网络或者对端主机出现异常,就会触发"重置报文",也就是上述六个标志位的RST(本身的含义是清空之前的中间状态,重新进行传输),RST过去还没有效果,就会释放连接(删除对方的相关信息)。
🚩 连接管理
🚩滑动窗口
🚩流量控制
🚩拥塞控制
🚩延迟应答
上述这些都会一一讲述