尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

网络原理 ----TCP

发布时间:2026/9/11 17:44:58

资讯中心
01
ARTICLE

网络原理 ----TCP

网络原理 ----TCP
一TCP协议在这张图中前六行是报头最后一行是载荷真实的TCP协议是一行的在这张图中是每四个字节换一行TCP全称为传输控制协议(TransmissionControlProtocol).人如其名,要对数据的传输进行一个详细的控制源/目的端口 表示数据从哪个进程来到哪个进程去4位首部长度描述一个TCP报头的长度TCP报头的长度可变导致需要有一个属性描述到底是多长四位这四位字段叫数据偏移一共四个二进制而四个二进制位的取值范围为0-1111即0-15的范围为0~15此处的单位是4字节所以TCP报头的最大长度为60字节最小长度为20字节一行四个字节“选项”之前一共五行选项optional 可选的选项中有很多个属性可以选择一个也可以选择多个还可以一个都不选保留6位现在不用但是留给未来做扩展吸取了udp的教训4位TCP报头长度表示该TCP头部有多少个32位bit(有多少个4字节)所以TCP头部最大长度是15* 4 606个标志位 URG:紧急指针是否有效ACK:确认号是否有效PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走RST:对方要求重新建立连接。我们把携带RST标识的称为复位报文段SYN:请求建立连接。我们把携带SYN标识的称为同步报立段FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段二TCP的核心机制2.1 可靠传输一个数据发出去到对方收到中间经过很多的路由器/交换机这些路由器/交换机相当于“路口”每个路由器/交换机转发能力有上限如果某个时刻的数据传输量暴增可能超出某个设备的转发能力上限但是在网络世界中数据是有时效性的数据“堵车”了等到达最终目标就无意义了不如直接丢包丢包通常是路由器/交换器的主动行为所以丢包是客观存在的并且无法预知什么时候产生丢包“可靠传输”就是对抗丢包的1感知到丢包2丢包后有补救措施2.2 如何实现可靠传输核心机制1确认应答确认应答是实现可靠传输的核心机制能够感知到对方是否收到对方收到之后回复一个“收到”确认应答是通过特殊的数据包来实现的在6位保留位中其中的第二个ACK这一位如果为1就表示应答数据报ACK即acknowledge应答可能会有“后发先至”的随机事件发的数据包就相当于一辆一辆的车经过的每个路由器交换机就是路口每个数据包在经由路由器转发的时候走的路径可能不同后面的车可能会抄近道什么的所以应答是需要的但是需要考虑应答的顺序可以给数据编号此时应答数据可以根据编号完成应答TCP实际上是根据每个字节进行的编号采取了连续递增的整数来对数据进行编号所以只需要知道数据中第一个字节的编号后面每个字节的编号就都知道了编号是给载荷部分编号报头部分不需要编号报头中32位序号此处保存的就是当前数据包载荷中第一个字节的编号应答报文要根据序号来进行应答例如发送方发送1~44接收方收到了这些数据返回ack告诉发送方收到了哪些数据应答报文中的确认序号填写的值是收到的数据最后一个字节的序号 1而不是第一个字节的序号可以理解成1接收方在告诉发送方哪个序号前面的数据全都收到了2接收方也是在向发送方索要下一个数据从哪里开始发送1-1000的时候TCP是一个普通的TCP数据报此时序号这里填写成1ACK标志位为0返回应答报文1001的时候TCP是一个应答数据报此时序号这里是独立编号根据方向来编号的确认序号这里填写的是1001ACK标志位为1同时ACK数据报的载荷是空的核心机制2超时重传超时重传是针对确认应答进行的重要补充也是实现可靠传输的核心机制丢包是客观存在的随机出现如果丢包了就把丢的数据重发一遍重发数据是可以对抗丢包的Q如何判定是否丢包A根据ack的等待时间来判定主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发但是,主机A未收到B发来的确认应答,也可能是因为ACK丢失了因此主机B会收到很多重复数据.那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉这时候我们可以利用前面提到的序列号,就可以很容易做到去重的效果保证同一份序号的数据应用程序这里只能read到一次在接收方中操作系统内核中有一个接受缓冲区内存每个socket都有自己独立的接收缓冲区每次收到新的数据操作系统判定当前这个序号是否已经在缓冲区中存在了如果存在了就直接把这个数据丢弃了不存在才放到接收缓冲区中应用程序读取数据从接收缓冲区来读取数据在接收缓冲区中作用有1去重2重新排序保证应用程序读到的数据就是发送的顺序解决了后发先至的问题Q超时的时间如何确定?A最理想的情况下,找到一个最小的时间,保证确认应答一定能在这个时间内返回。• 但是这个时间的长短随着网络环境的不同是有差异的。• 如果超时时间设的太长会影响整体的重传效率;• 如果超时时间设的太短有可能会频繁发送重复的包TCP为了保证无论在任何环境下都能比较高性能的通信因此会动态计算这个最大超时时间• Linux中(BSDUnix和Windows也是如此)超时以500ms为一个单位进行控制每次判定超时重发的超时时间都是500ms的整数倍.• 如果重发一次之后,仍然得不到应答,等待2*500ms后再进行重传.• 如果仍然得不到应答等待4*500ms进行重传。依次类推以指数形式递增.• 累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接.注意重传的时间不是无限长的重传的次数也不是无限的有一定的阈值达到一定的程度就会视为“网络出现严重故障”放弃通信了释放连接核心机制3连接管理连接管理对于可靠传输起辅助作用在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接建立连接建立连接的工作过程称为“三次握手”握手即计算机中常见的专业术语握手就是打招呼其本身这个动作并不传递实质性的业务数据应用层数据包仅仅是打招呼握手过程中传递的是“只有报头没有载荷”的TCP数据报建立连接是通信双方的事情一个巴掌拍不响客户端需要告诉服务器“我要和你建立连接”即我要保存你的信息服务器也要告诉客户端“我也要和你建立连接”即我也要保存你的信息通信双方把对方的信息都保存了连接才算完成如图从上到下时间在推移谁发起第一次请求谁就是客户端即谁主动谁是客户端在这个请求中报头里包含一个SYN标记和 synchronized同步 完全不同多线程的“加锁”的“同步”本质上是“互斥”TCP这里的同步其实是一种“通知”表示接下来要进行通信了此处这个syn数据报也称为“同步报文段”图中有四次网络通信但是建立连接却称为三次握手是因为中间两次的网络通信是可以合并的中间两次的数据传输时机是完全相同的1收到syn之后第一时间来返回内容。2把两个数据合并成一个提高传输效率合并成一次只需要封装分用一次分两次发送需要封装分用两次三次握手的意义1投石问路初步的验证了网络通信路径是畅通的是后续进行可靠传输的前提条件2验证通信双方的发送能力和接收能力是否正常3三次握手过程中还可以完成一些参数协商三次握手中协商的一个重要的参数序号从哪里开始TCP通信时序号大概率不是从1开始的三次握手的时候客户端和服务器各自生成一个自己的初始序号通过三次握手告诉对方对方就知道接下来咱们的通信你的数据会从哪个序号开始三次握手是否可以改成四次两次1可以改成四次但是没有必要中间的两次通信最好还是合并在一起不要拆开拆开效率会比较低2不可以改成两次TCP的状态转换CLOSE这是一个虚拟的状态表示TCP连接释放了不存在LISTENlisten服务器进入的专属状态表示服务器已经准备就绪随时可以用客户端来建立连接eg手机开机信号良好随时可以有人打电话给你SYN_SENT客户端发送出第一个syn处于的状态SYN_RCVD服务器收到第一个syn处于的状态上面两个状态SYN_SENT和SYN_RCVD很难直接看到存在的时间非常短但是如果通信存在问题此时就可以看到这样的状态ESTABLISHEDestablished表示连接完成表示接下来可以进行数据通信了断开连接断开连接需要四次挥手都表示一个不携带载荷没有业务意义表示特定功能的tcp数据报在三次握手中一定是客户端开始第一个操作但是在四次挥手中客户端和服务器都有可能是主动发起的一方FIN结束报文段Q中间的两次能否合并变成三次挥手不一定有的时候可以触发特殊机制有时不可以标准情况下不能合并的关键在于服务器方返回ack和发送fin的时机是不同的ack是在收到fin之后由操作系统内核控制自动的立刻的返回fin 则需要应用程序执行到close方法进程退出才会触发所以是有时间差的TIME_WAIT谁主动断开连接谁就会进入这个状态等待一定时间之后再释放连接进入这个状态之后正常情况下不会收到也不会发送任何数据了静静的等待一段时间连接就自然释放了1Q如果没有time_wait直接释放会怎样最后一个ack可能会丢包如果是前面的数据包丢包直接触发超时重传就可以了因为此时通信双方的连接都正常工作重传都能处理但是最后一个ack丢包此时对方就会重传fin此时客户端这边已经释放了连接的话就没有人响应fin 的重传数据没有人能返回ack了2为什么是TIME_WAIT的时间是2MSL?操作系统中有一个参数MSL表示网络通信中数据从一端到另一端传输经历的最大时间• MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话• 就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的);• 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器会再重发一个 FIN. 这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK)CLOSE_WAIT等待执行close方法一般⽽言对于服务器上出现大量的CLOSE_WAIT状态原因就是服务器没有正确的关闭socket导致四次挥手没有正确完成。这是⼀个BUG只需要加上对应的close即可解决问题核心机制4滑动窗口这个窗口就是个比喻窗口内部可以过去但是窗口外部就不可以过去刚才我们讨论了确认应答策略,对每⼀个发送的数据段,都要给⼀个ACK确认应答.收到ACK后再发送下 ⼀个数据段.这样做有⼀个⽐较⼤的缺点,就是性能较差.尤其是数据往返的时间较⻓的时候.既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了).注意批量发送数据也不能无限大的批量发送批量发送的数据越多效率确实越高。但是批量发送的数据太多了对于可靠性也是有影响的所以需要对批量发送的“度”进行限制定量的衡量这个概念称为窗口能够不阻塞的发送数据的最大的量就称为“窗口大小”窗口大小指的是无需等待确认应答而可以继续发送数据的最大值.上图的窗口大小就是4000个字节 (四个段)如图1发送前四个段的时候,不需要等待任何ACK,直接发送2收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推3操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉4窗口越大,则网络的吞吐率就越高Q当收到1001这个ack的时候是继续等等到4001再次批量发送四组数据还是收到1001之后立刻发送一组数据A收到1001之后立刻发送一组数据----------------------------------------------------------------------------滑动传输是TCP中用来提升传输效率的机制但是TCP再怎么提高也不可能比UDP快Q如果出现了丢包,如何进行重传?这里分两种情况讨论1情况一数据包已到达ack被丢了这种情况下的丢包不用管因为“确认序号”填写的是收到数据最后一个字节序号1意思就是这个序号之前的数据就全部收到了比如“下一个是5001”这个ack的含义就是5001之前的数据都收到了包含了1-10001001-2000等等2情况二数据包丢了数据丢了则需要重传如图当1001-2000丢了之后接下来2001-3000这个数据到达对方对方返回的ack是1001不是3001当某一段报文段丢失之后,发送端会一直收到1001这样的ACK,就像是在提醒发送端我想要的是 1001 一样如果发送端主机连续三次收到了同样一个1001这样的应答,就会将对应的数据1001-2000重新发送这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2001-7000)接收端其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中这种机制被称为高速重发控制(也叫快重传)-------------------确认应答和滑动窗口这两种机制是共同配合的适用于不同的场景1如果传输的数据频次低两次传输数据的间隔比较大就使用确认应答---超时重传2如果传输的数据频次高连续不断的write数据就使用滑动窗口---快速重传核心机制5流量控制接收端处理数据的速度是有限的.如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继⽽引起丢包重传等等一系列连锁反应因此TCP支持根据接收端的处理能力,来决定发送端的发送速度.这个机制就叫做流量控制(Flow Control)• 接收端将自己可以接收的缓冲区大小放入TCP首部中的窗口大小字段,通过ACK端通知发送端;• 窗口大小字段越大,说明网络的吞吐量越高;• 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;• 发送端接受到这个窗口之后,就会减慢自己的发送速度;• 如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端接收方每次收到数据之后都会计算出剩余缓冲区的大小把这个值通过ack返回给发送方发送方就可以参考这个数字设置下一轮滑动窗口的大小Q接收端如何把窗口大小告诉发送端呢?ATCP首部中,有一个16位窗口字段,就是存放了窗口大小信息仅仅是在ack数据报中才有效注意这里指定的数字是16bit但是并不意味着最大就是64KB因为在“选项”中还有一个“窗口扩展因子”而实际反馈的数值 16位窗口大小 窗口扩展因子所以发送方会以收到的“16位窗口大小”作为下一轮次滑动窗口的窗口大小1使用接收缓冲区剩余空间大小定量的衡量接收方的处理能力2通过ack的窗口大小通知发送方窗口大小是多少3发送根据收到的ack的窗口大小调整滑动窗口的大小4如果窗口的大小为0此时发送方暂停发送业务数据任然会周期性的发送窗口探测报文上述都是TCP内部实现的核心机制6拥塞控制虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据仍然可能引发问题.因为网络上有很多的计算机可能当前的网络状态就已经比较拥堵.在不清楚当前网络状态下,贸然发送大量的数据是很有可能引起雪上加霜的.TCP引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输 数据拥塞控制也是制约流量控制的发送效率流量控制是根据接收方的处理能力进行制约的拥塞控制是根据中间的通信链路来进行制约的拥塞控制的思路首先按照比较慢的速度发送数据如果没有丢包链路畅通放大窗口加快速度如果出现丢包链路堵塞减小窗口放慢速度通过这样的方式“尝试”出合适的窗口大小如图纵轴的拥塞窗口直接决定了发送方发送的窗口大小纵轴的单位可以理解为“多少份”一份可以是若干个字节初始情况下按照比较小的窗口来传输数据传输速度比较慢称为慢启动没有出现丢包窗口大小会进一步变大按照指数的方式增长并且给窗口大小引入阈值当窗口大小超过阈值的时候不再指数增长而是线性增长为了避免某一个轮次窗口突然变得很大导致出现严重丢包一旦出现丢包就视为出现了拥堵了当前就得减小窗口大小了1旧版本直接回到最初的慢开始状态非常小的窗口进行指数增长线性增长2新版本回到新的阈值位置丢包位置的窗口大小/2从这个位置继续进行线性增长核心机制7延迟应答延迟应答用于提高传输效率即接收方收到数据的时候不会立即返回ack而是稍等一会。在这稍等的一会中接收方的应用程序就可以多消费一部分数据了从而反馈一个更大的窗口如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小• 假设接收端缓冲区为1M.一次收到了500K的数据;如果立刻应答,返回的窗口就是500K;• 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;• 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来;• 如果接收端稍微等一会再应答比如等待200ms再应答那么这个时候返回的窗口大小就是1M;一定要记得,窗口越大网络吞吐量就越大传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率Q所有的包都可以延迟应答么?A并不是数量限制每隔N个包就应答一次时间限制超过最大延迟时间就应答一次具体的数量和超时时间,依操作系统不同也有差异;一般N取2,超时时间取200ms在延时时间里可能是水位降低了也可能是水位升高了所以说延时应答也不能确保返回的窗口100%更大甚至可能变小根据经验规律大部分的情况下还是会变大的因为一旦水位线提升此时发送速度就会降低流量控制Q延迟应答要延时多久A不能延迟太久不然会触发超时重传另一方面也会按照ack的个数来进行延时应答批量返回ack的时候每隔N个数据包返回一个ack核心机制8捎带应答捎带应答是在延时应答的基础上进一步提高效率在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是一发一收的.意味着客户端给服务器说了Howareyou服务器也会给客户端回一个Fine,thankyou那么这个时候ACK就可以搭顺风车,和服务器回应的Fine,thankyou一起回给客户端如图应用程序需要一定的计算和逻辑生成响应正常情况下ack和响应是两个不同的时间但是ack由延时应答在等一会的过程中正好ack就可以搭载响应的顺风车返回的响应数据既是响应数据也是一个ack核心机制9面向字节流创建一个TCP的socket,同时在内核中创建一个发送缓冲区和一个接收缓冲区• 调用write时,数据会先写入发送缓冲区中;• 如果发送的字节数太长,会被拆分成多个TCP的数据包发出;• 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去;• 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区;• 然后应用程序可以调用read从接收缓冲区拿数据;• 另一方面,TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据, 也可以写数据.这个概念叫做全双工由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:• 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节;• 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read100个字节,也可以一次read一个字节,重复100次;粘包问题• 首先要明确,粘包问题中的包,是指的应用层的数据包.• 在TCP的协议头中,没有如同UDP一样的报文长度这样的字段,但是有一个序号这样的字段.• 站在传输层的角度,TCP是一个一个报文过来的.按照序号排好序放在缓冲区中.• 站在应用层的角度,看到的只是一串连续的字节数据.• 那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包那么如何避免粘包问题呢?归根结底就是一句话,明确两个包之间的边界• 对于定长的包,保证每次都按固定大小读取即可;例如上面的Request结构,是固定大小的,那么就从 缓冲区从头开始按sizeof(Request)依次读取即可;• 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从⽽就知道了包的结束位置;• 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议,是自己来定的,只要保证分隔符不和正文冲突即可)对于UDP协议来说,是否也存在粘包问题呢?• 对于UDP,如果还没有上层交付数据,UDP的报文长度仍然在.同时,UDP是一个一个把数据交付给应用层.就有很明确的数据边界.• 站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不收.不会出 现半个的情况.核心机制10异常情况进程崩溃本质上和正常断开连接的过程是一样的调用close方法触发 fin 和进程退出触发 fin 都是文件描述符表中的内容被释放了无论是主动释放还是操作系统统一释放都是释放了• 进程终止:进程终止会释放文件描述符,仍然可以发送FIN.和正常关闭没有什么区别.主机关机• 机器重启:和进程终止的情况相同.主机断电/网线断开1接收方掉电之前客户端发数据都是有ack的突然没有ack此时客户端会触发超时重传最后单方面释放连接会触发一个特殊的数据包复位报文2发送方断电客户端断电此时发送数据突然就没了。此时接收方会等等发送方发送数据如果一定时间没有收到数据的时候就会触发“心跳包”探测对方是否在正常工作1没有心跳则挂了 2心跳是周期性的给对方发送不携带载荷的数据等待对方的ack如果对方有ack回来说明对方的工作状态是正常的如果对方没有ack回来则说明出问题了此时接收方就可以单方面释放连接了• 机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就 会进行reset.即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在.如果对方不在,也会把连接释放.另外,应用层的某些协议,也有一些这样的检测机制.例如HTTP长连接中,也会定期检测对方的状态.例如QQ,在QQ断线之后,也会定期尝试重新连接.三TCP/UDP对比TCP是可靠连接,那么是不是TCP一定就优于UDP呢?TCP和UDP之间的优点和缺点,不能简单, 绝对的进行比较• TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;• UDP用于对高速传输和实时性要求较高的通信领域,例如,早期的QQ,视频传输等.另外UDP可以用于广播TCP只能一对一; 归根结底,TCP和UDP都是程序员的工具,什么时机用,具体怎么用,还是要根据具体的需求场景去判定.3.1⽤UDP实现可靠传输参考TCP的可靠性机制,在应用层实现类似的逻辑;例如: • 引入序列号,保证数据顺序;• 引入确认应答,确保对端收到了数据;• 引入超时重传,如果隔一段时间没有应答,就重发数据
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。