1.TCP/IP模型:各层协议(重点TCP/UDP)
目录
1. TCP/IP模型概述
四层模型 vs OSI七层模型
数据封装过程
2. 各层协议详解
2.1 应用层协议
2.2 传输层协议
TCP vs UDP 对比
2.3 网络层协议
2.4 网络接口层协议
3. TCP协议深度解析
3.1 TCP头部结构
3.2 TCP三次握手
3.3 TCP四次挥手
3.4 TCP可靠性机制
超时重传
滑动窗口
拥塞控制算法
4. UDP协议深度解析
4.1 UDP头部结构
4.2 UDP特点和应用
优点
典型应用场景
4.3 基于UDP的可靠传输协议
自定义可靠性机制
5. 端口和套接字
5.1 知名端口
编程视角:Socket API 简析
6. 网络编程实践
6.1 TCP粘包处理
7. 面试常见问题
Q1: TCP和UDP的主要区别?
Q2: 为什么需要三次握手?
Q3: TIME_WAIT状态的作用?
Q4: TCP拥塞控制算法有哪些?
Q5: UDP如何实现可靠性?
网络调试工具
总结
1. TCP/IP模型概述
四层模型 vs OSI七层模型
OSI七层模型:理论模型
TCP/IP四层模型:实际实现的模型+----------------+----------------+
| OSI模型 | TCP/IP模型 |
+----------------+----------------+
| 应用层 | |
| 表示层 | 应用层 |
| 会话层 | |
+----------------+----------------+
| 传输层 | 传输层 |
+----------------+----------------+
| 网络层 | 网络层 |
+----------------+----------------+
| 数据链路层 | 网络接口层 |
| 物理层 | |
+----------------+----------------+
数据封装过程
应用数据 → TCP/UDP段 → IP数据报 → 帧 → 比特流↓ ↓ ↓ ↓ ↓
应用层 传输层 网络层 数据链路层 物理层
2. 各层协议详解
2.1 应用层协议
# 常见的应用层协议
HTTP/HTTPS # 超文本传输协议(Web)
FTP/SFTP # 文件传输协议
SMTP # 简单邮件传输协议
DNS # 域名系统
DHCP # 动态主机配置协议
SSH # 安全外壳协议
2.2 传输层协议
TCP vs UDP 对比
特性 | TCP (传输控制协议) | UDP (用户数据报协议) |
---|---|---|
连接性 | 面向连接的 | 无连接的 |
可靠性 | 可靠交付(确认、重传、拥塞控制) | 尽最大努力交付(不保证不丢失、不重复) |
有序性 | 保证数据按序到达 | 不保证顺序 |
数据形式 | 面向字节流(无边界) | 面向报文(有边界,一次发送一个报文) |
首部开销 | 大 (20-60字节) | 小 (仅8字节) |
速度 | 慢(需要建立连接、确认等) | 快 |
控制机制 | 有流量控制、拥塞控制 | 无 |
应用场景 | 可靠性 > 实时性的场景: Web (HTTP/HTTPS)、Email、文件传输(FTP)、数据库 | 实时性 > 可靠性的场景: 视频会议、语音通话、直播、DNS、QUIC |
核心记忆:应用层管内容,传输层管端口,网络层管IP地址,链路层管MAC地址。
-
TCP是打电话:需要先建立连接,可靠,但啰嗦。
-
UDP是发短信:直接发,可能丢,但高效。
2.3 网络层协议
# 核心网络层协议
IP # 网际协议(IPv4/IPv6)
ICMP # 互联网控制消息协议(ping)
ARP # 地址解析协议
RIP/OSPF/BGP # 路由协议
2.4 网络接口层协议
# 数据链路层和物理层协议
Ethernet # 以太网
Wi-Fi # 无线局域网
PPP # 点对点协议
VLAN # 虚拟局域网
3. TCP协议深度解析
3.1 TCP头部结构
// TCP头部(20字节 + 可选字段)
struct tcp_header {uint16_t src_port; // 源端口(2字节)uint16_t dst_port; // 目的端口(2字节)uint32_t seq_num; // 序列号(4字节)uint32_t ack_num; // 确认号(4字节)uint8_t data_offset; // 数据偏移(4位)uint8_t flags; // 标志位(6位)uint16_t window; // 窗口大小(2字节)uint16_t checksum; // 校验和(2字节)uint16_t urgent_ptr; // 紧急指针(2字节)// 可选选项(0-40字节)
};
3.2 TCP三次握手
目的:双方确认彼此的发送和接收能力正常,并同步初始序列号(ISN)。
客户端 → 服务器:SYN=1, Seq=x
客户端 ← 服务器:SYN=1, ACK=1, Seq=y, Ack=x+1
客户端 → 服务器:ACK=1, Seq=x+1, Ack=y+1建立连接,协商初始序列号
// 非常形象的伪代码描述
// 客户端 (Client) 服务端 (Server)
int client_sock = socket(); int server_sock = socket();
bind(server_sock); bind(server_sock);listen(server_sock); // 进入LISTEN状态// 1. SYN_SENT状态
send(client_sock, SYN=1, seq=J); -------> // 收到SYN// 2. SYN_RCVD状态<------- send(server_sock, SYN=1, ACK=1, seq=K, ack=J+1);// 3. ESTABLISHED状态
send(client_sock, ACK=1, seq=J+1, ack=K+1); -> // 收到ACK// 4. ESTABLISHED状态
为什么是三次?
-
根本原因:防止已失效的连接请求报文突然又传送到服务器,导致错误。
-
简单理解:两次握手不够。如果客户端的第一个SYN报文滞留了,客户端重发一个并建立连接。完成后,那个滞留的SYN报文才到达服务器,服务器会认为客户端又想建立新连接并同意,但客户端已关闭,导致服务器资源空等。三次握手避免了这个问题。
3.3 TCP四次挥手
目的:双方都确认要关闭连接。
客户端 → 服务器:FIN=1, Seq=u
客户端 ← 服务器:ACK=1, Seq=v, Ack=u+1
客户端 ← 服务器:FIN=1, ACK=1, Seq=w, Ack=u+1
客户端 → 服务器:ACK=1, Seq=u+1, Ack=w+1优雅关闭连接,确保数据完整传输
// 客户端 (Client) 服务端 (Server)
// (双方都处于ESTABLISHED状态)// 1. FIN_WAIT_1状态
send(client_sock, FIN=1, seq=U); -------> // 收到FIN// 2. CLOSE_WAIT状态<------- send(server_sock, ACK=1, seq=V, ack=U+1); // 第一次挥手完成// 3. FIN_WAIT_2状态
... (等待服务器发FIN) ... (服务器可能还有数据要发送)<------- send(server_sock, FIN=1, ACK=1, seq=W, ack=U+1); // 服务器数据发完了// 4. TIME_WAIT状态
send(client_sock, ACK=1, seq=U+1, ack=W+1); -> // 收到ACK, 进入CLOSED状态
// 等待2MSL后进入CLOSED状态
为什么是四次?
因为TCP连接是全双工的,每个方向都必须单独关闭。一方发送FIN只表示它没有数据要发送了,但还可以接收数据。
为什么需要TIME_WAIT状态?等待2MSL(最大报文段寿命)的原因:
-
可靠地终止连接:确保最后一个ACK能到达服务器。如果ACK丢失,服务器会重发FIN,客户端还能响应。
-
让旧连接的报文在网络中消散:避免之前连接的延迟报文被新建立的相同IP和端口的连接错误接收。
3.4 TCP可靠性机制
-
确认与重传:接收方收到数据后要发送ACK确认。发送方在一定时间(RTO)内没收到ACK,会重传数据。
-
序列号与确认号:保证数据按序到达。
-
流量控制:使用滑动窗口协议,接收方通过通告窗口大小来告诉发送方自己还能接收多少数据,防止发送过快导致接收方缓冲区溢出。
-
拥塞控制:防止网络过载。核心算法:
-
慢启动:窗口从1开始,按指数增长。
-
拥塞避免:窗口超过慢启动阈值(ssthresh)后,按线性增长。
-
快重传:收到3个重复ACK立即重传,而不等超时。
-
快恢复:快重传后,直接进入拥塞避免,而非慢启动。
-
超时重传
// 重传计时器管理
void tcp_retransmit(struct tcp_connection* conn) {if (!ack_received_within_timeout()) {retransmit_packet(); // 重传数据包double_timeout_interval(); // 指数退避}
}
滑动窗口
发送方窗口:[已确认][已发送未确认][可发送][不可发送]
接收方窗口:[已接收][可接收][不可接收]通过窗口大小实现流量控制
拥塞控制算法
// TCP拥塞控制状态机
void tcp_congestion_control(struct tcp_connection* conn) {switch (conn->state) {case SLOW_START:// 指数增长:cwnd *= 2每个RTTif (conn->cwnd >= conn->ssthresh) {conn->state = CONGESTION_AVOIDANCE;}break;case CONGESTION_AVOIDANCE:// 线性增长:cwnd += 1每个RTTbreak;case FAST_RECOVERY:// 快速恢复算法break;}
}
4. UDP协议深度解析
4.1 UDP头部结构
// UDP头部(8字节)
struct udp_header {uint16_t src_port; // 源端口(2字节)uint16_t dst_port; // 目的端口(2字节)uint16_t length; // 长度(2字节)uint16_t checksum; // 校验和(2字节)
};
4.2 UDP特点和应用
优点
-
低延迟:无连接建立开销
-
低开销:头部只有8字节
-
无拥塞控制:适合实时应用
典型应用场景
// 1. DNS查询
void dns_query() {// UDP包:查询域名对应的IP// 快速、简单,适合小数据包
}// 2. 视频流媒体
void video_streaming() {// 容忍少量丢包,但不能容忍延迟// 使用UDP传输视频帧
}// 3. 在线游戏
void online_gaming() {// 需要低延迟,状态更新频繁// 使用UDP传输游戏状态
}// 4. VoIP语音通话
void voice_call() {// 实时性要求高,可以容忍少量丢包// 使用UDP传输语音数据
}
4.3 基于UDP的可靠传输协议
自定义可靠性机制
// 在应用层实现可靠性
struct reliable_udp {uint32_t seq_num; // 序列号uint32_t ack_num; // 确认号uint8_t flags; // 标志位uint16_t checksum; // 校验和char data[]; // 数据
};void udp_reliability() {// 应用层实现:// - 序列号和确认机制// - 超时重传// - 流量控制// 例子:QUIC协议(HTTP/3)
}
5. 端口和套接字
5.1 知名端口
# 常见TCP端口
80 # HTTP
443 # HTTPS
21 # FTP
22 # SSH
25 # SMTP
53 # DNS(也用于UDP)# 常见UDP端口
53 # DNS
67/68 # DHCP
123 # NTP
161 # SNMP
编程视角:Socket API 简析
理解协议最终要落到编程上。
TCP Socket 编程流程:
// 服务器端
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 1. 创建TCP Socket
bind(sockfd, ...); // 2. 绑定IP和端口
listen(sockfd, backlog); // 3. 开始监听
int connfd = accept(sockfd, ...); // 4. 接受连接 (阻塞,直到三次握手完成)
recv(connfd, buf, size, 0); // 5. 接收数据
send(connfd, buf, size, 0); // 6. 发送数据
close(connfd); // 7. 关闭连接 (触发四次挥手)
UDP Socket 编程流程:
// 服务器端
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 1. 创建UDP Socket
bind(sockfd, ...); // 2. 绑定IP和端口
recvfrom(sockfd, buf, size, 0, client_addr, ...); // 3. 接收数据 (同时获取客户端地址)
sendto(sockfd, buf, size, 0, client_addr, ...); // 4. 向该地址发送数据
-
必背题:
-
TCP和UDP的区别(至少说出连接性、可靠性、有序性、速度、首部开销)。
-
三次握手和四次挥手的详细过程及为什么是三次/四次。
-
TIME_WAIT状态的作用。
-
-
必会分析:
-
能画出握手和挥手的状态变迁图。
-
能解释滑动窗口、流量控制和拥塞控制的基本思想。
-
能说出SYN洪泛攻击的原理(伪造IP发SYN,耗尽服务器的半连接队列)。
-
-
实战编程:
-
能说出TCP粘包/拆包的原因及解决方法(消息头定长、包尾加分隔符、自定义协议如 length + body)。
-
能简单描述如何使用Socket API编写TCP/UDP的服务器和客户端。
-
6. 网络编程实践
6.1 TCP粘包处理
// 解决方案1:定长消息
struct fixed_message {uint32_t length; // 消息长度char data[]; // 消息数据
};// 解决方案2:分隔符
void send_with_delimiter(int sockfd, const char* data) {send(sockfd, data, strlen(data), 0);send(sockfd, "\r\n", 2, 0); // 添加分隔符
}// 解决方案3:TLV格式(Type-Length-Value)
struct tlv_message {uint8_t type; // 消息类型uint32_t length; // 数据长度char value[]; // 数据值
};
7. 面试常见问题
Q1: TCP和UDP的主要区别?
答:TCP是面向连接的、可靠的、基于流的协议,有流量控制和拥塞控制;UDP是无连接的、不可靠的、基于数据报的协议,传输效率更高。
Q2: 为什么需要三次握手?
答:三次握手确保双方都能确认对方的发送和接收能力正常,防止已失效的连接请求报文突然传到服务器导致错误。
Q3: TIME_WAIT状态的作用?
答:1) 确保最后一个ACK能够到达对方;2) 让旧连接的重复报文在网络中消失,避免影响新连接。
Q4: TCP拥塞控制算法有哪些?
答:慢启动、拥塞避免、快速重传、快速恢复。现代TCP还有CUBIC、BBR等算法。
Q5: UDP如何实现可靠性?
答:在应用层实现序列号、确认机制、重传计时器等,如QUIC协议所做的那样。
网络调试工具
# 常用网络工具
netstat # 查看网络连接状态
ss # netstat的替代品
tcpdump # 网络抓包分析
wireshark # 图形化抓包分析
iperf # 网络性能测试
nc # 网络调试工具
总结
TCP/IP模型是现代互联网的基石:
-
✅ 分层设计:各层职责明确,易于实现和维护
-
✅ TCP:可靠传输,适合需要数据完整性的应用
-
✅ UDP:高效传输,适合实时和延迟敏感的应用
-
✅ 协议选择:根据应用需求选择合适的传输层协议
-
✅ 性能优化:理解协议特性进行针对性调优