可靠性:
之前我们在UDP中谈到了,UDP不可靠但是简单,TCP可靠但是也要做更多的工作,那这些工作具体是什么呢?接下来让我们详细了解一下。
确认应答机制(ACK机制)
序号:我们可以把TCP的发送缓冲去看作一个char sendbuffer[NUM] 的数组
每一个ACK都带有确认信号
超时重传机制
第二种情况收到了重复报文,可以通过序号来甄别。
超时重传的时间限制该如何设定?
时间短 :会收到大量重复报文;时间长:client与server太用心效率低;可是短和长本来就不能固定的确定,所以超时重传的时间也不能是固定的,要根据当时的网络状况等因素来改变。
快重传VS 超时重传
快重传是一种网络传输协议中的算法,用于处理丢失的报文段。根据快重传算法的规定,当发送方连续收到三个重复确认时,即表示某个报文段可能丢失了,发送方会立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器到期。这样可以减少网络传输的延迟和拥塞问题的发生。
在一些实现中,快重传还会将拥塞窗口的值扩大一些,即等于ssthresh的三倍MSS。这是因为当发送方连续收到三个重复确认时,意味着有三个分组已经离开了网络,不再占用网络资源,而是停留在接收方的缓存中。因此,适当扩大拥塞窗口可以提高网络的传输效率。
快重传和超时重传相互合作,解决端到端的问题。
网络拥塞问题--拥塞控制
少量丢包时候,可以通过重传来解决,当发生大量丢包时候,就是网络的问题了,zheg=zhong故障叫做网络拥塞。 网络拥塞时候不能再进行重传了。
TCP的拥塞控制算法 --慢启动机制。
慢启动是指在TCP协议中,当一个新的连接建立时,发送方会以较小的窗口大小开始传输数据,并随着时间的推移逐渐增加窗口大小来达到最大的传输速率。 慢启动的目的是为了在发送方和接收方之间建立起一个可靠的连接并适应网络的拥塞情况。
慢启动算法的原理是,发送方在开始传输数据时并不知道网络的实际情况,因此需要通过以较小的速度增加发送窗口的方式来试探网络的拥塞程度,并避免因发送过多数据而造成网络拥塞。
在慢启动阶段,发送方会不断增加发送窗口的大小,以加快数据的传输速度。当发送方收到确认消息时,窗口大小会翻倍,直到达到一个阈值为止。一旦窗口大小达到阈值,发送方就进入拥塞避免阶段,此时窗口大小会以较慢的速度增加。如果发生数据包丢失的情况,发送方会通过快速重传和快速恢复的机制来快速重新发送丢失的数据。
总之,慢启动是TCP协议中一种用于逐渐增加发送窗口大小以达到最大传输速率的算法,它能够有效地管理网络拥塞并提高传输性能
流量控制
在进行流量控制时,我们是如何得知接收方的接受能力的?
三次握手过程中,TCP报头中有窗口大小前两次是不能携带报文数据的,但是可以携带缓冲区的大小(自己的),所以我们通过交换报文就可以得知对方的 接受能力 ,通过流量控制可以减少数据重传次数,提高TCP数据传输效率。
首先,我们来解决一个问题,帮助我们更好的理解流量控制
TCP是如何做到全双工的?
TCP是有接收缓冲区和发送缓冲区的。
因为TCP有发送和接收缓冲区,所以clent和server就有了两对接收和发送缓冲区==》TCP全双工。
client发送速度不能太快,也不能太慢,--》引入流量控制。
什么决定server的接收能力?-------- 接收缓冲区剩余空间大小。
滑动窗口 ---提高效率
滑动窗口机制允许向对方发送多个数据,但暂时都不需要确认,就可以立刻发送下一个数据。
滑动窗口的本质:
发送方可以一次性向对方发送推送数据的上限,滑动窗口必须有上限,这个上限由min(拥塞窗口,对方的接收能力)决定。滑动窗口的本质属性是指针或者下标。
它既想给对方推送数据,又想对方来得及接收。
滑动窗口的t模型
滑动窗口必须向右移动吗?
不一定
滑动窗口可以为0吗?
可以
如果没有收到开始报文的应答,而是收到中间的?
可能有这种情况,可以通过序号和确认序号解决。win_start=收到的应答报文中的确认序号。win_end =win_start+min(拥塞窗口,对方接受能力)
滑动窗口会不会越界?
不会,我们图上虽然是用一维数组模拟的,实际上内核中是通过一个环状结构,通过取模运算来管理滑动窗口的。
延迟应答
如果接收数据的主机收到数据后立即进行ACK应答,此时返回的窗口可能比较小。
假设对方接收端缓冲区剩余空间大小为1M,对方一次收到500K的数据后,如果立即进行ACK 应答,此时返回的窗口就是500K。但实际接收端处理数据的速度很快,10ms之内就将接收缓冲区中500K的数据消费掉了。在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来。如果接收端稍微等一会再进行ACK应答,比如等待200ms再应答,那么这时返回的窗口大小就是1M。
需要注意的是,
1)延迟应答的目的不是为了保证可靠性,而是留出一点时间让接收缓冲区中的数据尽可能被上层应用层消费掉,此时在进行ACK响应的时候报告的窗口大小就可以更大,从而增大网络吞吐量,进而提高数据的传输效率。
2)此外,不是所有的数据包都可以延迟应答。
数量限制:每个N个包就应答一次。
时间限制:超过最大延迟时间就应答一次(这个时间不会导致误超时重传)。
延迟应答具体的数量和超时时间,依操作系统不同也有差异,一般N取2,超时时间取200ms。
捎带应答
捎带应答其实是TCP通信时最常规的一种方式,就好比主机A给主机B发送了一条消息,当主机B收到这条消息后需要对其进行ACK应答,但如果主机B此时正好也要给主机A发生消息,此时这个ACK就可以搭顺风车,而不用单独发送一个ACK应答,此时主机B发送的这个报文既发送了数据,又完成了对收到数据的响应,这就叫做捎带应答。
捎带应答最直观的角度实际也是发送数据的效率,此时双方通信时就可以不用再发送单纯的确认报文了。
此外,由于捎带应答的报文携带了有效数据,因此对方收到该报文后会对其进行响应,当收到这个响应报文时不仅能够确保发送的数据被对方可靠的收到了,同时也能确保捎带的ACK应答也被对方可靠的收到了。
面向字节流
当创建一个TCP的socket时,同时在内核中会创建一个发送缓冲区和一个接收缓冲区。
调用write函数就可以将数据写入发送缓冲区中,此时write函数就可以进行返回了,接下来发送缓冲区当中的数据就是由TCP自行进行发送的。
如果发送的字节数太长,TCP会将其拆分成多个数据包发出。如果发送的字节数太短,TCP可能会先将其留在发送缓冲区当中,等到合适的时机再进行发送。
接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区,可以通过调用read函数来读取接收缓冲区当中的数据。
而调用read函数读取接收缓冲区中的数据时,也可以按任意字节数进行读取。
由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:
写100个字节数据时,可以调用一次write写100字节,也可以调用100次write,每次写一个字节。
读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read100个字节,也可以一次read一个字节,重复100次。
实际对于TCP来说,它并不关心发送缓冲区当中的是什么数据,在TCP看来这些只是一个个的字节数据,它的任务就是将这些数据准确无误的发送到对方的接收缓冲区当中就行了,而至于如何解释这些数据完全由上层应用来决定,这就叫做面向字节流。
粘包问题
什么是粘包?
首先要明确,粘包问题中的“包”,是指的应用层的数据包。
在TCP的协议头中,没有如同UDP一样的“报文长度”这样的字段。
站在传输层的角度,TCP是一个一个报文过来的,按照序号排好序放在缓冲区中。但站在应用层的角度,看到的只是一串连续的字节数据。那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包。
如何解决粘包问题
要解决粘包问题,本质就是要明确报文和报文之间的边界。
对于定长的包,保证每次都按固定大小读取即可。
对于变长的包,可以在报头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。比如HTTP报头当中就包含Content-Length属性,表示正文的长度。
对于变长的包,还可以在包和包之间使用明确的分隔符。因为应用层协议是程序员自己来定的,只要保证分隔符不和正文冲突即可。
TCP异常情况
进程终止
当客户端正常访问服务器时,如果客户端进程突然崩溃了,此时建立好的连接会怎么样?
当一个进程退出时,该进程曾经打开的文件描述符都会自动关闭,因此当客户端进程退
时,相当于自动调用了close函数关闭了对应的文件描述符,此时双方操作系统在底层会正
常完成四次挥手,然后释放对应的连接资源。也就是说,进程终止时会释放文件描述符
TCP底层仍然可以发送FIN,和进程正常退出没有区别。
机器重启
当客户端正常访问服务器时,如果将客户端主机重启,此时建立好的连接会怎么样?
当我们选择重启主机时,操作系统会先杀掉所有进程然后再进行关机重启,因此机器重启和进程终止的情况是一样的,此时双方操作系统也会正常完成四次挥手,然后释放对应的连接资源。
机器掉电/网线断开
当客户端正常访问服务器时,如果将客户端突然掉线了,此时建立好的连接会怎么样?
当客户端掉线后,服务器端在短时间内无法知道客户端掉线了,因此在服务器端会维持与客户端建立的连接,但这个连接也不会一直维持,因为TCP是有保活策略的。
服务器会定期客户端客户端的存在状况,检查对方是否在线,如果连续多次都没有收到ACK应答,此时服务器就会关闭这条连接。
此外,客户端也可能会定期向服务器“报平安”,如果服务器长时间没有收到客户端的消息,此时服务器也会将对应的连接关闭。
其中服务器定期询问客户端的存在状态的做法,叫做基于保活定时器的一种心跳机制,是由TCP实现的。此外,应用层的某些协议,也有一些类似的检测机制,例如基于长连接的HTTP,也会定期检测对方的存在状态。
TCP小结
TCP协议这么复杂就是因为TCP既要保证可靠性,同时又尽可能的提高性能。
可靠性:
检验和;序列号;确认应答;超时重传;连接管理;流量控制;拥塞控制。
提高性能:
滑动窗口;快速重传;延迟应答;捎带应答。
需要注意的是,TCP的这些机制有些能够通过TCP报头体现出来的,但还有一些是通过代码逻辑体现出来的。