一、 TCP/IP 分层模型与基础通信结构
1. 网络分层参考模型

-
应用层:负责应用程序的网络访问(如 DNS, HTTP, FTP, 序列化/反序列化)。
-
表示层/会话层:在 TCP/IP 模型中通常合并入应用层。
-
传输层:操作系统内核实现,负责端到端的数据传输控制(包含 TCP, UDP 等)。TCP 全称为"传输控制协议",意为对数据的传输进行详细的控制。
-
网络层 (互联网层):负责 IP 寻址与路由选择(ARP, IP, ICMP)。
-
数据链路层与物理层:网卡层驱动与硬件设备。
2. 底层通信机制与缓冲区

-
双缓冲区模型 :主机间的通信本质是应用层通过系统调用(如
read/write或send/recv),将数据在用户区与 OS内核的 TCP 缓冲区(发送缓冲区 / 接收缓冲区) 之间进行拷贝。 -
字节流管理 :发送端往发送缓冲区写入的数据,TCP 会天然地为每一个字节进行编号,这就是序列号 (Sequence Number) 的底层来源。
二、 TCP 首部格式与流量控制
1. TCP 首部结构与核心字段

TCP 报头标准长度为 20 字节,最大为 60 字节(计算范围:4×15=604 \times 15 = 604×15=60 字节,由4位首部长度决定)。
-
源/目的端口号 (16位):表示数据从哪个进程来,到哪个进程去。
-
序号 (32位) 与确认序号 (32位):用于保证数据的有序性和可靠性。
-
首部长度 (4位) 与 保留位 (6位)。
-
标志位 (Flags, 6位) :URG, ACK, PSH, RST, SYN, FIN。本质是报头中的比特位(Linux内核
struct tcphdr中定义)。 -
窗口大小 (16位) :用于流量控制。
-
校验和 (16位):发送端填充,包含 TCP 首部和数据部分(CRC校验)。接收端校验不通过则丢弃。
-
紧急指针 (16位):标识紧急数据的范围。
2. 流量控制模型
-
核心逻辑:发送数据一定要上报,必须进行合理的流控以保证效率。如果对方接收缓冲太小,发送极速会导致丢包。
-
窗口机制 :根据客观事实,发送端必须尽早知道对方的接收能力。"16位窗口大小"填的就是自己(发送该报文一端)接收缓冲区剩余空间的大小。双方通过报文交互,实时协商接收能力。
三、 TCP 标志位解析 (Flags)
接收方收到的 TCP 报文有不同的类型,必须根据标志位采取不同的处理方式。
1. 连接管理标志 (SYN, FIN, ACK)
-
SYN (同步标志位) :请求建立连接。前两次握手只有 TCP 报头,不能携带数据(因为连接尚未完成),但已经可以进行双方接收能力的协商。
-
FIN (结束标志位):通知对方本端要关闭连接。
-
ACK (确认标志位):确认号是否有效。常规应答报文中 ACK 标志位几乎都设为 1(可以是纯应答,也可以是应答+数据)。
2. 数据与异常控制标志 (PSH, RST, URG)
-
PSH (推标志):提示接收端应用程序立刻从 TCP 缓冲区把数据读走。
-
RST (复位标志) :异常重置报文。通信过程中连接出现任何认知不一致或异常(如浏览器
ERR_CONNECTION_RESET),对方会要求重新建立连接。 -
URG (紧急标志) :表示紧急指针有效。用于处理带外数据 (MSG_OOB)。紧急数据通常只有一个字节,不占常规空间,绕过常规字节流接收队列,允许应用程序"插队"优先读取处理。
四、 序号机制、可靠性与超时重传
1. 序列号 (Seq) 与确认应答 (ACK)
-
编号与分割 :TCP 将每个字节的数据都进行了编号。例如发送
1~1000字节,下一个报文发送1001~2000字节。 -
确认应答 :每一个 ACK 都带有对应的确认序列号,含义是:"我已经收到了哪些数据,下一次请从哪里开始发" (例如收到
1~1000,确认序号就是1001)。
2. 丢包判定与去重机制

