1. TCP协议概述:它在协议栈里到底扮演什么角色?
各位好,我是老张。做网络这行十几年了,每次带新人我都会先问一个问题:TCP到底在协议栈的哪一层? 很多人脱口而出“传输层”,但你再追问一句“传输层干嘛的”,他就开始支支吾吾了。
今天咱们就把TCP的底裤扒干净。从它在协议栈的位置,到它那几个核心特点,再到报文段里每个字段是干什么的——我都会结合我踩过的坑来讲。
1.1 TCP在协议栈中的位置
先看这张图,我手绘的,凑合看。它把TCP在五层模型里的位置标得清清楚楚:
说白了,TCP工作在传输层,夹在应用层和网络层中间。应用层把数据扔给TCP,TCP负责把它可靠地送到对端——至于中间IP层怎么路由、链路层怎么传帧,TCP不管,那是下面几层的事。
我记得刚入行时有个同事问我:“TCP和IP到底谁重要?” 我当时的回答是:IP负责找到路,TCP负责把货完好无损地送到。缺一个都不行。
1.2 TCP的三个核心特点
TCP为什么能成为互联网的基石?就靠这三个特点:
- 面向连接:通信前必须先建立连接,通信完必须释放连接。不是像UDP那样直接扔数据包。
- 可靠传输:保证数据不丢、不乱、不重复。靠的是确认、重传、序号机制。
- 全双工:双方可以同时收发数据。你发你的,我发我的,互不干扰。
我个人的经验: 很多人觉得“面向连接”就是TCP比UDP好。其实不是。我做过一个实时视频项目,延迟要求极高,丢几帧无所谓——这时候UDP反而更合适。TCP的可靠是有代价的,它要维护状态、要重传、要保序,这些都会增加延迟。
你想想看,如果TCP不是全双工的,那HTTP的请求-响应模型就得排队——你发完请求,等服务器回完,你才能发下一个。那网页加载得多慢?
1.3 TCP报文段首部格式——每个字段都是血泪史
下面这张表,我建议你存下来。每次抓包分析时,你都会回来翻它。
| 字段 | 长度 | 作用 |
|---|---|---|
| 源端口 | 16位 | 发送方的端口号,用于标识上层应用 |
| 目的端口 | 16位 | 接收方的端口号 |
| 序号 | 32位 | 本报文段数据的第一个字节的序号 |
| 确认号 | 32位 | 期望收到的下一个字节的序号 |
| 数据偏移 | 4位 | 首部长度,单位是4字节 |
| 标志位 | 6位 | URG/ACK/PSH/RST/SYN/FIN |
| 窗口 | 16位 | 接收窗口大小,用于流量控制 |
| 校验和 | 16位 | 检验首部和数据的完整性 |
| 紧急指针 | 16位 | 仅在URG=1时有效,指向紧急数据的末尾 |
咱们一个一个说。
1.3.1 源端口和目的端口
这两个字段加起来,再加上源IP和目的IP,就构成了一个四元组——唯一标识一条TCP连接。我在排查连接问题时,第一件事就是看四元组对不对。曾经有一次线上故障,就是因为NAT把源端口改了,导致服务端回包找不到对应的连接。
1.3.2 序号和确认号
这是TCP可靠传输的核心。序号是本报文段数据的第一个字节的编号,确认号是期望收到的下一个字节的序号。
举个例子:假设A给B发了序号为100、长度为200字节的数据。B收到后,确认号就是300(100+200)。如果B只收到了前100个字节,确认号就是200——A就知道后面的数据丢了,得重传。
我曾经踩过的坑: 有一次写一个自定义协议,把序号理解成了“报文段的编号”,结果对端一直收不到正确的确认。查了两天才发现——序号是字节级别的,不是报文段级别的。这个细节搞错了,整个可靠传输就全乱套了。
1.3.3 数据偏移
这个字段只有4位,但很重要。它表示TCP首部的长度,单位是4字节。因为TCP首部有可变长的选项字段,所以需要这个字段来告诉接收方:首部到哪结束,数据从哪开始。
4位能表示的最大值是15,所以首部最长是15×4=60字节。固定部分20字节,选项部分最多40字节。
1.3.4 标志位——TCP的“开关”
6个标志位,每个都有自己的使命:
- URG:紧急指针有效。告诉接收方“这里有紧急数据,先处理”。
- ACK:确认号有效。除了SYN报文,几乎所有报文ACK都置1。
- PSH:推送。让接收方尽快把数据交给应用层,别等缓冲区满了。
- RST:复位。连接出错了,强制断开。
- SYN:同步。用于建立连接时的握手。
- FIN:结束。用于关闭连接。
你抓包时,最常看到的就是SYN、ACK、FIN这三个。RST一般出现在异常场景——比如你连一个不存在的端口,对端会回RST。
1.3.5 窗口
16位的窗口字段,用于流量控制。接收方告诉发送方:“我的接收缓冲区还剩多少空间,你看着发。”
窗口大小最大是65535字节。但现在的网络环境,这个值经常不够用——所以有了窗口缩放选项(Window Scale),可以把窗口放大到1GB级别。
1.3.6 校验和
TCP的校验和覆盖了首部+数据+伪首部。伪首部包含源IP、目的IP、协议号、TCP长度——这是为了检测IP层有没有把数据送错地方。
嗯,这里要注意:校验和是强制要求的,不像UDP那样可选。如果校验和出错,TCP直接丢弃这个报文段,不会通知发送方——等着超时重传吧。
1.3.7 紧急指针
这个字段现在用得很少了。它只在URG=1时有效,指向紧急数据的最后一个字节。我记得早年做Telnet协议时用过它——用户按Ctrl+C中断,需要立即发送,不能等缓冲区满了再发。
总结一下: TCP报文段首部虽然只有20字节的固定部分,但每个字段都承载着TCP的核心机制。序号和确认号支撑可靠传输,窗口支撑流量控制,标志位支撑连接管理——后面的课程,我们会一个一个展开讲。