1.TCP/IP模型:各层协议(重点TCP/UDP)

目录

[1. TCP/IP模型概述](#1. TCP/IP模型概述)

[四层模型 vs OSI七层模型](#四层模型 vs OSI七层模型)

数据封装过程

[2. 各层协议详解](#2. 各层协议详解)

[2.1 应用层协议](#2.1 应用层协议)

[2.2 传输层协议](#2.2 传输层协议)

[TCP vs UDP 对比](#TCP vs UDP 对比)

[2.3 网络层协议](#2.3 网络层协议)

[2.4 网络接口层协议](#2.4 网络接口层协议)

[3. TCP协议深度解析](#3. TCP协议深度解析)

[3.1 TCP头部结构](#3.1 TCP头部结构)

[3.2 TCP三次握手](#3.2 TCP三次握手)

[3.3 TCP四次挥手](#3.3 TCP四次挥手)

[3.4 TCP可靠性机制](#3.4 TCP可靠性机制)

超时重传

滑动窗口

拥塞控制算法

[4. UDP协议深度解析](#4. UDP协议深度解析)

[4.1 UDP头部结构](#4.1 UDP头部结构)

[4.2 UDP特点和应用](#4.2 UDP特点和应用)

优点

典型应用场景

[4.3 基于UDP的可靠传输协议](#4.3 基于UDP的可靠传输协议)

自定义可靠性机制

[5. 端口和套接字](#5. 端口和套接字)

[5.1 知名端口](#5.1 知名端口)

[编程视角:Socket API 简析](#编程视角:Socket API 简析)

[6. 网络编程实践](#6. 网络编程实践)

[6.1 TCP粘包处理](#6.1 TCP粘包处理)

[7. 面试常见问题](#7. 面试常见问题)

[Q1: TCP和UDP的主要区别?](#Q1: TCP和UDP的主要区别?)

[Q2: 为什么需要三次握手?](#Q2: 为什么需要三次握手?)

[Q3: TIME_WAIT状态的作用?](#Q3: TIME_WAIT状态的作用?)

[Q4: TCP拥塞控制算法有哪些?](#Q4: TCP拥塞控制算法有哪些?)

[Q5: UDP如何实现可靠性?](#Q5: UDP如何实现可靠性?)

网络调试工具

总结


1. TCP/IP模型概述

四层模型 vs OSI七层模型

cpp 复制代码
OSI七层模型:理论模型
TCP/IP四层模型:实际实现的模型

+----------------+----------------+
|   OSI模型      |   TCP/IP模型   |
+----------------+----------------+
| 应用层         |                |
| 表示层         |   应用层        |
| 会话层         |                |
+----------------+----------------+
| 传输层         |   传输层        |
+----------------+----------------+
| 网络层         |   网络层        |
+----------------+----------------+
| 数据链路层      |   网络接口层    |
| 物理层         |                |
+----------------+----------------+

数据封装过程

cpp 复制代码
应用数据 → TCP/UDP段 → IP数据报 → 帧 → 比特流
    ↓         ↓          ↓         ↓       ↓
应用层    传输层      网络层    数据链路层  物理层

2. 各层协议详解

2.1 应用层协议

bash 复制代码
# 常见的应用层协议
HTTP/HTTPS    # 超文本传输协议(Web)
FTP/SFTP      # 文件传输协议
SMTP          # 简单邮件传输协议
DNS           # 域名系统
DHCP          # 动态主机配置协议
SSH           # 安全外壳协议

2.2 传输层协议

TCP vs UDP 对比
特性 TCP (传输控制协议) UDP (用户数据报协议)
连接性 面向连接 无连接
可靠性 可靠交付(确认、重传、拥塞控制) 尽最大努力交付(不保证不丢失、不重复)
有序性 保证数据按序到达 不保证顺序
数据形式 面向字节流(无边界) 面向报文(有边界,一次发送一个报文)
首部开销 大 (20-60字节) 小 (仅8字节)
速度 慢(需要建立连接、确认等)
控制机制 流量控制拥塞控制
应用场景 可靠性 > 实时性的场景: Web (HTTP/HTTPS)Email文件传输(FTP)、数据库 实时性 > 可靠性的场景: 视频会议语音通话直播DNSQUIC

核心记忆应用层内容传输层端口网络层IP地址链路层MAC地址

  • TCP是打电话:需要先建立连接,可靠,但啰嗦。

  • UDP是发短信:直接发,可能丢,但高效。

2.3 网络层协议

bash 复制代码
# 核心网络层协议
IP           # 网际协议(IPv4/IPv6)
ICMP         # 互联网控制消息协议(ping)
ARP          # 地址解析协议
RIP/OSPF/BGP # 路由协议

2.4 网络接口层协议

bash 复制代码
# 数据链路层和物理层协议
Ethernet     # 以太网
Wi-Fi        # 无线局域网
PPP          # 点对点协议
VLAN         # 虚拟局域网

3. TCP协议深度解析

3.1 TCP头部结构

cpp 复制代码
// 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)。

bash 复制代码
客户端 → 服务器:SYN=1, Seq=x
客户端 ← 服务器:SYN=1, ACK=1, Seq=y, Ack=x+1  
客户端 → 服务器:ACK=1, Seq=x+1, Ack=y+1

建立连接,协商初始序列号
cpp 复制代码
// 非常形象的伪代码描述
// 客户端 (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四次挥手

目的:双方都确认要关闭连接。

cpp 复制代码
客户端 → 服务器: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

优雅关闭连接,确保数据完整传输
cpp 复制代码
// 客户端 (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(最大报文段寿命)的原因:

  1. 可靠地终止连接:确保最后一个ACK能到达服务器。如果ACK丢失,服务器会重发FIN,客户端还能响应。

  2. 让旧连接的报文在网络中消散:避免之前连接的延迟报文被新建立的相同IP和端口的连接错误接收。

3.4 TCP可靠性机制

  1. 确认与重传:接收方收到数据后要发送ACK确认。发送方在一定时间(RTO)内没收到ACK,会重传数据。

  2. 序列号与确认号:保证数据按序到达。

  3. 流量控制 :使用滑动窗口 协议,接收方通过通告窗口大小来告诉发送方自己还能接收多少数据,防止发送过快导致接收方缓冲区溢出。

  4. 拥塞控制:防止网络过载。核心算法:

    • 慢启动:窗口从1开始,按指数增长。

    • 拥塞避免:窗口超过慢启动阈值(ssthresh)后,按线性增长。

    • 快重传:收到3个重复ACK立即重传,而不等超时。

    • 快恢复:快重传后,直接进入拥塞避免,而非慢启动。

超时重传
cpp 复制代码
// 重传计时器管理
void tcp_retransmit(struct tcp_connection* conn) {
    if (!ack_received_within_timeout()) {
        retransmit_packet();          // 重传数据包
        double_timeout_interval();    // 指数退避
    }
}
滑动窗口
cpp 复制代码
发送方窗口:[已确认][已发送未确认][可发送][不可发送]
接收方窗口:[已接收][可接收][不可接收]

通过窗口大小实现流量控制
拥塞控制算法
cpp 复制代码
// TCP拥塞控制状态机
void tcp_congestion_control(struct tcp_connection* conn) {
    switch (conn->state) {
        case SLOW_START:
            // 指数增长:cwnd *= 2每个RTT
            if (conn->cwnd >= conn->ssthresh) {
                conn->state = CONGESTION_AVOIDANCE;
            }
            break;
            
        case CONGESTION_AVOIDANCE:
            // 线性增长:cwnd += 1每个RTT
            break;
            
        case FAST_RECOVERY:
            // 快速恢复算法
            break;
    }
}

4. UDP协议深度解析

4.1 UDP头部结构

bash 复制代码
// 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字节

  • 无拥塞控制:适合实时应用

典型应用场景
cpp 复制代码
// 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的可靠传输协议

自定义可靠性机制
cpp 复制代码
// 在应用层实现可靠性
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 知名端口

bash 复制代码
# 常见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 编程流程

cpp 复制代码
// 服务器端
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 编程流程

cpp 复制代码
// 服务器端
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. 向该地址发送数据
  1. 必背题

    • TCP和UDP的区别(至少说出连接性、可靠性、有序性、速度、首部开销)。

    • 三次握手和四次挥手 的详细过程及为什么是三次/四次

    • TIME_WAIT状态的作用

  2. 必会分析

    • 能画出握手和挥手的状态变迁图

    • 能解释滑动窗口流量控制拥塞控制的基本思想。

    • 能说出SYN洪泛攻击的原理(伪造IP发SYN,耗尽服务器的半连接队列)。

  3. 实战编程

    • 能说出TCP粘包/拆包的原因及解决方法(消息头定长、包尾加分隔符、自定义协议如 length + body)。

    • 能简单描述如何使用Socket API编写TCP/UDP的服务器和客户端。

6. 网络编程实践

6.1 TCP粘包处理

cpp 复制代码
// 解决方案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协议所做的那样。

网络调试工具

cpp 复制代码
# 常用网络工具
netstat      # 查看网络连接状态
ss          # netstat的替代品
tcpdump     # 网络抓包分析
wireshark   # 图形化抓包分析
iperf       # 网络性能测试
nc          # 网络调试工具

总结

TCP/IP模型是现代互联网的基石:

  • 分层设计:各层职责明确,易于实现和维护

  • TCP:可靠传输,适合需要数据完整性的应用

  • UDP:高效传输,适合实时和延迟敏感的应用

  • 协议选择:根据应用需求选择合适的传输层协议

  • 性能优化:理解协议特性进行针对性调优

相关推荐
Miracle&6 小时前
2.TCP深度解析:握手、挥手、状态机、流量与拥塞控制
linux·网络·tcp/ip
C语言小火车6 小时前
【C++八股文】基础知识篇
c++·tcp/ip·const·智能指针·多线程同步·static关键字·c++内存模型
liulilittle6 小时前
IP校验和算法:从网络协议到SIMD深度优化
网络·c++·网络协议·tcp/ip·算法·ip·通信
7ACE7 小时前
Wireshark TS | 接收数据超出接收窗口
网络协议·tcp/ip·wireshark
tuokuac7 小时前
nginx配置前端请求转发到指定的后端ip
前端·tcp/ip·nginx
googleccsdn7 小时前
ESNP LAB 笔记:配置MPLS(Part4)
网络·笔记·网络协议
LUCIAZZZ8 小时前
HTTPS优化简单总结
网络·网络协议·计算机网络·http·https·操作系统
名誉寒冰9 小时前
TCP 拥塞控制与四次挥手解析
网络·网络协议·tcp/ip
Fireplusplus9 小时前
TCP MSS
网络·网络协议·tcp/ip