TCP协议的十个重要特性及总结
TCP报头
先来分析分析每部分的含义和作用
源端口号/目的端口号:
表示数据从哪个进程来, 到哪个进程去.
32位序号/32位确认序号:
后面的图中解释
4位首部长度:
表示该tcp报头有多少个4字节(32个bit),所以TCP头部最大长度是15*4=60个字节
6位保留: 顾名思义, 先保留着, 以防万一
6位标志位
URG: 标识紧急指针是否有效
ACK: 标识确认序号是否有效
PSH: 用来提示接收端应用程序立刻将数据从tcp缓冲区读走
RST: 要求重新建立连接. 我们把含有RST标识的报文称为复位报文段
SYN: 请求建立连接. 我们把含有SYN标识的报文称为同步报文段
FIN: 通知对端, 本端即将关闭. 我们把含有FIN标识的报文称为结束报文段
16位窗口大小: 看后图
16位检验和:
由发送端填充, 检验形式有CRC校验等. 如果接收端校验不通过, 则认为数据有问题. 此处的校验和不光包含TCP首部, 也包含TCP数据部分.
16位紧急指针:
用来标识哪部分数据是紧急数据.
选项和数据暂时忽略
1、确认应答(ACK机制,可靠传输的最核心机制)
确认应答其实就跟我们平时打电话一样,比如韦德今天要约詹姆斯去打球,韦德打电话给詹姆斯,然后詹姆斯回复,詹姆斯的每次回复就是一次确认应答。
对上述过程有个大致了解后我们再来看看我们TCP协议
2、 超时重传
确认应答是比较理想的情况.数据在真实情况中传输过程中,可能会丢包的。
举个例子
实际的传输
超时的时间如何确定?
最理想的情况下, 找到一个最小的时间, 保证 “确认应答一定能在这个时间内返回”.
但是这个时间的长短, 随着网络环境的不同, 是有差异的.
如果超时时间设的太长, 会影响整体的重传效率;
如果超时时间设的太短, 有可能会频繁发送重复的包;
TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.
Linux中(BSD Unix和Windows也是如此), 超时以500ms(可以修改内核参数进行改变)为一个单位进行控制, 每次判定超时重发的超时时 间都是500ms的整数倍.
如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
如果仍然得不到应答, 等待 4 * 500ms 进行重传.
依次类推, 以指数形式递增累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.
3、连接管理机制
在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接
三次握手
三次握手是为了建立连接
1.验证通信双方发送能力和接收能力是否正常.
2.通信双方要协商一些重要参数.
第一次:
客户端 - - > 服务器 此时服务器知道了客户端要建立连接了
第二次:
客户端 < - - 服务器 此时客户端知道服务器收到连接请求了
第三次:
客户端 - - > 服务器 此时服务器知道客户端收到了自己的回应
到这里, 就可以认为客户端与服务器已经建立了连接.
下面就还是用之前的例子进行解释
下面展示主机中真实的三次握手的详情
为什么不用两次?
不行.如果没有最后一个ACK,此时主机B是无法知道自己的发送能力和对方的接受能力是否正常.
主要是为了防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送的第一个请求连接并且没有丢失,只是因为在网络中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时之前滞留的那一次请求连接,因为网络通畅了, 到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
为什么不用四次?
因为三次已经可以满足需要了, 四次就多余了,四次可以建立连接但是没必要,就是将三次的第二次分成两步就会需要传输两个包,这样效率就会低下。
四次挥手
四次挥手其实跟我们日常的情侣分手一样
四次挥手只有在“延时应答”这种特殊情况的时候才会有合并。
FIN是内核的事情.内核收到FIN就知道对方不会再继续发送数据了.
主机B的应用程序就会继续读取接受缓冲区中积压的数据,如果数据读完了,继续再读,就会触发异常. (异常相当于是从一个已经关闭连接的连接中读取数据,数据读完了才触发的)
丢包分析
4、滑动窗口
没有滑动窗口
传输过程,发一个收到一个,是串行过程,每次传输数据都要对应一个等待时间
传输N份数据就要等待N次应答时间
总的传输时间=N份数据传输时间+N份应答传输时间
带滑动窗口
滑动窗口是发送方的概念.接收方只有一一个接受缓冲区,没有"滑动窗口大小"这样的概念.
发送方的窗口大小太大,是可能导致问题的.但是这个事情不是由连接管理协商出来的而是靠拥塞控制+流量控制确定的
像上述一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能 (其实本质是将多个段的等待时间重叠在一起了)
滑动窗口是批量传输数据
总的传输时间=N份数据传输时间重叠成了1份时间,N份应答传输时间,重叠成了一份时间
每个窗口具体是怎么滑动工作的呢?
窗口只有一个,是一格一格往后滑的,每当数据进入到窗口里面就立刻被发送了
窗口:不等待ACK的情况下,批量发送的最大数据量,就叫“窗口大小"
滑动:窗口范围就是表示当前哪些数据在等待ACK,随着一个ACK到达就立刻发送下一个数据,等待的数据包的范围就在逐渐滑动
滑动窗口下三种丢包情况
1、数据包抵达,ACK丢了
比如上图的窗口大小就是6000,如果主机B一直收不到1001的ACK所以窗口就一直不动等待最后一个数据的ACK,直到最后收到了6001的ACK,窗口就会默认之前的数据都已经收到,然后发送方就会立刻传输6001-12001的数据,这就相当于一下子就往后话滑了好几步。
我们上面展示的是1-6000,就算最后6001这个数据的ACK丢了,那6001后面还有其他的数据可以参考,即使到了最后一个数据发完了,还有一个FIN作为接收响应
2、数据包丢了/主机B接收顺序反了
如果一直发送不出去,就会超时连接,重置连接。
如果数据包丢了就会进行重传,此处重传只是重传丢了的数据,其他数据不需要额外重传这种叫做"快速重传" (搭配滑动窗口下的超时重传)
如果中间两次丢包
滑动窗口是发送方的概念,接受缓冲区就是一一个固定大小的内存空间(空间大小是固定,可以通过内核的参数配置)如果接受缓冲区满了就不会继续传输了(流量控制机制)
如果是主机接收顺序反了,后发先至
拥塞控制和流量控制共同决定发送方的窗口大小的.
5、拥塞控制
拥塞控制是考虑网络传输路径上的拥堵程度,互联网类似一个交通网,可能某个环节就会拥堵。
就算接收方处理能力很强,但是如果传输路径.上拥堵了,此时发送方发的快也没用,接收方的处理能力,是好衡量. (接受缓冲区空余空间大小)但是传输路径太复杂,不好衡量。
拥塞控制由于不好衡量传输路径的拥堵情况只能通过"反复试探"的方式,逐渐试探出应该用多大的窗口大小。
为啥要动态变化?
网络的拥堵情况也是瞬息万变的.要随时根据网络的实际情况进行动态调整.
为啥曲线设定成这样,都是从数学角度求出的一个最优解
6、流量控制
窗口大小不能无限大,如果窗口过大,传输的速率快,那样接收方就可能处理不过来,接收方收到数据后是需要一定的时间处理的。
流量控制就是根据接收方的处理能力(通过接收缓冲区的“剩余空间大小”来决定发送方的速率)来反向制衡发送方的发送速率(窗口大小)
流量控制可以用生产者消费者模型
如果中间有缺漏
流量控制总结
流量控制本质上,是根据接收方的处理能力来制约发送方的发送速率,根据接受缓冲区的剩余空间大小,来制约发送方的滑动窗口大小,通过TCP报头中的"窗口大小字段"来反映给发送方的。流量控制会影响到发送方的窗口大小,但是不是决定因素
可以通过生产者消费者模型理解,发送方是生产者,接收方的应用程序是消费者.
滑动窗口大小就和消费者应用程序的消费速度也是有关系的,如果剩余空间大,说明消费速度就挺快的,发送速率就可以高一些,反之就发送速率低一些就行了.
如果窗口大小为0,发送方会暂停发送,但是会定时发送一个探测报文. 如果发送方有空间了,就会立刻继续传输.
7、延迟应答
是为了提高效率,在流量控制的基础上,尽量返回一个合理但是又比较大的窗口
延时应答其实就是让ACK的发送时间晚一会(不影响可靠性的前提下)延时的时间中就会给应用程序提供更多的消费数据的机会,此时时间到了,再发ACK的时候,得到的窗口大小(接受缓冲区的剩余空间就会更大)
所有的包都可以延迟应答吗?
不是,
数量限制,每隔N个包就会应答一次
时间限制,超过最大延迟时间就应答一次
具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms(这个延时应答的等待时间不能超过超时重传的时间.
)
8、捎带应答
在延迟应答的基础上,为进一步提高效率引入的机制
9、面向字节流的粘包问题
标题是面向字节流的粘包问题 所以只要涉及到字节流都会有可能发生这个问题
首先要明确, 粘包问题中的 “包” , 是指的应用层的数据包.
在TCP的协议头中, 没有如同UDP一样的 “报文长度” 这样的字段, 但是有一个序号这样的字段.
站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中.
站在应用层的角度, 看到的只是一串连续的字节数据.
那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分 是一个完整的应用层数据包
所以粘包,粘的是应用层的数据报 就是导致处理数据的时候 容易读取半个应用层数据报的情况
那么如何避免粘包问题呢?
明确两个包之间的边界
对于定长的包, 保证每次都按固定大小读取即可;
对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置;
对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可)
对于UDP协议来说, 是否也存在 “粘包问题” 呢?
UDP协议是没这个问题的
TCP协议本身不帮你区分应用层数据报. (以字节为单位传输)
UDP协议没这个问题. (以UDP包为单位)
因为站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半个"的情况
10、保活机制
在一些"异常情况"下,TCP对于连接会有特殊的处理。
进程奔溃
这种情况下 TCP连接会正常进行四次挥手端来连接(主要是进程退出 都会自动关闭相关的文件)
主机关机(按流程)
这个按流程就是用户一步一步合理关机
在关机的时候会强制杀死进程 杀进程的时候会进行四次挥手断开连接
主机断电/断网
接收方断电
当接收方断电 发送方发送消息的时候就会出现没有ACK的情况 =>超时重传 => 重传一定的次数 => 重置连接 => 放弃对方然后断开连接
发送方断电
对于接收方来说本来也不知道发送方啥时候发送消息那接收方会一直等下去吗?
肯定不会 其实在TCP中 互相会时不时给对方发送心跳包(一个毫无意义的数据报 只是证明对方还在线 还活着) 所以一旦一段时间都没有收到对方的心跳包 就可以认为对方已经挂了 就会断开连接
心跳包
TCP可靠性的保证机制总结
TCP保证可靠性主要依靠下面7种机制:
1、检验和
TCP检验和的计算与UDP一样,在计算时要加上12byte的伪首部,检验范围包括TCP首部及数据部分,但是UDP的检验和字段为可选的,而TCP中是必须有的。计算方法为:在发送方将整个报文段分为多个16位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全1则正确(UDP中为0是正确),否则存在错误。
2、序列号
TCP将每个字节的数据都进行了编号,这就是序列号。
序列号的作用:
a、保证可靠性(当接收到的数据总少了某个序号的数据时,能马上知道)
b、保证数据的按序到达
c、提高效率,可实现多次发送,一次确认
d、去除重复数据
数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现
3、确认应答机制(ACK)
TCP通过确认应答机制实现可靠的数据传输。在TCP的首部中有一个标志位——ACK,此标志位表示确认号是否有效。接收方对于按序到达的数据会进行确认,当标志位ACK=1时确认首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了。而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制。
4、超时重传机制
当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个闹钟,到点了还没有收到应答则进行重传)
5、连接管理机制
连接管理机制即TCP建立连接时的三次握手和断开连接时的四次挥手。
6、流量控制
接收端处理数据的速度是有限的,如果发送方发送数据的速度过快,导致接收端的缓冲区满,而发送方继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应。
TCP图解
TCP与 UDP的对比
1、TCP适用于处理要求可靠性强的地方 效率自然不是很好
2、UDP适用于处理可靠性要求没那么高 但是要求效率高的地方(机房内部通信(分布式系统)
如果两个主机在同一个机房内部,两个主机间的网络环境就很简单.此时丢包概率就较小考虑UDP)
3、UDP 能实现广播 (一个人可以发给很多人信息),TCP只能一对一传输 (如果TCP想实现广播就需要应用层配合 而且延迟较高)
4、什么场景UDP和TCP都不能胜任?
游戏方面 不仅要求可靠 而且要求效率极高(就好比我打游戏 延迟100ms就很难受了)
这里就需要引进一些其他基于TCP改进的传输层协议 比如 quic enet kcp udt
其实这些协议都是在想办法扩大窗户口 减少ACK的应答时间 缩短超时重传的时间 提高拥塞控制的慢启动 等等
如何基于UDP实现可靠传输
UDP本身是改变不了的(因为是内核实现的) 也就是说只能在应用层实现UDP的可靠性
主要俩点 一是确认应答,二是超时重传 (设置序列号和确认序号 设定接收缓冲区和发送缓冲区) 连接管理不是必须要
但是加上这俩个必然会降低效率 那么就必须引进滑动窗口 万一滑动太快就需要流量控制和拥塞控制 如果进一步提高效率的话就需要延迟应答和捎带应答其实本质就是在应用层实现一些TCP的特点
转载:https://blog.csdn.net/char_m/article/details/107172269