传输层协议:UDP和TCP
1.传输层概念
传输层主要负责两台主机之间的数据传输,使数据从发送端到接收端。
端口号
端口号标识了一个主机上进行通信的不同的应用程序。
传输层接收到数据后,是要给到具体的应用层的进程。所以发送端的传输层封装报文时,就要添加上将来会发送到的对端的进程,接收端会在传输层提取出端口号,就可以指定到要发生的进程了。
五元组
在TCP/IP协议中,把源IP,源端口号,目的IP,目的端口号,协议号,这样一个五元组来标识一个通信。
上图客服端A和B都访问了服务器,A和B的IP地址是不一样的,且客服端A打开了两个浏览器页面,也就是两个进程,两个进程的端口号是不一样的,就可以使用五元组来确保一次通信。
服务器接收到一个五元组,先提取出目的IP和端口号,确定是发送给本服务器,接着提取出协议号来对应给出数据处理,并提取出源IP和源端口号,作为响应数据的目的IP和端口号,数据处理好后就把数据发送回去。
端口号划分
0-1023:知名的端口号,HTTP,FTP,SSH等使用多的应用层协议,它们的端口号都是固定的
1024-65535:操作系统动态分配的端口号,客服端程序的端口号,就是由操作系统从这个范围随机分配的。
2.UDP协议
16位源端口号:数据从来自哪一个进程。
16位目的端口:数据要发送给对端的进程是谁
16位UDP长度:表示整个数据报(UDP首部+UDP数据)的长度
16位UDP检验和:如果UDP检验和出错,就会把报文丢弃
在使用网络编程接口时,端口号类型都是uint16_t,就是因为协议规定的。
UDP报头固定是8个字节大小,收到一个报文时,提取出前八个字节,剩下的就是有效载荷。
在根据报头的16位目的端口的字段,把提取出来的字段交给指定进程。
源代码中的UDP协议
struct udp_hdr
{
uint16_t src_port;// 源端口
uint16_t dsc_port;// 目的端口
uint16_t length;// UDP长度
uint16_t check;// 校验和
};
无状态就是无法记忆前一次访问网页
facvion是访问图标
补充
在传输过程中之所以使用变成字节流序列而不是结构体,是因为保证通用性,主机使用的语言不同,使用结构体会收到限制,而字节流序列保证了每一台主机都可以解析这个序列得到信息。
UDP 是一种无连接的协议,它的主要目的是提供一种快速、简单的方式发送数据。它的头部只有 8 个字节,相对于 TCP 来说非常轻量级。UDP 不进行三次握手等复杂操作,数据发送时延迟较低,适合对实时性要求高的应用场景,如实时音频 / 视频流、在线游戏等。如果引入缓冲区和复杂的可靠性机制,会增加协议的复杂性和数据传输的延迟,这与 UDP 的设计目标相悖。
例如,在实时音频通话中,如果数据因为缓冲区和复杂的重传机制而延迟到达,就会导致通话卡顿,影响用户体验。即使丢失少量数据,通过一些前向纠错(FEC)等技术可以在一定程度上弥补,但延迟往往比丢失少量数据的影响更大。
关于报文理解
报文从网卡中接收到,会触发外部引脚触发中断使OS处理报文,而时钟中断是调度进程使应用层来处理报文,网卡接收到数据往上传输,而客服端是很多的,就会有多个报文传输过来,在链路层就会有多个报文,而对于多分报文就需要进行管理,以链表结构进行管理,结构体名字是sk_buff,成员有head指向头空间,data指向应用层数据(有协议头则指向头位置),tail指向应用层数据尾部等,而在应用层开始往下走,结构体的data指针就会移动,添加此协议层的报头,指针的移动就可以控制空间的开辟于缩减,完成了插入于删除的操作,,实现了添加报头域删减报头的方法。
3.TCP协议
这里的4位首部长度表示的是报文大小和选项的大小之和,4为最多15最少0,这里的一个表示的四个字节,也就是4位最大是60个字节,最小0,但是因为报文最小要求的是20个字节,所以4位首部长度最小是5最大15
在tcp中,客服端向服务端发送消息,服务端需要发送应答回去,而此时客服端不需要再发送接收应答回去了,因为发过去服务端又需要发送回来,就死循环了,所以消息需要应答,而接收后返回的应答则不需要回复,而这里的通信于应答都是一个报头。在传输过程中还会出现一个问题,多个消息同时发送,且是有顺序的发送,而接收方是不一定有序接收的,接收方可能接收的是乱序的,所以就需要32位序号来保证有序,根据序号以小到大顺序排,这样就可以使接收的数据有序,而应答方的序号就是发送过去报文的序号+1,这样做是因为服务端也要发送自己的信息过去,顺带了应答,也就是捎带应答,就需要有自己的序号,所以就把发送的序号+1作为自己的序号,这就是为什么有两个序号而不是一个序号,因为服务端也是发了数据过去只不过带了应答。
如果接收方收到的数据不是按顺序的,它会将这些数据暂存在接收缓冲区中,直到缺少的序号的数据到达。例如,如果接收方收到了序号为 501 - 1000 的数据,但还没有收到序号为 1 - 500 的数据,它会将 501 - 1000 的数据暂存,直到收到 1 - 500 的数据后,再将所有数据按顺序组装。
16位窗口
16位窗口大小表示的发送方自己的接收缓冲区大小,就可以告诉对端自己的还能接收多少,一般是在三次连接中确定了对方的接收大小,根据接收能力来控制发送速度,来提高或者减缓以达到合适区间,这种操作也叫流量控制。
6个标志位
标志位本质就是报头里的bit位,之所有有这个标志位是用来表示这个报文的具体的类型是什么,以做出对应的操作,比如三次连接时就是向发送报文标志位位SYN请求建立连接,SYN是同步标志位,一表示标志位有效。
URG:紧急指针有效
带URG标志位的报文不是正常数据,是带文数据,函数resv的参数flag标志位为MSG_OBB就是表示紧急处理,16位紧急指针类似于数组下标,数组下标也是指针,这里就是一个偏移量,紧急数据只有一个字节方便插队,偏移量的作用是定位紧急数据在报文段中的具体位置,帮助接收方快速找到紧急数据的起始点。紧急指针的值是16位的值,指出了紧急数据的最后一个字节的序列号,然后紧急数据的大小是可以知道的,紧急指针值减去紧急数据的大小就是偏移量了。
应用场景 :当主机需要发送紧急数据(如远程终端操作中的中断命令等),使得接收端能够尽快处理这些紧急数据。例如,在网络管理软件中,管理员可能需要远程紧急停止某个服务。发送方通过设置 URG 标识位并携带紧急指针,将紧急停止命令的指令数据快速发送给接收方,接收方收到后会优先处理这部分紧急数据,而不是像普通数据一样按照先来先到的顺序排队等待处理。
假设客户端的初始序列号
X
是1000:
第一次握手:客户端发送SYN报文,序列号为1000。SYN标志位占一个序列号,所以下一个数据字节的序列号变为1001。
第二次握手:服务器发送SYN-ACK报文,序列号为2000(服务器的初始序列号),确认号为1001。
第三次握手:客户端发送ACK报文,序列号为1001,确认号为2001。
现在,客户端要发送紧急数据:
客户端设置URG标志位,并在紧急指针字段中填写值为1005。
这意味着紧急数据的最后一个字节的序列号是1005。
当前序列号是1001(因为第三次握手后的序列号是1001),所以紧急数据的大小是
1005 - 1001 = 4
字节。
ACK:确认号有效
应用场景 :用于确认收到对方发送的数据。在 TCP 通信中,接收方收到发送方的数据后,会根据接收到的数据序列号等信息生成一个确认号,通过设置 ACK 标识位为 1 并携带确认号,告知发送方哪些数据已经成功接收。例如,在一个文件传输过程中,接收方每收到一段数据,就发送一个带有 ACK 标识的报文段给发送方,告诉发送方这部分数据已经收到,发送方可以根据确认号继续发送后续的数据,从而保证数据的可靠传输。
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
应用场景 :用于实时性要求较高的数据传输场景。例如,在在线聊天软件中,当用户发送一条消息时,发送方将消息封装在带有 PSH 标识的 TCP 报文中发送。接收方收到后,会立即将消息从 TCP 缓冲区读取并交给聊天软件的应用程序进行处理,这样可以避免消息在接收方缓冲区堆积,确保消息能够及时地显示在接收用户的界面上。
RST:对方要求重新建立连接,携带RST标识叫复位报文段
应用场景 :当服务器检测到某个 TCP 连接异常,如长时间没有数据传输,或者连接的参数出现错误(如端口号错误等),服务器会发送一个带有 RST 标识的报文段给客户端,告知客户端当前连接出现问题,需要重新建立连接。例如,如果一个客户端与服务器建立连接后,由于网络故障导致连接中断,服务器在一定时间内没有收到来自客户端的任何数据,服务器会向客户端发送 RST 报文段,客户端收到后会重新发起连接请求,重新建立与服务器的通信连接。
SYN:请求建立连接,携带SYN标识叫同步报文段
应用场景 :用于建立两个主机之间的 TCP 连接。例如,当用户打开一个网页浏览器并输入一个网址时,浏览器作为客户端会向服务器发送一个带有 SYN 标识的 TCP 报文段,请求与服务器建立连接。服务器收到后会回复一个带有 SYN 和 ACK 标识的报文段,然后客户端再发送一个带有 ACK 标识的报文段,完成三次握手,建立可靠的 TCP 连接后才能进行数据传输。
FIN:通知对方,本端要关闭了,携带FIN标识为结束报文段
应用场景 :用于正常关闭 TCP 连接。例如,在一个文件下载完成后,客户端发送一个带有 FIN 标识的报文段给服务器,告诉服务器客户端已经完成下载,要关闭连接。服务器收到后,会进行相关的清理工作(如关闭文件句柄等),然后回复一个带有 ACK 标识的报文段,之后服务器也可能发送一个带有 FIN 标识的报文段告知客户端自己也准备好关闭连接,客户端再回复 ACK 报文段,完成四次挥手,彻底关闭连接。
4.确认应答机制
第一次握手
客服端发送了TCP报文段,其中SYN标志位置为1,表示开始请求建立连接,报文里面还有初始序列号ISN,服务器收到SYN报文后,就可以知道连接请求的序列号
第二次握手
收到客服端的SYN报文后,同意连接,就发送SYN和ACK标志都为一的报文段作为响应,服务器会现则一个初始序列号放入序列号字段,同时将确认号字段设置为客服端的ISN+1,表示成功接收客服段的SYN报文,比期望接好客服端的下一个数据字节。这里不是序号+1作为发送回去的序号,因为确认序号是对端的序号+1,不然客服端不知道是否接收成功。
序列号(Sequence Number) 是发送方为每个字节的数据分配的唯一编号,用于标识和排序发送的数据。
确认号(Acknowledgment Number) 是接收方告知发送方已经成功接收到的最后一个字节的序列号加一,表示期望接收的下一个字节的序列号。
第三次握手
客服端收到服务器的SYN-ACK报文,发送一个ACK标志位为1的报文端作为确认,这个报文的序列号为ISN+1,这里跟第二次握手的确认序号有关系,因为是期望从这里发送,不是冲突的,确认号为Y+1,是服务端序号+1,表示成功接收到服务器的SYN报文,同时告知服务器期望接收的下一个数据字节。
第三次握手之前都是不发数据的,第三次握手知道对端缓冲区大小,前两次是不携带数据的就可以发送报文过去,也不会浪费资源。
补充初始序列号作用
保证连接唯一性,序号是唯一的,那么以序号连接就是唯一的
数据有序传输,序列号是接收方能够按照正确的顺序接收和重组数据,即使乱序到达也可以重新排序。
防止报文重复,可以做到去重操作,避免重复接好同样报文
超时重传机制
主机A发送数据给B后,可能会因为网络问题,数据无法到达主机B。
如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发。
但是主机A没有收到B发来的确认应答,也可能是ACK丢失了,因此主机B会收到很多重复的数据,TCP就需要识别出那些报是重复的包,并丢弃掉重复的。
TCP为了保证无论在任何情况下都能比较高性能的通信,就会动态计算这个最大超时时间,Linux超时以500ms为一个单位进行控制,每次判断超时重发的时间都是500ms整数倍,如果第一次不行,第二次就是2*500ms进行重传,接下来就是4*500ms,累积到一定重传次数,TCP认为网络或者对端主机异常,强制关闭连接。
5.连接管理机制
服务端状态转化
closed->listen服务器调用listen进入listen状态,等待客服端连接
listen->syn_recd一旦听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客服端发送SYN确认报文
syn_rcvd->established服务端一旦接收到客服端的确认报文,就进入established状态,进行读写数据
established->close_wait当客服端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文进入close_wait
close->wait->last_ack进入close_wait后说明服务器准备关闭服务器进行关闭连接,当服务器真正调用close关闭连接时,会向客服端发送FIN,此时进入服务器进入last_ack状态,等待最后一个ack到来(是ack是客服端确认收到了FIN)
last_ack->closed服务器收到了对FIN的ack,彻底关闭连接
客服端状态转换
closed->syn_sent客服端调用connect,发送同步报文段
syn_send->established connect调用成功,则进入established状态,开始读写数据
established->fin_wait_1 客服端主动调用close,向服务器发送结束报文,同时进入fin_wait_1
fin_wait_1->fin_wait_2客服端接收到服务器结束报文的确认,则进入fin_wait_2,开始等待服务器的结束报文段
fin_wait_2->time_wait客服端接收到服务器发来的结束报文段,进入time_wait,并发出last_ack
time_wait->closed客服端要等待一个2MSL(Max Segment Life,报文最大生存时间)才会进入closed状态
6.报文管理
服务器在面对多个客服端发来的报文需要进行处理,就用到了队列的数据结构来管理多分报文,通常有一下两种队列,接收队列和发送队列。
接收队列:存储从网络中接收的数据报,数据包到达时,按照到达的顺序放入接收队列,应用程序从队列头部读取数据包进行处理。
发送队列:存储待发送的数据包,应用程序将数据包放入发送队列尾部,网络协议栈从队列头部取数据报发送。
每个客服端都有独立的接收和发送队列,服务端为每一个客服端维护独立的队列,确保客服端的数据不会混淆。
对于多个客服端进行连接,就会有多种连接情况,有的刚进行连接,有的连接一半,有的连接成功,这些连接状态就需要管理起来。
数组:用于存储全连接队列中的连接信息,每一个元素对应一个连接,包含文件描述符,客服端地址,缓冲区指针的等。
全连接和全连接队列
全连接:全连接是指 TCP 三次握手已经完成,客户端和服务端已经建立了完整的通信连接,可以开始双向数据传输。
全连接队列:当客户端和服务端完成三次握手后,该连接会被移到全连接队列中。全连接队列中的每个连接都代表一个已经建立的、可以进行数据传输的连接。服务端为每个全连接分配了必要的资源,例如缓冲区、文件描述符等。
数组用于存储全连接队列中的连接信息
数组是一种基本的数据结构,用于存储多个相同类型的数据项。在全连接队列的管理中,数组可以用来存储每个连接的相关信息。
数组元素对应连接信息:数组的每个元素代表一个全连接。每个元素中存储的信息可能包括:
文件描述符:用于标识和操作该连接的句柄。
客户端地址:记录连接的客户端的网络地址。
缓冲区指针:指向用于存储该连接接收和发送数据的缓冲区。
其他状态信息:如连接建立时间、最后活动时间等。
数组索引的作用:通过数组的索引,可以快速访问和定位特定的连接。如果服务端需要处理某个连接的数据,可以直接通过索引找到对应的数组元素,进而获取该连接的所有相关信息。
动态调整大小的局限性:数组的大小在创建时通常是固定的。如果连接数量超过了数组的大小,就需要创建一个更大的新数组,并将原有数据复制到新数组中,这个过程较为繁琐且可能影响性能。因此,在实际应用中,可能会采用动态数组(如
std::vector
在 C++ 中)来管理全连接队列,以便更方便地动态调整大小。
三次握手补充
三次握手本质是四次,客服端的发送请求SYN,以及应答对端的ACK,发送方的SYN和ACK,但是服务器一般都是无脑接收请求,所以就把应答和连接请求合在一起发送过去了,就变成了三次报文发送,服务器的SYN和ACK合在一起变成捎带应答报文了。
为什么四次挥手不可以合并
因为客服端发送断开连接,服务器并不一定也断开连接,可能还在发送数据,就会延后,发送数据才会发送FIN进行断连请求,所以就不是很好的合并在一起。
关闭连接
全双工表示双方可以同时接收和发送数据,如果一方断开连接变成半连接就是半双工状态。
而在网络连接中,close会释放文件描述符,则对端就无法发送数据给来,因为文件描述符释放就无法读取数据,shutdown函数可以只关闭写端,文件描述符并不会释放,处于半双工状态。
一方断开连接另一端没有断开,没有断开连接的一端就会一直处理close_wait的状态,但是依旧占用文件描述符,就会占用系统资源,占着茅坑不拉屎,导致了文件描述符泄漏问题,达到一定程度就会出现卡顿,进程挂掉现象。
一端先发送FIN进行断连,最后不会马上closed,而是需要等待一定的时间才能处于closed状态,等待过程是处于time_wait状态,处于time_wait的时间是两个报文最大存活时间,之所以是这个时间,因为报文有序号,可能在某个路由器上排队没有丢包但是判断超时了,就会执行超时重传,但是这个报文还是存活中,当客服端关闭重启时,那个报文刚好发送到服务器,而客服端重启连接是各种信息不变(目的ip和源ip等,有概率),服务器就会处理那个旧报文,从而影响连接,导致三次握手失败问题,因为网络上报文时非常多的,基数大的情况下就有可能发生,所以设置两个报文最大存活时间就是保证双方的旧报文都会消散。前面编写的网络代码,服务器打开后在关闭,重新连接就会显示bind error,是因为服务端先关闭的,就会进入到time_wait状态,还没有进入到close状态,连接还是存在的,端口也没有被释放依旧被占用,所以重连需要换一个端口,或者等待一定时间。
客户端重启连接的相同参数
当客户端重启连接时,如果客户端的 IP 地址、端口号等信息与之前的连接相同,就可能出现旧报文和新连接的冲突。例如,客户端 A 的 IP 地址是 192.168.1.100,端口号是 5000,它与服务器建立了一个连接。当这个连接关闭后,客户端又重新启动连接,并且使用同样的 IP 地址和端口号,就有可能出现旧的报文(在网络中存活的旧报文)和新的连接同时到达服务器。
server的tcp连接在没有完全断开之前不允许重新监听的,某些情况是不合理的
7.滑动窗口