-
无法 100% 保证到达:发送方无法保证数据一定能到达对方,也无法确认是"数据丢了"还是"应答丢了"。
-
判定标准 :只要在特定的时间间隔内收不到应答(超时),就缺省判定为报文丢失,触发重传。
-
去重机制 :如果是 ACK 丢失导致发送方重传,接收方会收到重复数据。此时依靠序列号进行去重。
3. 动态超时重传时间 (RTO)
-
时间设定难题:网络状态是变化的(RTT 往返时间不同)。设得太长影响效率,设得太短导致频繁发重复包。
-
动态计算策略 :Linux 中以 500ms 为一个单位进行控制。如果重发得不到应答,等待时间按指数形式递增 (2×5002 \times 5002×500ms -> 4×5004 \times 5004×500ms -> ...)。累计到一定次数仍无响应,则强制关闭连接。
五、 TCP 连接管理与状态机
建立连接是有时间与空间成本的(OS 需要用 struct Link 等数据结构来管理连接)。

5.1 三次握手 (建立连接)
-
过程 :客户端
connect发起,OS 自动完成握手。服务端accept不参与底层握手过程,只负责从内核队列中取出建立好的连接。 -
本质:原本是 4 次握手(双方各发起一次 SYN 和 ACK),但服务端的 SYN 和 ACK 合并为了一次报文,从而变成了 3 次握手(稍带应答)。
-
为什么是三次?(必须是奇数次) :以最小成本,100% 验证双方通信信道通畅,确认网络能够支持全双工通信。(类比男女朋友结为夫妻,需要双方意愿且外部条件满足)。
-
安全隐患:由于服务端必须无脑接受客户端的连接请求,因此容易引发资源消耗或安全问题(如 SYN 泛洪攻击)。
5.2 四次挥手(断开连接)
-
概念解释:
-
全双工通信 (Full-Duplex):指数据在通信双方之间可以同时进行双向传输的工作模式。TCP的本质即是建立并维护一个可靠的全双工通信。
-
四次挥手:TCP断开连接的过程。断开连接的本质是"建立双方断开连接的共识"。
-
-
详细笔记:
-
TCP 状态流转标识 :
CLOSED,LISTEN,SYN_SENT,SYN_RCVD,ESTABLISHED,FIN_WAIT_1,FIN_WAIT_2,TIME_WAIT,CLOSE_WAIT,LAST_ACK。 -
断开连接(四次挥手)过程:
-
C->S:客户端表示"我要发的数据已经发完了,我要和你断开"(发送 FIN)。
-
S->C:服务端表示"我也收到断开连接请求了"(返回 ACK)。此时服务端可能还有数据要发送。
-
S->C:服务端表示"我也发完了,我也要断开连接"(发送 FIN)。
-
C->S:客户端表示"收到,确认断开"(返回 ACK)。
-
-
主动断开连接方 :主动断开连接的一方,最终会进入
TIME_WAIT状态,等待最后一次挥手彻底完成。
-
5.3 TIME_WAIT 与 CLOSE_WAIT
-
概念解释:
-
句柄泄漏 (Handle Leak):指程序在申请了文件描述符(句柄)后,由于逻辑错误未正确释放(未调用 close),导致系统可用的文件描述符逐渐耗尽的问题。
-
MSL (Maximum Segment Lifetime):报文在网络中的最大生存时间。
-
-
详细笔记:
-
CLOSE_WAIT 产生原因与危害:
-
如果客户端(C)已经退出或关闭,而服务器端(S)没有调用
close()关闭 Socket,服务端就会一直卡在CLOSE_WAIT状态。 -
在此状态下,连接并未真正释放,只是停止了文件句柄的读写。这会导致句柄泄漏问题,是一个严重的BUG。
-
修复方案 :必须在服务端逻辑中加上对应的
close()调用(或修正代码中遗漏的new_sock.Close()),确保四次挥手正确完成。
-
-
TIME_WAIT 状态的作用(2MSL机制):
-
主动关闭方在发送最后一个 ACK 后,必须处于
TIME_WAIT状态等待 2MSL 的时间(CentOS7/Ubuntu 默认 60s,可通过cat /proc/sys/net/ipv4/tcp_fin_timeout查看)。 -
为什么是 2MSL?
-
保证两个传输方向上尚未接收或迟到的报文段都已经消失,防止服务器立刻重启后收到上一个进程的错误/迟到数据。
-
理论上保证最后一个报文可靠到达。如果最后一个 ACK 丢失,服务端会重发 FIN,此时处于
TIME_WAIT的客户端仍可重发 LAST_ACK。
-
-
-
TIME_WAIT 引起的 Bind 失败与解决:
-
服务端主动断开连接时,会产生大量
TIME_WAIT。若此时重启服务端程序(如./server),会报bind error: Address already in use。 -
因为
TIME_WAIT占用了五元组,相同端口暂不可用。 -
解决方法 :使用
setsockopt()设置SO_REUSEADDR选项。
-
-
5.4 涉及的函数
-
函数名 :
shutdown -
函数原型:
c
#include <sys/socket.h>
int shutdown(int sockfd, int how);
- 功能与参数说明 :主动关闭全双工连接的全部或部分。
how参数:SHUT_RD(禁止后续接收),SHUT_WR(禁止后续发送),SHUT_RDWR(禁止后续收发)。
-
函数名 :
setsockopt -
函数原型:
c
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
- 功能与参数说明 :用于设置 Socket 选项。常用于解决 TIME_WAIT 导致的 bind 失败,设置
SO_REUSEADDR为 1。
六、 数据流转、面向字节流与滑动窗口
6.1 数据流转与面向字节流
-
概念解释:
-
序列化/反序列化 (Serialization/Deserialization):将应用层结构化数据转换为连续字节流(序列化)以便网络传输,并在接收端恢复原始结构(反序列化)的过程。
-
面向字节流 (Byte Stream):TCP将数据视为无边界的连续字节序列。读写次数不需要一一对应。
-
粘包问题 (Sticky Packet):由于TCP面向字节流且无报文边界,接收方应用层可能将多个连续发送的数据包当作一个包读取,或者读到"半个包"的现象。
-
-
详细笔记:
-
数据流转链路:应用层 (Message) -> 序列化 -> 系统调用 (write) -> OS Kernel (发送缓冲区) -> TCP层 -> 网络层 -> 传输层 -> 本地发送网卡 -> 网络 -> 主机B本地接收网卡 -> 接收缓冲区 -> read -> 反序列化。
-
面向字节流的特性:
-
存在发送缓冲区和接收缓冲区。数据写入发送缓冲区后,长包拆分,短包等待(积累到合适大小或时机发送)。
-
读写不需要匹配:写100字节可以1次写完,也可以分100次写;读也可以随意组合,互不影响。
-
-
粘包问题解析与解决:
-
"包"指的是应用层数据包。TCP有序号机制保证字节按序到达,但不提供应用层边界。
-
相比之下,UDP不存在粘包问题,因为UDP交付是按单个完整报文进行的(要么全收,要么不收)。
-
解决粘包归根结底是"明确两个包之间的边界":
-
定长包 :每次按固定大小读取(如
sizeof(Request))。 -
变长包(包头约定长度):在包头增加总长度字段。
-
变长包(分隔符):包与包之间使用明确的分隔符(确保正文不与分隔符冲突)。
-
-
-
6.2 滑动窗口机制

