1. TCP协议概述:它在协议栈里到底扮演什么角色?

各位好,我是老张。做网络这行十几年了,每次带新人我都会先问一个问题:TCP到底在协议栈的哪一层? 很多人脱口而出“传输层”,但你再追问一句“传输层干嘛的”,他就开始支支吾吾了。

今天咱们就把TCP的底裤扒干净。从它在协议栈的位置,到它那几个核心特点,再到报文段里每个字段是干什么的——我都会结合我踩过的坑来讲。

1.1 TCP在协议栈中的位置

先看这张图,我手绘的,凑合看。它把TCP在五层模型里的位置标得清清楚楚:

TCP/IP 五层协议栈 应用层(HTTP / FTP / SMTP) 传输层 TCP(可靠) / UDP(不可靠) 网络层(IP / ICMP) 数据链路层(以太网 / PPP) 物理层(光纤 / 双绞线 / 无线电) 用户态 内核态 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的核心机制。序号和确认号支撑可靠传输,窗口支撑流量控制,标志位支撑连接管理——后面的课程,我们会一个一个展开讲。