Skip to content
约 0 字 · 预计阅读 0 分钟

TCP/IP 协议

TCP/IP 协议栈

TCP/IP 是互联网的基础协议,采用分层架构:

┌─────────────────────────────────────────────────────────────┐
│                    应用层 (Application)                      │
│  HTTP / FTP / SMTP / DNS / SSH                              │
├─────────────────────────────────────────────────────────────┤
│                    传输层 (Transport)                        │
│  TCP / UDP                                                  │
├─────────────────────────────────────────────────────────────┤
│                    网络层 (Network)                          │
│  IP / ICMP / ARP / RARP                                     │
├─────────────────────────────────────────────────────────────┤
│                    数据链路层 (Data Link)                    │
│  Ethernet / WiFi / PPP                                      │
├─────────────────────────────────────────────────────────────┤
│                    物理层 (Physical)                         │
│  双绞线 / 光纤 / 无线电波                                    │
└─────────────────────────────────────────────────────────────┘

上述图示展示了 TCP/IP 四层模型。

各层功能:

层次功能典型协议
应用层提供网络服务接口HTTP、FTP、DNS
传输层端到端通信TCP、UDP
网络层路由选择、分组转发IP、ICMP、ARP
数据链路层帧传输、MAC 寻址Ethernet、WiFi

TCP 协议

TCP 报文格式

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
├───────────────────────────────────────────────────────────────┤
│          Source Port          │       Destination Port        │
├───────────────────────────────────────────────────────────────┤
│                        Sequence Number                        │
├───────────────────────────────────────────────────────────────┤
│                    Acknowledgment Number                      │
├───────┬───────┬───────────────┬───────────────────────────────┤
│ Data  │ Reserved│     U     A   P   R   S   F   │          Window            │
│ Offset│      │     R     C   S   S   Y   I   │                            │
│       │      │     G     K   H   T   N   N   │                            │
├───────────────────────────────────────────────────────────────┤
│           Checksum            │         Urgent Pointer        │
├───────────────────────────────────────────────────────────────┤
│                    Options (if present)                       │
├───────────────────────────────────────────────────────────────┤
│                           Data                                │
└───────────────────────────────────────────────────────────────┘

上述图示展示了 TCP 报文格式。

关键字段说明:

字段大小说明
Source/Dest Port各 16 位源/目的端口
Sequence Number32 位序列号,数据字节流编号
Ack Number32 位确认号,期望收到的下一个序列号
Data Offset4 位TCP 头部长度
Flags6 位控制标志(SYN/ACK/FIN/RST 等)
Window16 位接收窗口大小
Checksum16 位校验和

TCP 三次握手

上述时序图展示了 TCP 三次握手过程。

为什么需要三次握手?

原因说明
防止历史连接避免延迟的 SYN 报文导致错误连接
同步序列号双方都需要确认对方的初始序列号
防止重复连接确保双方都准备好建立连接

三次握手状态变化:

客户端状态变化:
CLOSED → SYN_SENT → ESTABLISHED

服务器状态变化:
CLOSED → LISTEN → SYN_RCVD → ESTABLISHED

TCP 四次挥手

上述时序图展示了 TCP 四次挥手过程。

为什么需要四次挥手?

原因说明
半关闭状态TCP 是全双工,每个方向需要单独关闭
服务器可能还有数据服务器收到 FIN 后可能还有数据要发送
确保数据完整确保双方都完成数据发送

四次挥手状态变化:

客户端状态变化:
ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED

服务器状态变化:
ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

TIME_WAIT 状态:

问题说明
为什么需要确保最后的 ACK 到达服务器
持续时间2MSL(Maximum Segment Lifetime)
过多影响占用端口资源,可能导致端口耗尽

TCP 滑动窗口

滑动窗口实现流量控制,防止发送方发送过快导致接收方缓冲区溢出:

发送方窗口:
┌─────────────────────────────────────────────────────────────┐
│                    发送窗口                                  │
│  ┌─────────────────────────────────────────────────────────┐│
│  │  已发送未确认  │  可发送未发送  │  不可发送              ││
│  │  ┌───┬───┬───┐│  ┌───┬───┬───┐│                        ││
│  │  │ 1 │ 2 │ 3 ││  │ 4 │ 5 │ 6 ││                        ││
│  │  └───┴───┴───┘│  └───┴───┴───┘│                        ││
│  └─────────────────────────────────────────────────────────┘│
│        ↑                    ↑                               │
│     窗口左边界           窗口右边界                          │
└─────────────────────────────────────────────────────────────┘

接收方窗口:
┌─────────────────────────────────────────────────────────────┐
│  ┌─────────────────────────────────────────────────────────┐│
│  │  已接收确认  │  接收窗口  │  缓冲区                      ││
│  │  ┌───┬───┬───┐│           │                              ││
│  │  │ 1 │ 2 │ 3 ││           │                              ││
│  │  └───┴───┴───┘│           │                              ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

上述图示展示了滑动窗口的工作原理。

窗口控制机制:

机制说明
窗口通告接收方通过 Window 字段告知发送方可用缓冲区
零窗口接收方缓冲区满时,Window=0,发送方停止发送
窗口探测发送方定期发送探测报文,检测窗口是否恢复

TCP 拥塞控制

拥塞控制防止过多的数据注入网络,避免网络拥塞:

上述流程图展示了 TCP 拥塞控制的状态转换。

四种算法:

算法说明
慢启动cwnd 从 1 开始指数增长,直到达到 ssthresh
拥塞避免cwnd 线性增长,每个 RTT 加 1
快重传收到 3 个重复 ACK 立即重传,不等超时
快恢复快重传后,cwnd 减半,进入拥塞避免