-
概念解释:
- 滑动窗口 (Sliding Window) :发送方无需等待确认应答 (ACK) 而可以连续发送数据的最大量。本质是流量控制的具体实现方案。
-
详细笔记:
-
窗口的作用:一发一收性能差。一次发送多条数据(将等待ACK的时间重叠)可极大提高性能与网络吞吐率。
-
窗口大小的决定 :滑动窗口大小由"对方接收窗口大小"和"我的发送缓冲区"共同决定(取短板),即由对方的接收能力决定。
-
窗口的内部机制:
-
接收窗口是发送缓冲区的一部分。
-
发送缓冲区区域划分:已发送已确认 | 可以直接发(滑动窗口内) | 待发送。
-
计算公式 :
start = 确认序号;end = start + win。大小 = end - start。 -
滑动窗口移动的本质:start 和 end 向右增加。随着确认数据的到来,窗口不断右移,已确认的数据被冲刷掉。
-
序号在缓冲区中逻辑上无限大,窗口基本只向右滑动。
-
-
关于滑动窗口的问题思考:
-
可以向左滑动吗?不会!
-
可以变大/小/不变/为零吗?可以,完全由对方的接收能力决定。
-
会不会越过报文进行应答?不会,由确认序号的定义(要求连续确认)决定。
-
接收窗口一直向右会不会溢出?(环形缓冲区设计可解决)。
-
-
七、 TCP 异常处理、重传机制与网络控制
7.1 丢包与重传机制
-
概念解释:
-
超时重传 (Timeout Retransmission):当发送方在规定时间内未收到报文的ACK,认为包已丢失,主动重新发送该报文。
-
快速重传 (Fast Retransmit / 高速重发控制):如果发送端连续收到3个相同的ACK(说明某个包丢失,但后续的包收到了),无需等待超时定时器,立刻重发丢失的报文段。
-
-
详细笔记:
-
丢包场景分析与滑动窗口应对:
-
ACK丢失:部分ACK丢失不要紧,因为TCP确认消息是连续确认的。后续的ACK(如收到"下一个是3001")会自动涵盖前面丢掉的ACK。
-
最左侧数据包丢失 :发送端滑动窗口左侧不动。接收端持续返回相同的ACK(如重复返回3次"下一个是1001")。触发快速重传,补发1001-2000这段数据。接收端收到补发后,会直接返回之前已缓存数据的最大连续序号(如7001)。
-
中间报文丢失 :必须触发重传,确认了才能向右滑动。超时重传和快速重传的配合就是滑动窗口的核心逻辑!
-
-
报文暂存:TCP发包未收到应答时,报文必须暂存在发送缓冲区内,以便后期重传。
-
7.2 流量控制 (Flow Control)

