ARTICLE DETAIL

资讯详情

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

TCP报头全解-序号确认应答与可靠性是一个准数

TCP报头全解-序号确认应答与可靠性是一个准数 TCP 报头全解序号、确认应答与可靠性是一个准数我的github(https://github.com/xcx55/ubuntu-linux-project)感谢各位大佬参观我的github源笔记TCP底层1/226-9-23、分离的不同内核和应用层26-9-23、TCP底层326-9-24、TCP结构体-序号机制/可靠性本质26-9-25、TCP底层426-9-26、缓冲区长这个样子/网络通信辅助图26-9-26UDP 底层看完轮到 TCP 了。同样是传输层TCP 报头从 8 字节膨胀到 20 字节起——多出来的每一个字段都是在为可靠付费。这一篇把 TCP 报头逐字段拆开重点啃两件事序号机制到底在解决什么以及笔记里最锋利的一句话——可靠性本质是一个准数。一、先看报头怎么分离内核和应用层的分法不一样复习一下两种分离思路应用层协议自定义协议、HTTP 等依据特殊字符分离数据和报头\r\n、空行内核传输层报头本质是结构体——依据传输而来的、提前得知的结构体格式按字节切报头是本层要使用的数据有效载荷是要传给上层的数据。TCP 报文按字节一刀切前 20 字节是定长报头选项剩下全是载荷。有没有取消对齐都能拿到因为每个字段的位置是写死的。二、为什么 TCP 没有总长度字段UDP 却有UDP 报头里有一个 16 位 UDP 长度报头载荷总长度TCP 却没有——为什么UDP 是数据报发多少传多少收多少——长度字段有意义TCP 是面向字节流内部自己敲定一次传多少告诉接收方这一批多少字节没有意义——反正每一批都要整体交付上层一次读取得到的到底完不完整TCP 协议不保证由上层自己调控。带上总长度不是不可以只是多了字段、会被优化掉——历史遗留问题。所以 TCP 的报文长度 20 报头 n 载荷UDP 是 8 N。还有一个精妙的设计TCP 的 20 字节不包含选项。那选项长度怎么表达——4 位首部长度字段其数值 ×4 报头总长20选项。没有整体长度只有头长度——剩下的都是载荷算都算得出来。三、可靠性的哲学100% 可靠的协议根本不存在先做一道逻辑题A 给 B 发报文B 要应答但 B 怎么知道 A 收到了应答A 要再确认……逻辑递归无穷无尽。结论互联网里没有 100% 可靠的协议——最新的那条报文永远无法确认对方收到没有但历史报文的应答是 100% 可靠的——你收到了对一个报文的确认说明它一定到了。所以 TCP 的策略是保证数据报文的可靠性即可——让想要保证可靠性的报文尽快变为历史发出 → 得到应答 → 历史报文 → 确定送达。丢包为什么只能规定不能根除丢包有两种情况1、数据没传到对面2、返回的应答丢了。站在发送方视角这两种情况的表现一模一样——都收不到应答。发送方永远无法区分是数据丢还是应答丢。这么多人想不到别的办法吗不是想不到是逻辑上无解。所以 TCP 干脆换了个定义TCP 不是保证数据一定被对方收到而是保证发送方对一个确定的结果有准数——收到了应答就继续发下一个没得到应答就执行其他策略重传。可靠性 准数。这是整套 TCP 机制的地基。四、32 位序号一个字段四个用途为了配合准数序号seq登场。它一个字段身兼数职去重应答丢了会触发重传接收方会收到重复报文——32 位序号的核心用途之一就是去重按序排列双方并行收发效率高但并行造成乱序——接收方按序号升序排回去这也解释了为什么序号要 32 位序号必须够多捎带应答的基础TCP 双方地位对等对称发送和应答的字段是解耦的——任何一方都可能既发数据又捎带应答配合重传优化见下面的确认序号。每个字节都有编号辅助图里的比方很传神每一个字节就是一个从 1 开始编号的躺着的人。序号 当前报文数据第一个字节的编号算个账报文载荷 3 字节、seq2000 → 覆盖 2000、2001、2002最后字节编号 2002→下一个想要的字节编号是 2003——接收方等的就是它。五、确认序号一个天才约定确认序号ack 发出序号 1含义是我想要的下一个字节的序号从这开始。它的威力在批量发送时显现发送方发出 1000 2000 3000 4000 收到应答 1001 2001 4001 ↑ 3001 没来 → 3000 那个报文丢了一个应答缺口直接定位丢了哪一段——发送方不用全部重传只补缺口。而且这是接收方和发送方共同遵守的约定返回的确认序号本身就是一种报文序号规定有效减少发送方的重发次数。六、16 位窗口大小可靠性还有另一半——对面的内存TCP 缓冲区长这个样子发送缓冲区/接收缓冲区都是字节编号的队列。现在想一个问题接收方内存快满了TCP 发来的数据好不容易到了操作系统却不要了——是不是效率太低你只能靠应答让对方反复重传更慢。所以设计了流量控制接收方衡量自己的接收能力 接收缓冲区的剩余空间大小怎么告诉发送方应答报文里的 16 位窗口大小字段注意方向它填写的是发送方视角的对方容量——我构建的报文都是发给对方的所以我必须持续衡量对面的接收容量动态调整发送流量不能太慢也不能太快——既保效率也保准数。笔记里的感悟值得原样保留在我看来可靠性不止体现在报文安全更体现在对面主机内存安全这另一面流量控制护的是收方的内存。七、6 个标志位给报文定型20 字节报头里还有 6 位标志位用 bit 位区分报文类型ACK应答报文普通应答 / 捎带应答——捎带是常数级的包传递次数优化传递报文本身属于 IO能减少次数最好SYN建立连接三次握手——TCP 发数据前必须先建连接因为创建连接是有成本的双方 OS 里都要维护struct tcp_sock结构体要为可靠性做大量载入和管理工作FIN关闭连接四次挥手——断开往往是一方一厢情愿另一方不行我还有数据RST和四元组相关连接出问题时重置后面细说PSH触发中断在软件层立即唤醒 task_struct 调度运行——让接收进程马上来读缓冲区里已经连接好的数据URG 16 位紧急指针最少见的一对。URG 是开关紧急指针本质是偏移量——“广义上具有指向性的东西都可以叫指针”。它指向本次报文载荷中的紧急数据带外数据可以不被按序、优先读取接收它的函数也不同于 read。紧急数据为什么存在为了插队——比如取消上传这种控制命令必须优先于普通缓冲区数据抵达对面。但笔记也补了句大实话一般用得少建立两个 fd 不就完了总结报头分离应用层靠特殊字符内核靠结构体字节布局TCP 报文 20 报头不含选项 n 载荷UDP 带总长因为它是数据报TCP 不带因为字节流内部自己定只有首部长度×4没有 100% 可靠协议——最新报文永远无法确认TCP 保证的是历史报文可靠可靠性 准数丢包无法区分数据丢还是应答丢 → TCP 定义改为发送方得到确定结果重传导致重复seq 去重并行导致乱序seq 排序序号是第一个字节的编号确认序号 想要的下一个编号一个缺口定位一段丢失16 位窗口 流量控制填的是对面的接收能力动态调整——可靠性还有对面内存安全这一半6 标志位ACK/SYN/FIN/RST/PSH/URG紧急指针紧急数据 插队的带外数据。下一篇把这些字段用起来——三次握手到底在验证什么、四次挥手为什么要四次以及 accept/connect 为什么不参与握手。
返回列表