2026/4/6 12:26:18
网站建设
项目流程
文章目录传输控制协议TCP协议段格式⭐确认应答ACK机制⭐超时重传机制3次握手4次挥手3次握手4次挥手连接状态转换建立连接断开连接细节⭐流量控制机制⭐滑动窗口⭐延迟应答⭐拥塞控制⭐面向字节流⭐粘包问题TCP异常情况TCP小结传输控制协议TCP全名叫做传输控制协议(Transmission Control Protocol)要对传输进行控制HTTP协议在应用层我们已经讲解了TCP协议发生在传输层应用层有用户级缓冲区传输层有发送缓冲区和接收缓冲区当我们调用系统函数send,write,read,本质上是将数据从用户级缓冲区拷贝到发送缓冲区发送缓冲区通过TCP和IP协议发送至目标主机的接收缓冲区再从接收缓冲区拷贝到用户级缓冲区TCP协议段格式标准报头20个字节选项40个字节如何分离报头和有效载荷交付上层固定长度自描述字段有效载荷长度类型16位源端口号和16位目的端口号表示从哪个进程来到哪个进程去故之前学的端口号是16位4bit位首部长度表示报头大小注意这里基本单位是4字节2^4*460字节20字节是标准报头长度剩下40字节是选项暂时忽略16位窗口大小表示当前接收缓冲区的大小16位检验和发送端填充接收端检验若检验不通过则认为数据有问题TCP是有状态的6个标志位URGACKPSHRSTSYNFIN,接收端接收不同信号做出不同相应SYN申请建立链接把带有SYN标识叫做同步报文段ACK确认信号有效FIN告诉对方本端要关闭了PSH通知接收端快点把TCP接收缓冲区拿到上层RST申请建立链接URG设置紧急指针有效紧急指针应用场景一个软件出现故障了接收缓冲区阻塞了我们TCP为了保证有序发过去的检查报文肯定要排序但是阻塞轮不到我们的检查包这时URG设置1紧急指针设置偏移量接收端优先处理紧急指针检查软件状态⭐确认应答ACK机制为了保证TCP的可靠性我们必须保证数据传输的顺序性序号因此被设计每个数据包都有自己的序号比如说我们发送了序号为100的数据包长度100字节接收端会返回ACK201确认信号表示201之前的数据包我都收到了发送了序号为100的数据包长度100字节又发送了序号201的数据包长度100接收端如果先收到序号201的包会暂存等待序号100发送收到后返回301表示301之前的数据我都收到了确认序号的含义表示确认信号之前的报文我们都收到了保证了滑动窗口线性滑动避免没收到的报文我们直接跳过了⭐超时重传机制客户端发送100-200数据包还没发过去就丢了怎么办超过一定的时间间隔发送端会再次发送如果接收端接收到了返回的ACK的应答机制丢失了怎么办因为发送端没收到应答超时后会再次发送数据包那接收端岂不是接收两个相同的数据包别担心序号自带去重机制过多少时间算超时这个时间间隔随网络状况动态变化太长了会耽误效率太短了数据包还在网络中没传过过去频繁发重复的包TCP为了保持高性能通信动态计算这个值LINUX和Windows超时都是以500ms单位控制超时都是500ms整数倍若500ms未收到应答重传再未收到应答过500*2ms重传还没收到过500*4ms依次倍增若累计多次无应答发送方认为网络或对方主机出现异常强制关闭连接3次握手4次挥手3次握手首先建立链接客户端向服务端发送SYN报文服务端收到之后返回对此应答再发送SYN报文给客户端客户端收到后返回应答双向链接建立成功为了提高效率服务端通常把应答和SYN合并叫作捎带应答所以4次握手也是3次握手理解链接维护链接是有成本的服务端肯定会与多个客户端建立链接将这些链接先描述再组织管理起来另外世界上不存在100%成功的通信因为我们通信的时候最后一句永远没有应答所以当我们客户端发起最后一次应答时操作系统默认认为成功为客户端分配通信资源所以为什么要三次握手而不是1次或2次1次握手会导致SYN洪水客户端发送一个SYN请求服务端就要开空间存储但是如果客户端也不知道链接是否建立会多次发送SYN浪费服务端资源况且如果想恶意攻击服务器只需要多个客户端多次请求链接即可肉机2次握手为什么不行客户端请求服务端返回应答同时必须要为此次链接开辟空间客户端收到了应答开辟空间虽然看着可行但是代价还是让服务器承受了如果链接出问题了浪费的是服务端资源而服务端又要和多个客户端链接基数大易出错所以3次链接客户端先创建资源把风险规避到客户端一人身上并且验证全双工不然服务器崩了所有链接都失败4次挥手若客户端先关闭链接先向服务端发送FIN报文服务端收到后返回ACK应答服务端发送FIN报文客户端收到返回ACK应答4次挥手链接关闭为什么4次挥手发送报文双方都要发送断开链接双方都要断开客户端发送FIN后服务端如果没有想给客户端发送的报文了恰好也想中断链接也可以将FIN和应答一起发送也就是3次挥手但可能还有没发送的报文在此发送连接状态转换建立连接服务端调用listen函数进入监听状态LISTEN调用accept阻塞等待客户端连接客户端调用connect函数之后阻塞等待发送SYN报头服务端接收后进入状态SYN_RCVD,捎带应答客户端接收后进入状态ESTABLISHEDconnect返回客户端开辟资源建立连接给服务端应答服务端接收应答后进入ESTABLISHED状态返回acceptwrite实际是写入发送缓冲区read从接收缓冲区读取数据断开连接客户端调用close进入FIN_WAIT_1状态发送FIN服务端接收后进入CLOSE_WAIT状态返回应答客户端进入TIME_WAIT_2状态服务端调用close进入LAST_ACK状态发送FIN客户端进入TIME_WAIT状态等待一段时间进入CLOSED返回应答服务端接收进入CLOSED连接关闭细节13次握手与上层accept没关系是操作系统自动完成的2listen第二个参数是全连接队列最大节点数量既然accept与握手没关系服务端建立好的连接肯定是要组织的用队列组织起来accept从队列中拿节点全连接队列从半连接队列拿节点。这也是个消费生成者模型生产的节点放入队列accept消费为什么listen第二个参数不能设置太长accept消费能力有限太长它也消费不过来维护队列还要浪费资源为什么不能太短比如餐厅外面放很多板凳生意火爆挤不进去但一但有座位直接进去节点数就是凳子设置太短的话如果空出桌子来我们无法立即补充白白损失性能3服务器不会长时间保持SYN_RCVD状态处于此状态节点加入半连接队列不会长时间维护半连接处于半连接队列完成的节点完成3次握手后加入全连接队列4主动断开连接的一方在4次挥手之后进入TIME_WAIT状态2MSL之后进入CLOSED数据自动释放MSLTCP报文在网络传输最大存活时间通常设定2分钟TIME_WAIT作用通信双方数据自动消散断开连接提高4次挥手容错性万一最后一次ACK丢失了为什么服务器主动断开连接不能迅速重启因为进入TIME_WAIT状态要等待数据消散并且端口号可能被其他线程使用解决方案处于TIME_WAIT状态的服务器可以立即重启为什么客户端看起来不用等待可以直接重连因为客户端端口号随机分配的⭐流量控制机制接收端处理数据是有限的如果发送端发送数据过快导致接收缓冲区被打满再发送数据就会丢包所以我们需要控制发送速度接收端可以把自己接收缓冲区大小放入TCP报文“窗口大小”字段中通过ACK给发送端字段越大说明吞吐量越大如果收到窗口小就要减缓发送速度为0发送端不再发数据但是定时发一个报头询问对方缓冲区大小流量控制通过滑动窗口实现⭐滑动窗口如果我们每一个发送数据都要等待应答那样性能也太差了尤其是往返时间长的时候所以我们多条发送多条应答将等待时间叠在一起前4个数据发送不用等待应答收到第一条应答后,窗口向后滑动发送第5条数据以此类推窗口越大吞吐量越高万一有发送过去没收到应答的数据呢所以我们需要开辟发送缓冲区维护窗口双指针左边是已发送的已被确认的中间是已发送但未收到应答右边是待发送如果丢包该如何重传情况一数据包已传过去但应答丢了我们收到6001默认已收到前面应答但实际上滑动窗口滑不过去卡在1-1000会重新补发情况二数据包在传过去路上就丢了1001-2000在路上丢了会不断返回1001就像在我想要1001发送方收到3个1001会重新补发收到1001的应答之后再次收到就是7001了因为前面发的都已经乱序缓存了这种机制叫做高速重发控制也叫快重传已经有了快重传为什么还要超时等待快重传提高效率超时等待兜底滑动窗口只能右移窗口大小动态变化的左不动右移窗口变大左动右不动窗口变小也可以不变窗口怎么确定int start确认序号设置int end确认序号win大小winmin(win有效数据拥塞窗口)起始序号是双方协商定的比如定在1234传数据时数组下标1234即可因为之前通信有残留数据如果我们断开快速重连可能收到旧数据所以起始序号随机值再与之前通信数据重叠的概率就很低了我们下一次窗口定了5000大小但我们只剩2000大小的数据了怎么办当然是缩小窗口只传有效值滑动窗口会越界吗不会TCP缓冲区采用了环形算法其实还有第4块窗口(可以归并到已确认的窗口)⭐延迟应答比如说接收端缓冲区大小1M我们向接收端缓冲区发送500KB数据这时立即返回窗口就是500KB但是有可能10ms后上层就处理完数据了所以如果10ms再后返回的窗口就是1M所以我们延迟一会应答吞吐量会更大延迟应答也有限制设置隔几个包就应答一次超过最大时间应答一次不同操作系统有差异一般是隔两个包最大时间设置200ms最好的方法就是缓冲区有数据立即通过readrecv从内核拿上来返回的窗口就更大记住窗口越大吞吐量更大性能越佳⭐拥塞控制TCP发送报文时不一定是对方主机出现问题也有可能是网络问题少量丢包可以理解但是大量丢包有可能是网络阻塞了这时我们不敢贸然发送大量数据了否则只会加重阻塞同时网络不止我们一个用户多个用户共用一个网络每台主机识别到网络阻塞时都要做慢启动滑动窗口大小min窗口大小有效数值拥塞窗口大小慢启动就是一开始发送数据很小但之后呈指数增加后面越来越快不能让它成倍增加我们设置一个阈值超过这个阈值之后线性增加线性增加到一定值又阻塞后我们将阈值减为该值的一半即指数增长到该值后再次线性增长⭐面向字节流TCP是面向字节流的怎么理解如同水龙头想开多大就开多大字节就像水站在用户层来说对于写100个字节我们可以写1次100字节也可以写100次1字节读取字节也一样面向字节流用户不认识报文只认识一串字节站在传输层看TCP是按照序号一个个排在缓冲区的站在用户层看到的只是一串字节有时多拿有时少拿所以该怎么明确报文与报文边界⭐粘包问题包是就是应用层数据包解决粘包就是明确两个包边界四种方案固定报文报文大小固定应用层固定大小去拿特殊字符特殊字符分割两个包自描述字段特殊字符如http报文\n分行content确认正文大小自描述字段定长报头如UDP先读取20字节报头再读取content大小UDP会出现粘包问题吗不会UDP一个个交给应用层具有明显边界要么不传要么传1个不会出现传半个情况TCP异常情况关闭程序在关闭的时候其实就是正常挥手的过程电脑重启重启之前就是在关闭所有进程TCPsocket类似文件描述符由进程持有和关闭程序一致正常挥手断开连接突然断电服务端也会有保活机制过一段时间向客户端发送消息久久不回答会断开连接TCP小结与UDP相比保证可靠性方法序列号按序到达连接管理确认应答超时重传流量控制拥塞控制提高性能方法滑动窗口快速重传延迟应答捎带应答TCPUDP相比TCP用于可靠性传输如文件传输重要状态更新UDP用于高速传输和追求实时更新如早期qq视频传输也用于广播