-
概念解释:
-
流量控制:TCP根据接收端的处理能力,动态决定发送端的发送速度,避免接收端缓冲区被打满导致丢包。
-
窗口探测包 (Window Probe):当接收端窗口变为0时,发送端定期发送的微小数据段,用于探测接收端窗口是否恢复。
-
-
详细笔记:
-
工作原理
流量控制主要通过滑动窗口(Sliding Window)来实现。
-
接收方在每次给发送方回复 ACK(确认报文)时,都会在 TCP 首部的 Window(窗口大小) 字段中填入一个值。
-
这个值代表接收方目前剩余的缓冲区大小。
-
发送方根据这个通知,动态调整自己连续发送数据的上限。如果接收方通告窗口为 0,发送方就会停止发送,直到接收方重新通告一个非 0 窗口。
-
7.3 拥塞控制 (Congestion Control)


-
概念解释:
-
拥塞控制:防止过多数据注入网络,避免网络负载过大。它是针对整个网络状态的控制,区别于流量控制(针对通信两端)。
-
拥塞窗口 (cwnd):发送方维护的一个状态变量,控制同时发送到网络的数据量。
-
慢启动 (Slow Start):在初始发送或网络拥塞后,从较小的窗口开始,按指数级快速探测网络可用带宽的算法。
-
场景:你开着车(数据包)上高速公路。如果路上的车太多,收费站和主干道就会堵车(路由器排队、丢包)。交管部门(拥塞控制算法)会通过限制限行、调整红绿灯(降低发送速率)来缓解整条公路的拥堵。这不取决于你目的地那个人的车库有多大,而是取决于大马路堵不堵。
-
-
详细笔记:
-
工作流程 :实际发送窗口 =
min(拥塞窗口, 接收端窗口)。 -
慢启动:初始时拥塞窗口很小,但每收到一个 ACK,窗口大小就翻倍(指数级增长),用以快速探测网络带宽。
-
拥塞避免:当窗口达到一个阈值(ssthresh)后,为了防止动作过大导致拥堵,窗口变为线性增长(每次加 1)。
-
快重传:发送方只要连续收到 3 个重复的 ACK,就判定数据包丢了,不等超时定时器到期,立刻重传。
-
快恢复:配合快重传,在网络出现丢包但还没彻底瘫痪时,直接将窗口减半,然后重新进入线性增长,而不是直接掉回初始的慢启动状态。
-
补充:网络出现丢包时的两种处理情况
当网络在"拥塞避免"阶段由于达到极限而开始丢包时,TCP 会根据以下两种不同情况来决定是启动"快恢复"还是"重置为 1":
-
情况一:网络严重拥塞(触发超时重传)
如果网络彻底堵死,发送方长时间收不到任何回应,导致超时定时器到期。此时 TCP 判定网络瘫痪:
-
慢启动阈值
ssthresh变为当前窗口的一半。 -
拥塞窗口(cwnd)瞬间重置为 1。
-
重新开始慢启动过程。
-
-
情况二:网络轻微拥塞(触发快重传与快恢复)
如果网络只是个别丢包,发送方连续收到 3 个重复的 ACK。此时 TCP 判定网络大体通畅,只是轻微感冒:
-
立即触发快重传补漏。
-
随后触发快恢复 ,直接将窗口减半,然后重新进入线性增长(拥塞避免),而不掉回初始的慢启动状态。
-
7.4 提高效率的优化机制与异常情况
-
概念解释:
-
延迟应答 (Delayed ACK) :接收端收到数据后不立即返回ACK,而是等待一小段时间。

