计算机网络 之 【TCP协议】(TCP的核心定位与控制本质、TCP报文结构)

目录

[1. TCP的核心定位与控制本质](#1. TCP的核心定位与控制本质)

全双工与半双工

2.报文结构与解包

报头关键字段

16位窗口大小与流量控制

保留字段与校验和字段(了解)

32位序号与32位确认序号

标记位


IP网络本身提供的是不可靠传输服务,数据在传输过程中可能出现重复 (同一数据收到多次)、乱序 (后发的先到)、丢包(数据中途丢失)等问题,TCP协议正是为了解决这些不可靠问题而设计的,通过序号去重和排序、超时重传、确认应答等机制,在不可靠的IP层之上构建可靠的传输服务

1. TCP的核心定位与控制本质

控制本质 :TCP是一个传输控制协议 ,其"控制"体现在自主决定数据的发送时机、发送量以及出错后的处理方式

全双工通信 :一个TCP连接对应一个文件描述符,在内核中维护一对缓冲区(发送缓冲区和接收缓冲区),支持同时读写

系统调用本质: write、read、send、recv等接口的本质是拷贝函数,负责在用户级缓冲区和内核级缓冲区之间拷贝数据,而非直接发送数据到网络

全双工与半双工

全双工是指在通信的任意时刻,数据可以在两个方向上同时进行传输,互不干扰。就像一条双向四车道的高速公路,车辆可以同时向两个方向行驶

TCP协议支持全双工通信,这意味着:通信双方可以同时发送和接收数据,发送和接收操作互不阻塞,每个方向都有独立的流量控制和确认机制
半双工是指在通信的任意时刻,数据只能在一个方向上传输,不能同时双向传输。就像对讲机一样,一方说话时另一方只能听,必须等对方说完才能发言

关键特征: 通信双方可以双向传输 数据,但不能同时进行 ,需要切换传输方向 ,存在切换开销,同一时刻只有一个方向的数据流

特性 半双工 全双工
传输方向 单向交替传输 双向同时传输
实时性 较差,需要等待 好,无等待
效率 低(方向切换开销)
实现复杂度 简单 复杂
硬件成本 低(如单根信号线) 高(如双绞线、光纤)
典型应用 对讲机、RS-485总线 电话、以太网

2.报文结构

报文组成 :TCP报文由标准报头(20字节)选项有效载荷组成

报头关键字段

  • 源/目的端口号(各16位):标识通信的进程(数据是从哪个进程来, 到哪个进程去)
  • 32位序号 / 32位确认序号:用于保证数据的按序到达、去重以及确认应答
  • 4位首部长度:以4字节为单位,表示报头(含选项)的总长度,用于分离报头和有效载荷
  • 16位窗口大小:用于流量控制,表示本端接收缓冲区剩余的可用空间
  • 6个标记位:用于区分报文类型(如SYN建立连接、ACK确认、FIN断开连接、RST重置连接等)
  • 报头与有效载荷如何分离然后交付给上层

TCP通过固定20字节的标准报头(其中"4位首部长度"字段以4字节为单位指明整个报头的总长度)来实现报头与有效载荷的分离,内核解析时先读取固定长度的基础报头,再根据该字段计算出选项区的长度,从而精准定位到有效载荷的起始位置,完成解包并向上层交付

  • 注意事项

TCP通信中,无论是数据报文、纯ACK报文,还是SYN、FIN等控制报文,都必须携带完整的TCP报头,因为报头中的端口号、序号、确认序号、窗口大小、标志位和校验和等信息是保证连接状态维护、数据可靠性、流量控制和报文正确性所必需的,缺一不可

16位窗口大小与流量控制

rwnd(接收窗口)是接收方通过 TCP 报头中 16 位窗口字段通告给发送方的流量控制窗口大小

由于 16 位字段最大只到 65535,TCP 使用窗口缩放选项(Window Scaling) 扩展:

概念 说明
报头中的 16 位值 原始值(0 ~ 65535)
缩放因子 握手时协商(0 ~ 14),表示左移位数
实际 rwnd 报头值 << 缩放因子(最大约 1GB)

16位窗口大小是TCP报头中用于流量控制的核心字段,它表示本端接收缓冲区的剩余空间大小 ,取值范围0-65535字节,发送方根据这个值动态调整发送速度,确保不会超过接收方的处理能力,从而实现发送速率与接收速率的匹配
流量控制是TCP根据接收方的处理能力动态调整发送速率的机制,通过窗口大小字段告知发送方还能接收多少数据,使发送方能够稳定地按量发送,避免因发送过快导致接收方缓冲区溢出而丢包,确保数据能够平稳地从发送端传输到接收端

如果不做流量控制 ,发送方盲目发送数据会导致接收方缓冲区溢出而大量丢包,进而引发频繁的超时重传 ,这些被丢弃的数据已经占用 了路由器的缓存、链路的带宽和CPU处理资源 ,重传又再次占用同样的资源,形成双重浪费,因此流量控制不是技术上能不能做的问题,而是资源利用上好不好、优不优的问题

保留字段与校验和字段(了解)

保留字段是TCP报头中一个6位的预留字段,目前必须全部置为0,供未来协议扩展使用

校验和是TCP报头中一个16位的字段,用于检测TCP报文在传输过程中是否发生损坏

32位序号与32位确认序号

复制代码
序号(本报文数据位置)     确认序号(对方数据接收进度)
        ↓                              ↓
  保证按序到达             实现累积确认,允许ACK少量丢失
        ↓                              ↓
        └──────────┬───────────────────┘
                   ↓
           捎带应答成为可能
        (一个报文同时做两件事)
                   ↓
          提高传输效率,减少报文数量

序号能够保证一批一批数据的按序到达。 发送方为每个数据块分配唯一的序号,接收方根据序号对乱序到达的数据进行重排序,确保最终交付给应用层的数据与发送顺序一致,从而解决了IP网络固有的数据乱序问题
TCP序号的本质是字节流的编号,每个字节都被赋予一个序号

(1)序号的本质:发送数据块的第一个字节的序号

复制代码
// 正确的定义
struct tcp_sequence_correct {
     * 例如:
     * - 初始序号为 1000
     * - 发送 100 字节数据
     * - 数据占用序号:1000-1099
     * - 本报文的序号 = 1000(第一个字节)
     * - 下一个待发送的序号 = 1100
     */
    u32 seq;    // 当前报文第一个字节的序号
};

(2)通过首尾字节标识数据

复制代码
// 发送方发送数据块
void tcp_send_data_chunks() {
    /*初始序号 = 1000
     * 
     * 数据块1:发送100字节
     * 序号 = 1000(数据占用序号1000-1099)
     * 
     * 数据块2:发送200字节
     * 序号 = 1100(数据占用序号1100-1299)
     * 
     * 数据块3:发送50字节
     * 序号 = 1300(数据占用序号1300-1349)
     */
}

// 序号与数据的映射
struct data_mapping {
    u32 start_seq;      // 数据起始序号
    u32 end_seq;        // 数据结束序号 = start_seq + len - 1
    void *data;         // 实际数据内容
};

(2)确认序号的计算规则

复制代码
/*
 * 确认序号 = 已收到的最大连续字节序号 + 1
 * 
 * 具体场景:
 * 1. SYN报文:确认序号 = seq + 1(SYN占用1个序号)
 * 2. FIN报文:确认序号 = seq + 1(FIN占用1个序号)
 * 3. 普通数据报文:确认序号 = seq + len
 * 4. 纯ACK报文:
 *    纯ACK本身不携带数据,其确认序号反映的是接收方的数据接收进度。
 *    收到能扩展连续区间的数据时增长,收到重复或乱序数据时保持不变。
 */

意义:确认序号之前的所有数据都已收到,下一次发送请从确认序号开始

序号消耗规则
报文段 是否消耗序号 原因
SYN 消耗1 连接建立需要可靠的序号同步
FIN 消耗1 关闭连接需要可靠的结束通知
RST 不消耗 异常终止不需要可靠性保证
ACK 不消耗 纯确认不携带数据
普通数据 按字节数消耗 每个字节对应一个序号

(2)确认序号的累积确认特性

累计确认是指确认序号表示该序号之前的所有数据都已经成功收到,接收方不需要对每个报文单独确认,一个ACK可以确认多个报文

复制代码
void cumulative_acknowledgment() {
    /*
     * 场景:发送方发送了3个数据块
     * 数据块1: seq=1000, len=100  → 占用1000-1099
     * 数据块2: seq=1100, len=200  → 占用1100-1299
     * 数据块3: seq=1300, len=50   → 占用1300-1349
     * 
     * 情况1:只收到数据块1
     * ACK = 1100  → 确认1000-1099已收到
     * 
     * 情况2:收到数据块1和数据块3(数据块2丢失)
     * ACK = 1100  → 还是只确认到1100
     * 因为数据块2(1100-1299)没有收到,确认序号不会跳跃到1300
     * 
     * 情况3:收到所有数据块
     * ACK = 1350  → 确认所有数据(1000-1349)都已收到
     */
}

确认序号允许应答有少量的丢失。 由于确认序号是累积的,只要后续的ACK 到达,就能覆盖之前丢失的ACK。例如ACK1丢失但ACK2到达,ACK2确认了更大范围的数据,自动包含了ACK1的确认信息,因此少量的ACK丢失不会影响可靠传输
如果前面的报文丢了,即使后面的收到了,ACK确认序号只能是前面的那个,这保证了确认序号是线性的,不会跳跃,进而使滑动窗口连续滑动
需要有两个序号是为了:捎带应答时既是应答又能携带数据。 两个序号字段------一个标识本报文携带的数据位置,另一个确认对方的数据接收进度------使得一个报文可以同时完成数据发送和数据确认两项任务,避免了为每个ACK单独发送一个报文,大大提高了传输效率

标记位

TCP通过标准6个标记位(SYN、ACK、FIN、RST、PSH、URG)的不同组合来区分报文类型(如连接请求、确认、关闭、重置等),从而决定接收方的处理机制

标记位 全称 长度 功能说明 典型使用场景
SYN Synchronize 1 bit 请求建立连接,同步序号 三次握手第一步(客户端 → 服务端)
ACK Acknowledgment 1 bit 确认序号有效 除第一个SYN报文外,几乎都置1
FIN Finish 1 bit 通知对端本端关闭连接 四次挥手,主动关闭方发送
RST Reset 1 bit 强制终止 连接 连接异常、端口未打开、超时、半开连接
PSH Push 1 bit 提示接收端立即从缓冲区取走数据 实时交互数据、telnet、ssh
URG Urgent 1 bit 紧急指针有效 发送带外数据(OOB),如中断指令
报文段 标志位 作用 是否携带数据 是否消耗序号
复位报文段 RST=1 异常终止连接、拒绝非法请求 通常无 不消耗
同步报文段 SYN=1 建立连接、协商初始序号 可选(如选项字段) 消耗1个
结束报文段 FIN=1 正常关闭连接、表示数据发送完毕 通常无 消耗1个

连接重置(RST)常见原因:

  1. 连接请求端口未开放(如 connect 到未监听端口);
  2. 半开连接(一方崩溃后对端发送数据);
  3. 收到不属于任何连接的报文;
  4. 超时未收到响应,强制复位;
  5. 应用层调用 setsockopt 带 SO_LINGER 且 timeout=0

tcp保证可靠性,但是tcp允许连接建立失败

紧急指针 / URG

  1. TCP 紧急数据默认最多 1 字节(标准实现中)
  2. 通过 send(..., MSG_OOB) 发送
  3. 场景:服务器长时间繁忙,客户端发送 Ctrl+C / 中断 等高优先级指令

服务端管理连接的成本(描述 + 组织)

  1. 内核或应用层会用结构体(如 struct tcp_sock / struct socket)描述每个连接,包含:状态(ESTABLISHED, FIN_WAIT1...),序号、确认号,发送/接收缓冲区,定时器(重传、保活、时间等待),拥塞控制参数
  2. 这些结构体被组织在哈希表、连接表、队列中,维护开销包括内存、CPU、定时器管理

3.基于TCP应用层协议

  • HTTP
  • HTTPS
  • SSH
  • Telnet
  • FTP
  • SMTP

当然, 也包括用户写TCP程序时自定义的应用层协议

相关推荐
pingao1413781 天前
智联未来:4G温湿度传感器如何重塑数据监测新生.态
大数据·网络·人工智能
@insist1231 天前
信息安全工程师-核心考点梳理:第 1 章 网络信息安全概述
网络·软考·信息安全工程师·软件水平考试
无巧不成书02181 天前
零基础Java网络编程全解:从核心概念到Socket实战,一文打通Java网络通信
java·开发语言·网络
isyangli_blog1 天前
6. 使用Mininet创建星型网络拓扑,手动设置流表项
网络
skywalk81631 天前
发现Kotti项目的python包Beaker 存在安全漏洞
开发语言·网络·python·安全
干洋芋果果1 天前
python-can 虚拟车速通信
网络
学习3人组1 天前
成品批次信息及全链路溯源汇报材料(大客户专用)
网络·erp·mes
想唱rap1 天前
UDP套接字编程
服务器·网络·c++·网络协议·ubuntu·udp
AC赳赳老秦1 天前
OpenClaw多平台部署:Windows+Linux跨系统协同,实现全场景覆盖
linux·服务器·前端·网络·windows·deepseek·openclaw
rannn_1111 天前
【Redis|原理篇2】Redis网络模型、通信协议、内存回收
java·网络·redis·后端·缓存