拥塞窗口变化示例:

轮次:   1   2   3   4   5   6   7   8   9   10  11  12  ...
cwnd:   1   2   4   8   16  17  18  19  10  11  12  13  ...
        ↑               ↑   ↑           ↑
      慢启动          拥塞避免       快恢复
                    (ssthresh=16)  (超时,ssthresh=10)

上述示例展示了拥塞窗口的变化过程。

UDP 协议

UDP 报文格式

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
├───────────────────────────────────────────────────────────────┤
│          Source Port          │       Destination Port        │
├───────────────────────────────────────────────────────────────┤
│            Length             │           Checksum            │
├───────────────────────────────────────────────────────────────┤
│                           Data                                │
└───────────────────────────────────────────────────────────────┘

上述图示展示了 UDP 报文格式。

UDP 特点:

特点说明
无连接不需要建立连接,直接发送
不可靠不保证交付,不保证顺序
无拥塞控制网络拥塞时不会降低发送速率
开销小头部只有 8 字节
支持广播可以发送广播和组播

TCP vs UDP

特性TCPUDP
连接面向连接无连接
可靠性可靠传输不可靠
顺序有序无序
流量控制
拥塞控制
头部开销20 字节8 字节
适用场景文件传输、邮件视频流、DNS、游戏

Socket 编程

TCP Socket 编程

c
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

// 服务器端
int server_fd = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in addr = {
    .sin_family = AF_INET,
    .sin_port = htons(8080),
    .sin_addr.s_addr = INADDR_ANY
};

bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 5);

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);

char buffer[1024];
int n = read(client_fd, buffer, sizeof(buffer));
write(client_fd, buffer, n);

close(client_fd);
close(server_fd);

// 客户端
int sock = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in server_addr = {
    .sin_family = AF_INET,
    .sin_port = htons(8080),
    .sin_addr.s_addr = inet_addr("127.0.0.1")
};

connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

write(sock, "Hello", 5);
char buffer[1024];
int n = read(sock, buffer, sizeof(buffer));

close(sock);

上述代码展示了 TCP Socket 编程的基本流程。

Socket API 说明:

函数说明
socket()创建 Socket
bind()绑定地址和端口
listen()监听连接
accept()接受连接
connect()发起连接
read()/write()读写数据
close()关闭 Socket

UDP Socket 编程

c
// 服务器端
int sock = socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in addr = {
    .sin_family = AF_INET,
    .sin_port = htons(8080),
    .sin_addr.s_addr = INADDR_ANY
};

bind(sock, (struct sockaddr*)&addr, sizeof(addr));

char buffer[1024];
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);

int n = recvfrom(sock, buffer, sizeof(buffer), 0,
                 (struct sockaddr*)&client_addr, &client_len);
sendto(sock, buffer, n, 0,
       (struct sockaddr*)&client_addr, client_len);

close(sock);

// 客户端
int sock = socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in server_addr = {
    .sin_family = AF_INET,
    .sin_port = htons(8080),
    .sin_addr.s_addr = inet_addr("127.0.0.1")
};

sendto(sock, "Hello", 5, 0,
       (struct sockaddr*)&server_addr, sizeof(server_addr));

char buffer[1024];
int n = recvfrom(sock, buffer, sizeof(buffer), 0, NULL, NULL);

close(sock);

上述代码展示了 UDP Socket 编程的基本流程。

网络编程模型

I/O 多路复用

c
#include <sys/select.h>

fd_set readfds;
struct timeval timeout;

while (1) {
    FD_ZERO(&readfds);
    FD_SET(sock1, &readfds);
    FD_SET(sock2, &readfds);
    
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    
    int ret = select(max(sock1, sock2) + 1, &readfds, NULL, NULL, &timeout);
    
    if (ret > 0) {
        if (FD_ISSET(sock1, &readfds)) {
            // sock1 可读
        }
        if (FD_ISSET(sock2, &readfds)) {
            // sock2 可读
        }
    }
}

上述代码展示了 select 多路复用的使用方式。

I/O 多路复用对比:

方式最大连接数效率特点
select1024O(n)跨平台,效率低
poll无限制O(n)无最大连接数限制
epoll无限制O(1)Linux 特有,高效

epoll 编程

c
#include <sys/epoll.h>

int epfd = epoll_create1(0);

struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev);

struct epoll_event events[100];
while (1) {
    int nfds = epoll_wait(epfd, events, 100, -1);
    
    for (int i = 0; i < nfds; i++) {
        if (events[i].data.fd == listen_sock) {
            // 新连接
            int client_sock = accept(listen_sock, NULL, NULL);
            ev.events = EPOLLIN | EPOLLET;  // 边缘触发
            ev.data.fd = client_sock;
            epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &ev);
        } else {
            // 读取数据
            read(events[i].data.fd, buffer, sizeof(buffer));
        }
    }
}

上述代码展示了 epoll 的使用方式。

epoll 工作模式:

模式说明特点
LT (Level Triggered)水平触发默认模式,只要可读就一直触发
ET (Edge Triggered)边缘触发状态变化时触发,需一次性读完

总结

概念说明
TCP面向连接、可靠传输、流量控制、拥塞控制
UDP无连接、不可靠、开销小、支持广播
三次握手SYN → SYN+ACK → ACK
四次挥手FIN → ACK → FIN → ACK
滑动窗口流量控制,防止接收方缓冲区溢出
拥塞控制慢启动、拥塞避免、快重传、快恢复

参考资料

[1] Computer Networking: A Top-Down Approach. James F. Kurose

[2] TCP/IP Illustrated. W. Richard Stevens

[3] UNIX Network Programming. W. Richard Stevens

相关主题

基于 VitePress 构建