-
捎带应答 (Piggybacking) :ACK确认包搭乘应用层即将发送的响应数据包(顺风车)一起发往对端。

-
保活机制 (Keep-Alive):TCP内置定时器,定期询问对方存活状态。
-
-
详细笔记:
-
延迟应答:立刻应答可能导致返回的窗口较小。延迟一段时间(等待应用层消费掉部分缓冲区),可以返回更大的窗口,提升吞吐量。限制:数量限制(如每隔2个包应答)和时间限制(如最大延迟200ms)。
-
捎带应答 :在)
延迟应答基础上,一发一收的场景中,ACK 经常和业务响应(如 HTTP Response)合并发送,减少网络包数量。
-
TCP异常情况处理(容错能力强):
-
进程终止:释放文件描述符,自动发送 FIN,执行正常四次挥手。
-
机器重启:同进程终止。
-
机器掉电/断网:
-
若有写入操作,接收端发现连接不在,触发 Reset (RST)。
-
若无写入,TCP内置保活定时器(几十分钟级别)定期探活,失败则释放。通常保活机制会在应用层自己实现(如 HTTP长连接、QQ断线重连)。
-
-
-
八、 TCP 与 UDP 对比 & 基于 TCP 的协议
-
详细笔记:
-
TCP 的复杂性来源:
-
可靠性:校验和、序列号、确认应答、超时重发、连接管理、流量控制、拥塞控制。
-
提高性能:滑动窗口、快速重传、延迟应答、捎带应答。
-
其他:各类定时器(超时重传、保活、TIME_WAIT等)。
-
-
TCP vs UDP 场景对比:
-
TCP 适用于可靠传输(文件传输、重要状态更新)。
-
UDP 适用于高速传输、实时性要求高、允许轻微丢包的场景(早期QQ、视频流媒体,支持广播)。
-
经典面试题:用 UDP 实现可靠传输 -> 需在应用层引入序列号、确认应答、超时重传等机制。
-
-
基于 TCP 的应用层协议:HTTP, HTTPS, SSH, Telnet, FTP, SMTP 等。
-
九、 Linux 内核 Socket 数据结构 (C语言中的面向对象/多态)
9.1 结构体嵌套与多态机制
-
概念解释:
- 多态机制 (Polymorphism in C):Linux内核使用结构体嵌套(将被继承的结构体放在子结构体的第一个成员位置)来实现面向对象的继承和多态。指针强制转换后,可以直接操作父类属性。
-
详细笔记:
-
进程与文件系统的关联 :
task_struct包含files_struct指针 ->fd_array[]-> 指向具体的struct file->file->private_data指向网络层的struct socket。 -
网络协议栈的继承链条:
-
最内层(基类) :
struct sock sk(包含收发队列sk_receive_queue和sk_write_queue)。 -
第二层 :
struct inet_sock(第一个成员必须是struct sock sk)。 -
第三层 :
struct inet_connection_sock。 -
最外层(TCP派生类) :
struct tcp_sock/struct udp_sock。
-
-