?前言
TCP是一种可靠的协议,提供了多种策略来确保数据的可靠性传输。
可靠并不是保证每次发送的数据,对方都一定收到;而是尽最大可能让数据送达目的主机,即使丢包也可以知道丢包。
目录
在上篇博客说到,TCP将每个字节的数据都进行了编号,即为序列号。
序列号,既是TCP面向字节流的体现,也对发送的数据进行了排序,即编号小的,一定是先发送的。接收方在收到TCP报文后,根据报头中的序列号,将TCP报文按照序号顺序放入接收缓冲区
TCP协议规定,接收方接收数据,必须发送确认报文。这就是确认应答
序列号和确认号就是TCP缓冲区的编号(下标)
确认应答是一种特殊的报文(ACK),所谓应答报文,本质是ACK标志位字段为1的报文,此时“确认序号”字段才生效
初始序号是随机的,为了防止网络攻击;如果发送多个数据,每个数据都会带着一个序号。
和确认应答搭配使用的是捎带应答,即一方返回确认应答ACK报文时,也可以携带要发送给对方的数据。
比如客户端给服务器发送“How are you?”,接收方收到数据后,发送ACK确认报文,同时可以在报文中附上有效载荷(数据):“Fine,thank you”。
确认应答不仅让发送方知晓哪些报文收到了,还可以提高双方通信的效率
上图线性的通信效率较低,确认应答可以让双方一次性发送多个报文
发送方一次发送多个报文,接收方一次性接收并发送多个确认应答
即使中间的确认应答丢失:比如2001的确认应答丢失,但发送方只要接收到4001的确认应答,就代表4001前的报文,接收方都收到了;若1001~2000的报文丢失,那么接收方即使收到了后面的额2001~3000的报文,也只会给发送方返回1001的确认应答,如此发送方也能知晓1001~2000的报文丢失,进而使用接下来的策略——超时重传
确认应答是比较理想的情况,数据在网络传输中,是可能会丢包的。
报文如果成功送达接收方,接收方会发送确认报文,发送方就知道报文成功送达。但也可能出现如下两种情况:
这两种情况,发送方都不会收到接收方的确认报文。
TCP规定,在报文发送后的一个时间间隔后,如果还没收到确认报文,发送方一律认为报文丢失,重新发送该报文,这就是超时重传。
也有可能报文或确认报文因为网络拥塞而延迟送达对方主机,此时发送方也会超时重传,因此主机B会收到很多重复数据,那么TCP协议需要能够识别出哪些报文是重复的,并把重复的报文丢弃——根据报文的序列号就可以知道哪些报文是重复的
超时时间如何确定呢?
TCP为了保证在任何环境下都能有比较高性能的通信,因此会动态计算这个最大超时时间
上述我们了解到,发送方可以一次性发送多个报文,那么报文数量是如何确定的呢?
在TCP报文中,有一个16位窗口大小,该字段反应了发送该报文的主机进程的接收缓冲区剩余大小,即接收能力。
在通信双方建立连接,进行三次握手时,就会通报对方自己的窗口大小——即接收能力,双方基于每次通信报文中的窗口大小,动态的调整自己将要发送给对方的报文数量。这就是滑动窗口的原理
在发送缓冲区中,有编号,即序列号的存在。滑动窗口是根据对端发送的窗口大小,将发送缓冲区划分成三部分。
?滑动窗口就是可发送,未收到应答的部分。
代码实现上
操作系统可以维护两个变量——win_start和win_end,表示滑动窗口开始的序号和结束的序号
在收到一个确认报文后,确认报文中的seq确认序号,指示发送方下一次发送的序号,窗口大小(win)指示滑动窗口的大小(目前)
问题1:滑动窗口只能向右滑动吗?向左可以吗?
回答:只能向右。因为滑动窗口将发送缓冲区划分为三个部分,左边部分是已经发送的数据且受到应答,不应该再发送。
问题2:滑动窗口能变大吗?能变小吗?能变0吗,表示什么意思?
回答:滑动窗口的大小取决于对方通告的win大小,即窗口大小(目前),当然可以变大或变小。变0表示对方暂时不能接受数据,可能是接收缓冲区已满
问题3:滑动窗口能一直向右滑动吗?不会越界?
回答:发送缓冲区被设计成环形队列,所以可以一直向右滑动,滑动窗口左边的部分,也会在后续被新的数据覆盖
问题4:出现丢包问题,如何进行重传?
这里分两种情况讨论:
情况一:数据包到达对方数据,ACK确认报文丢失
此时,部分ACK丢失不要紧,因为可以通过后续的ACK进行确认。
即使ACK=2001的确认报文丢失,但发送方收到ACK=5001的确认报文,这就代表接收方5001前的报文都已收到
情况二:数据包丢失
?
这种机制被称为“高速重发机制”,也叫快重传
如果接收数据的主机立刻返回ACK,此时返回的窗口可能比较小
窗口越大,网络吞吐量越大,传输效率越高。
延迟应答一般有如下限制:
具体数量和超时时间,不同操作系统各有不同,一般N取2,超时时间为200ms
接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,此时如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应
因此TCP支持根据接收端的接受能力,来决定发送端的发送速度,这个机制就叫做“流量控制(Flow Control)”
窗口字段为16位,最大表示65535字节,但其实窗口大小还可以更大
在TCP首部的选项字段,有一个窗口扩大因子M,实际窗口大小是,窗口字段的值左移M位,即2^(16+M)?字节
详细可参看:【计算机网络】TCP协议报头详解
滑动窗口既体现了TCP的可靠性,又提高了性能。但网络通信还需要考虑网络拥塞情况,所以TCP还需要拥塞控制。
网络中有很多主机,如果当前网络状态比较拥堵,在不清楚当前网络状态下,盲目根据接收方窗口大小发送数据,可能导致网络更为拥堵。
TCP引入慢启动机制——先发送少量数据,探测网络信道状态,了解当前网络拥堵情况,再决定按照多大的速度传输数据
此处引入一个概念——拥塞窗口,其大小为swnd
慢开始的思路是
少量的丢包,触发超时重传;大量的丢包,认为是网络拥塞。当TCP通信开始后,网络吞吐量会逐渐上升,随着网络发生拥堵,吞吐量会立刻下降
注意:发送方的发送窗口,会根据接收方反馈的接收窗口和自身维护的拥塞窗口,取较小值作为实际发送的窗口大小
面向字节流
创建一个TCP的socket,同时在内核中创建一个发送缓冲区和接收缓冲区
由于缓冲区的存在,TCP程序的读和写不需要一一匹配。
粘包问题
粘包问题中的"包" ,指的是应用层的数据包。在TCP的协议头中,没有如同UDP一样的"报文长度"这样的字段,但是有一个序号这样的字段。
不同数据粘连,就是粘包问题
明确两个包之间的边界,是避免粘包问题的根本:
注意:对于UDP协议,不存在粘包问题,因为UDP报头中有总长度,并且UDP是面向数据报的,有明确的数据边界。站在应用层的角度,使用UDP时,要么收到完整的UDP报文,要么不收,不会出现“半个报文”的情况
TCP协议可靠性机制有:
提高性能:
其他:
定时器(超时重传定时器,保活定时器(连接),TIME_WAIT定时器等)
保活计时器和TIME_WAIT计时器可参看:【计算机网络】连接管理(三次握手,四次挥手)
基于TCP的应用层协议:HTTP,HTTPS,SSH,Telnet,FTP,SMTP。
本篇博客到此结束,感谢看到此处。
欢迎大家纠错和补充
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。