上篇文章,我为大家介绍和演示了关于 UDP 和 TCP 两个协议的网络编程,两个协议的网络编程还是有一定的区别,我个人感觉 TCP 的网络编程会比 UDP 的复杂不少,也更需要我们去理解,并且熟练地掌握。
这篇文章,我将为大家介绍关于 TCP 协议中的十大核心机制。
TCP 的特点:有连接,可靠传输,面向字节流,全双工
1. TCP 报文段首部格式解析
2. 十大核心机制
1.)确认应答
2.)超时重传
3.)连接管理
4.)滑动窗口
5.)流量控制
6.)拥塞控制
7.)延时应答
8.)捎带应答
9.)面向字节流
10.)异常情况
TCP 报文段首部格式解析
在讲解十个核心机制之前,我们先对这张表进行一个大概的了解
16位源端口号:发送方应用程序的端口号,告诉接收方我是谁????
16位目的端口号:接收方应用程序的端口号,告诉网络要把数据传输给谁????
32位序列号:数据包的编号,TCP将一个大的数据拆分成很多个小块进行发送,这个序列号
给数据块排队使用,接收方收到乱序的包后,可以根据这个序号把它们重新拼
成正确的顺序。
32位确认序列号:期望下一个收到字节的序列号,这是接收方给发送方的反馈,意思是“在这
个序号之前的数据我都收到了,请发下一个”。它是TCP可靠传输的核心。
4位首部长度:表示TCP头部有多长,告诉接收方数据从哪里开始读取。图中右侧标注的
“20 字节”指的是标准头部的最小长度。
保留(6位):暂时不需要设置,设置为0。这个是吸取了UDP的教训
URG (Urgent):紧急指针有效,表示包里有紧急数据,需要优先处理。
ACK (Acknowledgment):确认序号有效。连接建立后,所有传送的报文段都必须把ACK置为1。
PSH (Push):提示接收方尽快把数据交给应用程序,不要在缓存里赖着。
RST (Reset):连接出错,要求复位(重置)连接。
SYN (Synchronize):非常重要。用于发起一个新连接。我们在“三次握手”建立连接时主要看这个标志。
FIN (Finish):结束连接。表示“我说完了,我要挂电话了”。
16位窗口大小:接收方告诉发送方,我现在还可以接收多少数据,流量控制。如果接收方处
理不过来,窗口大小变小,发送方就会减慢发送速度,防止把接收方“撑死”。
16位校验和:用于校验数据在传输的过程中是否损坏,接收方收到数据后会算一遍,如果算
出来的结果和这个值不一样,说明数据出错了(比如路上被干扰了),这个包
就会被丢弃。
16位紧急指针:只有当 URG 标志为 1 时才有效。它告诉系统紧急数据在哪里结束???
十大核心机制
1.)确认应答(TCP可靠传输的核心机制)
但是在网络传输的过程中,可能会出现 “先发后至” 的情况,比如网络卡断了一下,发送方发送了俩条信息,我这边可能先收到发送方最后发来的那一条信息,然后才收到发送方最早发送过来的信息,这样信息的含义就可能会出现错误~~~
此时TCP报文段首部格式中有32位序列号和32位确认序列号
32位序列号:针对传输的数据进行编号
32位确认序列号:给ACK报文使用,关联当前这个ACK是应答哪个数据的
32为确认序列号有俩种理解方式:
1.从1001之前的数据,我已经接收到了
2.对方正在想你索要1001之后的数据
应答报文时一种特殊的报文,通常没有载荷,并且在报头的标志位中,将ACK设置为1
总结一下什么叫做可靠性???
什么是可靠性 --> 发送出去的信息,发送方能知道接收方是否接收到了
靠什么做到 ---> 确认应答
如何实现 ---> 发送方发送数据给接收方,接收方接收到数据并且返回一个应答报文
可能出现的问题 ---> 先发后至
如何解决先发后至问题 ---> 通过序列号和确认序列号解决
2.)超时重传
在网络传输的过程中,可能会出现丢包的概率情况,是无法避免的
当发送方发送一个数据,但迟迟没有接收到接收方返回的ACK,这时发送方可能会意识到可能是数据丢了,所以会选择在发送一次数据
但是如果是ACK丢包了,那么也会导致发送方发送俩分同样的数据过来,这时,如果这俩份数据是扣款数据,难道会扣款俩次吗????不会的,接收方知道我已经接收到的数据的序列号范围,如果发现新收到的数据已经在序列号的范围内,说明这个数据已经接受过了,就会将重复的数据丢弃
所以TCP不仅仅解决了可靠传输问题,还解决了数据的传输顺序问题,数据重复传输的问题
但是超时重传的重传也并不是无限次数的重传,如果连续传输多次都没有达到对方,说明可能出现了非常严重的网络故障问题,从而放弃对TCP的连接(直接将对方保留的信息删除)
因为网络故障,如果一直重传也没有任何意义,没必要消耗大量的资源和时间~~~
3.)连接管理
连接管理分为俩部分:
1.建立连接(三次握手)
2.断开连接(四次挥手)
这个核心机制我会单独出一篇文章讲讲滴~~~~
4.)滑动窗口(提高效率)
引入滑动窗口是为了提高效率,因为TCP为了确保可靠连接,肯定多多少少会损失一定的效率
滑动窗口的本质其实就是:批量发送,批量等待ACK
窗口大小越大,批量传输的数据越多,整体的传输效率越高~~~
当滑动窗口 “丢包” 该如何处理呢???
ACK丢失的情况就不需要做任何的处理!!因为ACK的确认序号后,后一个ACK可以包含前一个
但是如果是数据丢失,那么服务器就会一直向客户端索要丢失的数据,直到客户端反应过来,然后重新发送丢失的数据,服务器才停止索要
要注意:服务器是会记录哪些数据已经接收到了,哪些数据是丢失的,然后就会一直向客户端索要丢失了的那段数据!!!
这个机制称之为:快速重传
5.)流量控制(保证可靠性传输)
当滑动窗口传输的数据越大,传输的效率也就越大,如果窗口特别的大,是否可以呢?
当然不行,如果传输的特别快,接收方可能就处理不过来,就可能照成接收方丢包的情况
流量控制就是针对滑动窗口的大小进行控制的~~~~
流量控制机制:
流量控制是根据接收方的处理能力,来反向限制发送方的发送速度(窗口大小)
1.如何衡量接收方的处理能力?
根据缓冲区剩余空间的大小,如果缓冲区剩余空间还有很多,那么发送方就可以将窗口大小调大;反之,就要将窗口大小调小
2.衡量之后,如何通知发送方对窗口大小做出限制呢?
把接收方缓冲区剩余空间大小的值,通过ACK通知发送给发送方
16位窗口大小,就是填写缓冲区剩余空间大小的值,发送方按照这个值,确定下一轮窗口大小的值该多大
如果缓冲区已经满了,那么发送方就要停止发送!!!
但是如果暂停发送,那么接收方就不会发送ACK了吗?那发送方要如何直到什么时候可以继续发送数据了呢??? ------- 发送方会时不时的发送一个窗口探测,询问接收方我可以发送数据了吗?接收方会根据缓冲区剩余空间大小,给发送方返回一个ACK,告诉接收方我缓冲区剩余空间还有多少,如果为0,那么就继续暂停发送,如果为2000,那就可以发送俩段数据过来~~~~
窗口探测没有载荷,只是为了触发ACK,为了得到新的窗口大小的值
6.)拥塞控制(提高效率)
拥塞控制和流量控制类似,都是针对滑动窗口的大小进行限制
流量控制是针对接收方缓冲区剩余空间大小做出限制
拥塞控制是针对通信路径的处理能力做出限制
因为通信路径中间的状况是非常复杂的,数据都是一点点进行尝试(相当于我们去当一个陌生的城市,城市之间道路非常复杂,我们想去一个地方,只能一点点的尝试,不断询问路人~~~)
拥塞控制的机制:
刚开始发送数据的时候,按照一个比较小的速度发送数据(比较小窗口)
如果数据可以送达并且没有丢包情况,那么就加大速度(加大窗口大小)
如果数据有丢包情况,那么就减小速度(减小窗口大小)
所以窗口的大下并不是一个固定值,而是动态变化的
窗口大小最终取决于:流量控制和拥塞控制的较小值~~~~~
拥塞控制变化规律图:
初始情况下是一个以非常小的窗口启动(慢启动)
在不丢包的情况下,指数增长,每个轮次都会时窗口大小翻倍增长
当窗口大小增长到了阈值,就会从指数增长更改为线性增长(如果增长的太快,下一个轮次可能会直接丢包)
在线性增长的过程中,如果发生丢包情况,就立即缩小窗口大小
缩小窗口大小有俩种方式:
1.)老方式:回到 “慢开始” 状态,然后重新指数增长,然后线性增长
2.)新方式:回到阈值位置,然后线性增长(阈值 = 丢包时窗口大小 / 2 )
7.)延时应答(提升传输效率)
服务器并没有立即返回客户端发送的1-1000的ACK,而是等到下一轮数据,才给客户端返回2001的ACK,此处的延时应答,就可以给应用程序留更多的处理时间,返回的ACK数目就可以减少
在延时发送的这个时间段内,应用程序处理的数据越多,接下来发送的数据就越快~~~
8.)捎带应答(配合延时应答)
当客户端发送一个请求,服务器在根据请求计算响应需要一定的时间,如果计算的这段时间,刚好遇上了延时应答,就会顺便将ACK和响打包成一个数据包,然后一起发送过去
就好比四次挥手,也可以是三次挥手,如果刚好遇到延时应答,就顺便将ACK和响应打包成一个数据包一并发送过去,就变成了三次挥手~~~~
9.)面向字节流
在面向字节流的过程中,可能会出现粘包的现象的问题
在传输过程中,每个数据包都是单独分开的,但是当数据发送到缓冲区的时候,就会出现粘包情况
当数据在缓冲区的时候:
此处在应用层上,根本区分不出从哪到哪是一个完整的应用层数据包
那么该如何解决粘包问题呢?
1.)引入分隔符,用特殊的符号作为包的开头和结束(例如使用\n)
2.)在数据开头的地方,添加一个固定的属性,说明数据包的长度
10.)异常情况
1.)进程崩溃
和四次挥手是完全相同的,通过socket.close触发~~~
即使进程崩溃,客户端没了,但是TCP的连接还在操作系统内核中,后续还是可以处理挥手的情况
操作系统会自动对文件资源进行释放,清理PCB的文件描述符表
2.)主机关机(正常关机)
正常的关机需要一定的时间,在这段时间里,足够让客户端和服务器进行四次挥手的过程~~~
3.)主机关机(掉电)
在这种特殊的情况下,程序根本来不及进行四次挥手的操作
1.)如果是接收方掉电了,就不会给发送方返回ACK,那么发送方就会超时重传,然后通过几次超时重传,发送方还是接收不到接收方的ACK,就会主动放弃链接
2.)如果是发送方掉电了,接收方并不知道发送方咋回事,接收方只能定期给发送方发送一个 “心跳包”,这个心跳包不携带业务,只是为了触发一次ACK,如果发送方能返回ACK,那么接收方就继续等待;如果接收方发了心跳包,没有接收到发送方返回的ACK,此时接收方就主动断开连接~
4.)网线断开
网线断开就是将主动关机的俩个机制结合起来
接收方:周期性发送心跳包,这个心跳包不携带业务,只为了触发一次ACK,如果发送方
没有给接收方返回ACK,接收方就会主动断开连接~~~~
发送方:给接收方返回响应数据,如果接收方迟迟没有返回ACK,就会触发超时重传
在几次重传过程中,还是没有接收到接收方的ACK,那么发送方就会主动放弃连接~~~