Linux 网络编程:传输层 TCP(一)—— TCP 基础与连接管理
在 Linux 网络编程中,传输层协议主要包括 TCP(Transmission Control Protocol) 和 UDP(User Datagram Protocol)。其中 TCP 是互联网最常用的传输协议之一,广泛应用于 Web、数据库、邮件、文件传输等场景。
本篇将详细介绍 TCP 的基本概念、特点、报文格式、三次握手与四次挥手,为后续 Socket 编程打下基础。
一、什么是 TCP?
TCP(Transmission Control Protocol,传输控制协议)是一种:
- 面向连接(Connection-Oriented)
- 可靠传输(Reliable)
- 字节流(Byte Stream)
- 全双工通信(Full Duplex)
的传输层协议。
位于 TCP/IP 协议栈中的传输层:
应用层
↓
HTTP、FTP、SMTP
↓
TCP
↓
IP
↓
数据链路层
↓
物理层
二、TCP 的特点
1. 面向连接
通信双方在传输数据前必须先建立连接。
类似于:
打电话
↓
拨号
↓
接通
↓
开始聊天
TCP 也是如此:
客户端
↓
建立连接
↓
服务端
↓
数据传输
2. 可靠传输
TCP 保证:
- 数据不丢失
- 数据不重复
- 数据按顺序到达
实现机制:
- 序列号(Sequence Number)
- 确认应答(ACK)
- 超时重传
- 滑动窗口
- 流量控制
- 拥塞控制
3. 面向字节流
TCP 不关心数据边界。
例如:
客户端:
send(sock,"Hello",5,0);
send(sock,"World",5,0);
服务端:
可能收到:
HelloWorld
也可能:
Hel
loWorld
因此:
TCP没有消息边界
应用层需要自行处理数据包边界问题。
4. 全双工通信
客户端和服务端可同时发送和接收数据。
例如:
客户端 ←→ 服务端
双方互不影响。
三、TCP 与 UDP 对比
| 特性 | TCP | UDP |
|---|---|---|
| 是否连接 | 是 | 否 |
| 是否可靠 | 是 | 否 |
| 是否排序 | 保证 | 不保证 |
| 是否重传 | 支持 | 不支持 |
| 传输速度 | 较慢 | 较快 |
| 头部大小 | 20~60字节 | 8字节 |
| 应用场景 | Web、数据库 | 视频、直播、游戏 |
四、TCP 报文段结构
TCP 首部最小:
20字节
结构如下:
0 15 16 31
+----------------------+----------------------+
| 源端口 | 目标端口 |
+----------------------+----------------------+
| 序列号 Sequence Number |
+---------------------------------------------+
| 确认号 Acknowledgment Number |
+---------------------------------------------+
|头长|保留|控制位| 窗口大小 |
+---------------------------------------------+
| 校验和 | 紧急指针 |
+---------------------------------------------+
| 选项 Option |
+---------------------------------------------+
五、重要字段详解
1. 源端口
发送方端口。
例如:
52345
2. 目标端口
接收方端口。
例如:
80
443
3306
3. 序列号(SEQ)
TCP 为每个字节编号。
例如:
Seq = 1000
表示:
从1000开始发送数据
用于:
- 排序
- 丢包检测
- 重传控制
4. 确认号(ACK)
表示:
已经收到的数据
例如:
ACK = 2000
说明:
1999之前的数据都收到了
下一次请发送:
2000
之后的数据。
六、TCP 控制位(Flags)
TCP 有六个核心标志位:
| 标志位 | 含义 |
|---|---|
| SYN | 建立连接 |
| ACK | 确认 |
| FIN | 关闭连接 |
| RST | 重置连接 |
| PSH | 推送数据 |
| URG | 紧急数据 |
七、三次握手(Three-Way Handshake)
TCP 建立连接时需要三次握手。
为什么需要三次?
目的:
确认:
客户端发送能力
客户端接收能力
服务端发送能力
服务端接收能力
均正常。
第一次握手
客户端:
SYN=1
Seq=x
发送:
我要建立连接
状态:
SYN_SENT
第二次握手
服务端收到:
返回:
SYN=1
ACK=1
Seq=y
Ack=x+1
表示:
收到你的请求
我也同意建立连接
状态:
SYN_RCVD
第三次握手
客户端收到:
再次发送:
ACK=1
Ack=y+1
表示:
收到你的确认
连接建立成功
状态:
ESTABLISHED
服务端收到后:
ESTABLISHED
三次握手过程图
客户端 服务端
SYN=x
----------->
SYN=y ACK=x+1
<-----------
ACK=y+1
----------->
连接建立成功
八、为什么不是两次握手?
假设:
客户端发送:
SYN
由于网络延迟:
很久以后才到达服务端
客户端早已放弃。
如果仅两次握手:
服务端会误认为:
客户端仍然需要连接
从而建立无效连接。
第三次握手的作用:
确认客户端确实收到服务端响应
避免历史连接请求造成资源浪费。
九、Linux 查看三次握手
抓包:
sudo tcpdump -i eth0 tcp
或者:
sudo tcpdump -i any port 80
典型输出:
S
S.
.
含义:
S -> SYN
S. -> SYN+ACK
. -> ACK
十、四次挥手(Four-Way Handshake)
TCP 是全双工通信。
双方都要独立关闭。
因此需要:
四次挥手
第一次挥手
客户端:
FIN=1
表示:
我没有数据发送了
状态:
FIN_WAIT_1
第二次挥手
服务端:
ACK
表示:
收到关闭请求
状态:
CLOSE_WAIT
客户端进入:
FIN_WAIT_2
第三次挥手
服务端处理完剩余数据:
发送:
FIN
表示:
我也关闭
状态:
LAST_ACK
第四次挥手
客户端:
ACK
回复。
状态:
TIME_WAIT
服务端:
CLOSED
四次挥手过程图
客户端 服务端
FIN
--------->
ACK
<---------
FIN
<---------
ACK
--------->
十一、TIME_WAIT 状态
很多开发者会发现:
netstat -ant
输出:
TIME_WAIT
大量存在。
为什么需要 TIME_WAIT?
保证:
最后一个 ACK
能够成功到达
如果 ACK 丢失:
服务端会重发:
FIN
客户端还能处理。
TIME_WAIT 时长
Linux:
2MSL
通常:
60秒左右
查看:
cat /proc/sys/net/ipv4/tcp_fin_timeout
十二、TCP 状态转换图
常见状态:
LISTEN
↓
SYN_RCVD
↓
ESTABLISHED
↓
FIN_WAIT_1
↓
FIN_WAIT_2
↓
TIME_WAIT
↓
CLOSED
服务端:
LISTEN
↓
SYN_RCVD
↓
ESTABLISHED
↓
CLOSE_WAIT
↓
LAST_ACK
↓
CLOSED
十三、Linux 查看 TCP 状态
查看连接:
ss -ant
或者:
netstat -ant
示例:
LISTEN
ESTAB
TIME_WAIT
CLOSE_WAIT
查看统计:
ss -s
输出:
TCP:
estab 15
closed 230
timewait 40
十四、面试高频问题
TCP 为什么可靠?
答:
- 序列号
- ACK确认
- 超时重传
- 滑动窗口
- 流量控制
- 拥塞控制
为什么是三次握手?
为了确认:
- 双方发送能力正常
- 双方接收能力正常
并防止历史连接请求造成错误连接。
为什么是四次挥手?
TCP 全双工。
双方关闭发送通道需要分别通知,因此需要四次。
TIME_WAIT 为什么存在?
保证:
- 最后 ACK 可重发
- 老连接报文彻底消失
避免影响新的连接。
总结
TCP 是 Linux 网络编程中最重要的传输层协议之一,其核心特征包括:
- 面向连接
- 可靠传输
- 字节流
- 全双工通信
重点掌握:
- TCP 报文结构
- 序列号与确认号
- 六大控制位
- 三次握手建立连接
- 四次挥手断开连接
- TIME_WAIT 状态原理
理解这些基础知识后,学习 socket()、bind()、listen()、accept()、connect() 等 Linux Socket API 时会更加容易。下一篇通常会深入 TCP 的可靠传输机制,包括滑动窗口、流量控制、拥塞控制和重传机制。