窗口大小是指无需等待确认应答而可以继续发送数据的最大值,上图窗口大小就是4000个字节
发送前四个段时,不需要等待任何ACK,直接发送
收到第一个ACK后 ,窗口先后移动,继续发送第五个数据段,依次类推
操作系统内核为了维护这个滑动窗口,需要开辟缓冲区来记录当前还有那些数据没有应答,只有确认过应答的数据,才能从缓冲区中删掉,因为可以会超时重传。
出现丢包情况如何进行重传
情况一:数据包抵达,ACK被弄丢了
这种情况下,部分丢了ACK不要紧,会有超时重传机制,发送端会在每个报文后面启动一个超时计算器,会根据网络的往返时间和其变化情况来计算,超时未收到就会重传报文过去,直到收到ACK应答。
情况二:数据包直接丢了
当某一段报文丢失后,发送端会一直收到1001这样的ACK,因为1001是丢失的,没有接收到ACK应答,如果发送端主机连续收到3次同样一个“1001”这样的应答,就会将对应的数据1001-2000重新发送,也就是快重传,这个时候接收端收到1001后,再次返回的ACK就是7001,因为前面的2001-7000以及是接收到了,被放到了接收端操作系统内核的缓冲区中。
滑动窗口细节
滑动窗口不会向左滑动,滑动窗口可以变大,变小和不变,不变就是发送过去的数据立即被处理了,这样缓冲区就是原样,为0就说明接收方的接收缓冲区已经满了,无法接收数据。
滑动窗口丢包
1.最左侧丢包
最左侧丢包,其余的发过去,则接收端会返回应答报文,里面的确认序号就是未发送的丢包的数据,会一直发送,发送端就会识别出要执行快重传,重新发送数据包,直到接收到应答,窗口移动,窗口移动的条件是收到应答。
2.中间丢包
中间丢包就说明前面没有丢包,那么前面就会收到应答,滑动窗口就会先右边移动,直到start位置是中间数据发送过去的数据,因为这里是丢失就收不到应答,然后接收端就会一直发送这里的确认序号,就会触发快重传。
补充
滑动窗口是不会溢出的,就像环型队列一样,只要起点和终点位置不一样就说明还可以多发送数据,只要起点和终点位置重合,就说明对端的接收缓冲区到达极限了,就不会扩大了。
第三次握手可以携带数据,第三次握手是应答和数据一起发送过去,对于客服端来说,先发送了请求连接,第二次握手又得到服务端的应答,就说明客服端可以发送数据了,前两次握手也知道了对端的最大接受能力,只是服务端没有接受到应答,这样就可以提高效率。
8.流量控制
接收端处理数据的速度是有限的,发送端太快导致接收缓冲区就会被打满,如果继续发送数据就会导致丢包问题,也会有重传反应出现。TCP支持了根据接收端的处理能力,来决定发送端的发送速度,机制就是流量控制。
通过报文的窗口大小得知对端的缓冲区大小,根据窗口的大小来动态调整发送量。
窗口探测是发送一个没有数据的报文,发送报文就会收到应答,应答报文中就有窗口大小的信息,就可以得知此时对端的接受能力,以便动态调整发送速度,达到合适的传输速度。
流量窗口报文中的放大因子M是用于扩大窗口大小字段的值,从而支持更大的窗口,实际窗口大小字段的值左移M位,就会有窗口的大小字段乘以2的M的次方,M的取值范围通常是0到14,窗口因子可以将窗口大小扩大为31位,使最大窗口的值达到2^30字节左右,提高了16位窗口最大值为65535的问题,可以表示更大的数值。
9.拥塞控制
发送开始时,定义拥塞窗口大小为1,每次接收到一个ACK应答后,拥塞窗口+1,每次发送数据包时,将拥塞窗口和接收端主机反馈的窗口大小作比较,取较小的值作为实际发送的窗口。
以这样的增长速度,就是拥塞窗口的指数式的慢启动,初始比较慢,但是增长速度比较慢。
少量丢包,仅仅是触发了超时重传,大量丢包,就认为是网络阻塞。
拥塞的慢启动方案,以指数来增长,指数特性前期增长慢,但是后期增长快。前期慢速增长是为了试探,在危险边缘(阻塞)试探,后面就会慢慢提高速度,达到更快的发送速度,然后到达一个阈值就不在指数增长而是线性增长,这个临界点就是一个阈值,为了支持滑动窗口,就有了一个拥塞窗口,一个int值,小于这个值是大概率不会造成阻塞,大于这个值大概率会造成阻塞。所以滑动窗口的值就是拥塞窗口和对方接收取能力大小两个中最小的哪一个。拥塞窗口的值不会一直增大,而是动态变化的,临界点ssthresh,如果在线性探测中出现了拥塞情况,就会重新回到0出开始慢启动,这次的临界值ssthresh的值就是上一次达到拥塞的值的一半,从新的临界值开始线性探测,临界值是在动态变化的。拥塞窗口的值如果不拥塞,逻辑上就是会一直增长的,但是会受限于物理设备,带宽会影响到实际的发送数据能力,所以拥塞窗口变大与实际发送数据能力不一定变大。
10.延时应答
延时应答机制在接收到报文后,不着急应答,先等待一会,这个时间是在重发时间内,在等待的时间中,上层可能会提取缓冲区的数据,窗口的大小就会变大,就可以让对端发送更多的数据过来,提高了效率。滑动窗口的值受限于拥塞窗口和对端缓冲区大小,有一种可能延时上层提取后的缓冲区大小刚好是滑动窗口可以达到的大小,触发这个的概率很小的,但是网络中基数很大,概率事件就会有实例出现,只要有实例就一定会提高效率,那么这个机制就是有意义的。
延时应答数量限制:每隔N个包应答一次,N一般为2
时间限制:超过最大延迟时间就应答一次,超时时间一般为200ms
11.捎带应答
捎带应答也是为了提高效率,大部分报文都是ACK标志位为1,把应答和数据放在一起发送过去,提高了效率,不用分两次发送过去。
12.粘包问题
粘包问题是数据一次新发送不过去,发送的数据大于对端缓冲区接收大小,就需要多次分段发送过去,而多次发送就需要保证能取出完整数据,就需要协议来处理,明确报文和报文之间的边界线,还需要序列化和反序列化参与,保证了分段传输,对端能完整提取数据。
13.异常情况
1.一端直接退出,另一端未退出
一端退出就无法接收对端发过来的数据,而对端就会触发超时重传机制去一直发送数据,直到达到了一定的次数,就会认定对端处于断开连接的状态,从而关闭连接。
2.一端退出连接后马上重连
数据传输恢复
若在网络修复前,另一端因连接异常而处于等待状态,网络修复后,只要连接尚未被终止,另一端就会继续按照TCP协议栈的机制进行数据传输,之前发送的数据因故障未收到对端的确认应答,网络恢复后,另一端会重新发送之前未被确认的数据,等待对端的应答,从而恢复通信。
连接状态恢复
网线故障期间,另一端以及将连接判断位不可用并准备关闭连接,那么在网线修复后,另一端可能根据连接状态来决定是否重新建立连接。如果重新连接时,对端还在超时重传,当网络恢复后并收到对端的响应后,连接可能恢复到正常工作状态,而不用重新建立连接。
如果已经过了超时重传的时间,开始执行连接关闭的流程,网络恢复也处于连接关闭状态,另一端就需要重新发起建立连接过程,才能与对端重新建立通信。
14.TCP保活机制
发送保活探测报文,当连接处于空闲状态时,也就是一定时间没有发送数据,保活机制会定期发送保活探测报文。报文通常是一些小的TCP数据包,不包含实际的应用数据。
保活时间间隔:保护探测报文的发送数间间隔可以配置,常见的保活时间间隔为2个小时。
保活探测次数:如果连续发送多个保活数据报文后并没有收到对端的响应,TCP协议栈会认为连接已经断开,并关闭连接,通常默认值是三次保活探测报文。(TCP协议栈是操作系统中实现TCP协议及相关功能的软件模块集合,负责处理TCP协议相关的操作)