ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

简单了解传输层协议TCP

简单了解传输层协议TCP 上图为TCP报头结构由20字节固定首部可选字段构成其中数据偏移就是4位首部长度记录报头长度的基本单位为4字节比如若首部长度为0101就是5*420字节其中可选字段部分最多40字节15*460字节60-2040字节TCP叫做可靠性协议可靠性协议的含义尽最大努力发送报文保证能在丢失时进行检测和重传但是要注意可靠性不代表百分百保证对方接收到报文假设我们要保证报文被对方百分百接收到就有以下过程首先要知道对发出去的报文要有应答收到应答就代表历史报文绝对被对方收到发送方需要收到接收方的应答保证自己的发送的报文一定被对方收到了可是同样的接收方发回的应答报文不能保证发送方能收到所以发送方还得发送应答报文给接收方代表它收到了很明显如果一直这样下去就会成为一个循环很明显这是错误的所以事实上不存在绝对可靠的协议最新的报文永远都不会有应答TCP协议的可靠性在于对于历史报文的可靠性百分百保证发送方送给接收方的报文接收方一定收到了TCP规定A主机发送给报文B主机B主机必须应答这个应答报文可以不携带任何有效信息A主机收到了就代表双方都收到了因为不需要对应答保证可靠性这个就叫做确认应答机制当A主机发送给B主机的报文保证可靠B主机发送给A主机的报文保证可靠不包括应答报文就是全双工的可靠性总而言之可靠性不代表报文百分百必须送到而是你收到了我得知道没有收到我也知道报文丢失如果A假设是A发的没有收到B的应答报文就会分成两种情况1、报文真的丢失了2、应答丢失了当A没有收到应答报文它首先会判定是报文真的丢失了因为不管是报文丢了还是应答丢了对于A而言都是没收到应答那A怎么知道自己是没有收到任何应答还是还没等到A会给自己设置一个deadline超时就是丢包丢包就是规定出来的如果没有收到应答发送方就会进行 重传没有收到报文之后的补发措施B如果收到了报文是应答丢了再收到了A发的同样的报文就会进行去重报头中的32位序号就是用来进行去重操作的找到序号相同的报文B就会把旧的那一份丢掉TCP有两种通讯方式TCP通信双方地位是对等的它们的通信也是对称的方式一A发报文得到B的报文才能再发给B报文方式二A给B发一批报文B需要对每个报文都做应答真实的通信情况就是方式二方式二的A的等待时间就可以重叠跟发送一个报文的等待时间一样这里只考虑超时重传对于方式二B收到报文的顺序跟A发送的顺序不一定一样这是因为从A到B的路线其实有很多种路径不一样就可能出现晚出发的报文会早到B解决乱序问题就可以通过序号 来进行重新排序保证按序到达B发回的应答报文也需要有序号要不然如果有信号丢失A就没法确定哪个的报文应答丢了这个序号就是确认序号确认序号收到的序号1 就是 确认序号含义该序号之前的所有数据我全部收到了假设确认序号是3001就代表前面的1000 2000 3000 都收到了注意3001没有收到为什么序号和确认序号要分开成为两个明明每次使用都只要用到一个序号另外一个序号用不到为什么不干脆就用一个序号发送时只用到序号应答时只用到确认序号TCP是全双工的既然需要发送应答报文完全可以把要发送的数据也送过去因为数据可以捎带应答发送数据和发送确认就可以同时进行了窗口大小当接收方的接收缓冲区空间已经不够了发送方发送的信息太快接收方来不及接收剩余的就会扔掉让发送方重传这无疑是对网络资源的一种浪费网络传输也是需要消耗资源的就需要进行流量控制为了进行流量控制发送方就得知道接收方的接收能力接收方如何衡量自己当前的接收能力接受能力就是接收缓冲区内剩余空间的大小发送方如何得知对方的接收能力报头里面的16位窗口大小就是用来传递接收方 接收缓冲区的剩余大小标志位服务端会 连接无数个客户端客户端会发送无数报文给到服务端有的报文是应答有的是建立连接的有的是断开连接有的是数据报文这意味着报文本身是需要有类型的TCP 协议报头中就必须有对应的字段表明报文类型标志位就是用来标识报文的类型发送啥标志位就是将啥 标志位 置一ACK置1就是应答报文由于可以捎带应答所以大部分报文ACK标志位都是1SYN 叫做连接建立的请求报文比如客户端要求建立连接服务端在TCP这里是通过三次握手建立连接的发送方需要先发个SYN 给到接收方接收方表示可以连接就会发送SYNACK发送方发送应答发回去ACK什么是连接不管是服务端还是客户端客户端也可以连接多个服务端双方os内部都会存在大量的连接os就需要管理多个链接TCP链接的描述结构体叫做struct tcp_socktcp_sock内部的第一个成员为 struct inet_connection_sock他管理面向连接协议的通用信息比如全连接队列、半连接队列、重传策略等等全连接队列全连接队列也叫 Accept 队列Accept Queue是 TCP 三次握手完成后已经建立起完整连接但还没来得及被应用程序调用accept()取走的 socket 所暂存的队列。半连接队列半连接队列SYN Queue 是服务端用于暂存 “只完成了 TCP 两次握手但还没完成第三次握手” 的连接请求的队列。客户端还没有发送ack报文的时候当完成三次握手os会创建一个struct tcp_sock 对象放到监听 fd对应的全连接队列内服务端accept的时候再为它分配一个全新的 struct file为什么要进行三次握手要通信之前得保证网络通畅三次握手能保证 发送方 接收方 各自进行一次发送和接收验证 双方都可以收和发以最小次数验证全双工两次握手保证不了只能保证A能发数据和收数据B只能保证能收数据发数据保证不了断开连接需要进行四次挥手客户端主动发送FIN服务端发送 ACK同意断开服务端发送 FIN我也要断开链接客户端发送 ACK为什么要进行四次挥手四次挥手是以最小成本的方式争的了双方的同意断开连接是双方的事情双方都需要收到一次FIN四次挥手之后连接结构体自然就被释放了对应的文件结构体也就释放了客户端主动发送FIN 的意思是我再也不发了服务端知道后表示自己还要发消息客户端就还会收到消息结束全双工直到服务端也表示不发信息了就断开链接了在上层的表现就是双方都调用close()还出现一种情况服务端的ACK FIN 一起发给客户端此时四次挥手就变成了三次挥手同理三次握手也可以变成四次握手只不过建立连接时服务器都会无条件答应所以建立连接和应答永远会压缩在一起所以不会出现四次握手而断开链接时服务器必须得在条件满足的前提下才能断开链接所以说是四次挥手RST标志位RST 标志位代表“重置连接”Reset它的作用是立即、强制地终止一个 TCP 连接相当于“直接把电话线拔了”。三次握手时最后一个ACK有没有被接收方收到是不确定的发送到接收是有时间差在客户端角度只要将最后一次的ACK发出去就算是三次握手成功而最后一个ACK 是有可能丢的一旦服务器没收到最后一个ACK就会重新发 SYN ACK可是如果客户端认为自己已经链接成功没有收到服务器发来的syn ack会给服务端开始发送报文服务端收到报文之后就会增加 RST 标志位发送RST报文要求重新链接当然RST标志位还有其它应用场景要更复杂的多这里只简单讲述这一个例子PSH标志位全称push一个极端的例子如果接收端的接收缓冲区剩余空间大小为0发送端就得等可是要等多久是不确定的发送端需要发送报头接收缓冲区满了也可以接收报头不带数据就行接收端就需要做应答就需要重填窗口大小如果接收端窗口大小一直为0发送端发送了好几次也依然是0就会把PSH设置为1接收端接收到它就需要立刻通知应用层把缓冲区数据立即取走PSH标志位同样对发送端起到催促作用在TCP的正常运作中默认会使用一种叫Nagle算法的机制。它的目的是减少网络上小数据包的数量提高网络利用率。简单说它会尽量把多个小的数据块攒在一起等攒到一定大小再一次性发送。但这样会导致一种现象延迟。比如你敲击键盘发送一个字节Nagle算法可能会等一等看有没有更多数据一起发这就产生了延迟。这时候就可以通过设置PSH标志位来告诉TCP协议栈“别等了立刻把这份数据发出去别管Nagle算法了”URG标志位它是紧急指针它表示的是16位紧急指针是否有效如果URG标志位为0这16位紧急指针就无效指针只要具有一定的指向性就可以叫做指针比如索引这里的紧急指针代表的就是偏移量可以把发送缓冲区想象成一个char类型的buffer每一个字节都有自己的下标就是上文讲的32位序号的序号初始序号真实情况下是随机的序号不是从0开始的根据某种算法转成数组下标就是从0开始的偏移量而数组下标就是偏移量紧急指针发送的报文数据的某个字节在数组里的下标通常情况下就是一个字节注意紧急指针里是什么数据根本不重要它只需要存在接收方就知道需要发送中断信号正常情况下接收端是以先进先出如果带了URG接收端就会优先读取对应紧急指针指向的字节为什么需要有紧急指针比如正在上传某段资源时突然要求暂停上传数据按照正常应该是先上传完才能读到要求暂停的报文就需要加上紧急指针让暂停的报文插队接收端就会优先读取这个报文紧急指针的机制需要双方应用层都进行处理也可以建立两条链接一个收发数据一个收发控制命令就是不使用紧急指针确认应答机制ACK确认应答是确保可靠性的机制确认序号就是下一次发送序号的起始序号一旦发送丢包不管是数据报文丢了还是应答丢了发送方都不能一直等下去就需要设置时间间隔一旦超时直接重传这个特定的时间间隔不能太短因为报文传输也需要时间会导致频繁重传报文也不能太长这个特定的时间间隔是浮动的跟网络的健康程度有关因此会选择动态计算这个最大超时时间等待时间就是 500ms*2的n次方-1n为重传的次数意思就是第一次会等500-1ms第二次就会等500*2-1ms直到重传次数达到一定程度发送端tcp就会认为网络或者对端主机出现异常强行关闭连接流量控制简单来讲就是发送端利用接收端的发送过来的窗口大小来控制自己发送报文的速度发送端不能过多依赖重传机制就是一股脑全发出去不管接收方有没有能力全部接受完准备丢啥就传啥因为依赖重传会导致电力、带宽等资源的浪费网络传输也是需要消耗资源的一般窗口大小就是2的16次方但在如今64KB已经太小了就需要有一个窗口扩大因子这个窗口扩大因子M 就在tcp首部的选项中所以实际窗口大小是 窗口字段的值左移M 位滑动窗口tcp具有可靠性就意味着一个报文一旦把它发送出去了在收到应答之前这个报文都不会被发送方丢弃这个丢弃的时间点应该在收到应答之后。就意味着发送的数据会持续留在发送缓冲区内部直到收到应答报文同时为了效率发送端tcp往往会选择一次性发送一批报文假设一个报文携带1460字节初始序号这里假设为0总共要发送9000字节发送端会根据窗口大小这里假设接收端剩余空间大小为4000字节就会一次性发送三个报文1460、1460、1080序列号分别为014602920。发送完成之后发送端tcp就会等待不再发送直到接收方传来ack而滑动窗口指的就是这个根据窗口大小在发送缓冲区内划定的这个区域就是这个大小为4000字节的区域每一次发送都会发送滑动窗口内的一批报文大致可以如图这样抽象理解上面的管道就是发送缓冲区滑动窗口就是其中的一部分区域有了滑动窗口发送缓冲区就可以划分为三个区域滑动窗口的工作过程1、正常工作过程最开始的时候滑动窗口minACK_win真实发送的数据量ACK_win对方发来的剩余接收缓冲区大小start_win滑动窗口起始位置收到确认应答之后比如收到2001窗口的左侧就应该指向2001而直接发送的数据范围应该以对方的接受能力为主就代表右侧应该等于 起始位置 ACK_WIN这其实就是流量控制的底层实现当滑动窗口大小为0就是startend就是对方接收缓冲区剩余空间已经为0了窗口大小也会变大只要对方的剩余接收缓冲区变大滑动窗口永远只会向右滑动不管是滑动窗口的左指针还是右指针跟算法里面的滑动窗口类似2、异常工作过程接收方只有收到报文才会返回确认应答就是说如果发送方收到6001就是6001之前的报文都送到了如果接收方没收到某一个报文只能确认应答这个报文之前的序号就是说如果上一个报文序号是400140011000数据大小5001但是后续没收到5001序号的报文而是收到6001接收端不能发送确认序号为7001因为5001序号没收到所以只会继续返回确认序号5001的ack报文就会导致主机A收到多个5001只要达到3个就会把这个一定丢失的报文给重传了然后从回来的确认应答判断还丢了哪些这就是快重传机制如果应答报文达不到三个触发快重传就会进行超时重传确认序号序号之前的报文都已经确认收到这种确认序号的定义就会保证滑动窗口的左侧不会跳过任何没有经过确认的报文发送完应答之前数据就一定会在窗口内部收到应答之后滑动窗口的数据就会划分到左侧区域本质就是删除数据滑动窗口有没有可能越界可以把缓冲区当做一个环形数组不会发生越界滑动窗口就是流量控制的底层逻辑拥塞控制可以发现之前的策略 都是和另一端有关其实除了考虑接收端的接受能力还要考虑网络情况为了应付网络情况就有了拥塞控制注意只能解决能力范围以内的网络问题如果是路由器坏掉这种问题是没法应付的丢包 —— 这就是一种网络问题如果只有极少数报文丢包就是正常情况直接重传就好如果丢了大量的报文就可以判定网络出现问题了就会认为是发生了网络拥塞问题注意tcp只能判定原因是这个它只能推测出这一种情况比如大量报文积在了路由器此时就不能直接进行重传了因为会加重网络的拥堵情况此时就要进行拥塞控制如何进行拥塞控制前提网络已经拥塞了它会先重发一点点报文比如一个报文如果没收到应答就会继续发一个报文如果收到了应答就会持续增加报文数量这就是 慢启动 机制拥塞窗口可以当做一个整数报文数如果超过这个数就有可能会发生网络拥塞它就是用来衡量网络拥塞的指标用来衡量网络接收能力的指标目前为止已经有三个窗口了三个窗口的关系滑动窗口min(win窗口拥塞窗口注暂时不考虑发送报文数量少的情况所以不考虑报文数量的影响怎么做到拥塞时控制发的报文数量的缩小拥塞窗口的大小就可以控制发送的报文的数量慢启动就是以指数级增长增加发送的报文数量当然还要考虑对方的接受能力不可能让它一直指数级增长至于为什么叫做慢启动就是因为它前期很慢而且后期速度恢复的很快 能够在可靠性和效率中寻找平衡点他会有一个阈值ssthresh)超过这个阈值就会以线性方式增长如果超过阈值之后线性增长时又遇到了网络拥塞阈值就会减少成为此时拥塞窗口的一半然后拥塞窗口的大小重新为1拥塞窗口应不应该是变化的拥塞窗口大小一定是变化的因为网络健康状态是变化的发生拥堵的上限一定是变化的那一开始拥塞窗口是多大拥塞控制就是一种为了保证可靠性的策略但同时也考虑了效率问题的策略延迟应答它是为了提高 tcp传输效率而出现的机制比如通过时间延迟或者只有当发送方发来下一个报文达到某种数量时才进行应答延迟的这段时间应用层可能就拿走了接受缓冲区内一部分数据使得可接收范围变大延迟应答时就有一定概率 给发送方更新一个更大的窗口大小捎带应答就是把要发送的应答和要发送的数据一起发送过去主要出现在tcp双方互相发消息是为了提高报文发送的效率比如三次握手的时候最后一次ack发出客户端就会认为已经建立好连接就可以捎带应答面向字节流就是读的次数和写的次数没有关系区分报文完全靠应用层所以就需要应用层的协议比如HTTP比如 json 的序列和反序列化面向数据报就是写多少次读就得多少次
返回列表