先搞懂:TCP/IP协议栈的分层逻辑
要实现协议栈,得先把分层模型摸透——不是死记“链路层-网络层-传输层-应用层”,而是要明确每一层在代码中的职责:

分层 | 核心职责(代码视角) | 关键协议/函数 |
---|---|---|
链路层 | 帧封装(加MAC头)、物理介质适配 | 以太网帧、ether_header |
网络层 | IP寻址、路由选择、校验和计算 | IPv4、iphdr 、checksum |
传输层 | 可靠(TCP)/不可靠(UDP)数据传输 | TCP(listen /accept )、UDP(sendto /recvfrom ) |
应用层 | 业务数据交互(比如HTTP、FTP) | 自定义协议、recv /send |
举个例子:当你用TCP发送“Hello”时,数据会从应用层→传输层(加TCP头,标记源/目的端口)→网络层(加IP头,标记源/目的IP)→链路层(加MAC头,标记网卡地址),最后变成以太网帧发出去;接收时则反向解封装,一层一层把“包裹”拆开,直到应用层拿到“Hello”。
动手写:传输层实现(TCP/UDP是核心)
传输层是协议栈的“动力心脏”——TCP要保证可靠传输,UDP要追求速度,两者的实现逻辑完全不同。
1. TCP:从三次握手到数据收发(C语言示例)
TCP的核心是连接管理和可靠传输,我们用最基础的socket API写个TCP服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BACKLOG 5
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
if (sockfd == -1) perror("socket"), exit(1);
// 地址复用(避免端口占用错误)
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定端口:注意字节序转换(htons=主机→网络)
struct sockaddr_in server_addr = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = INADDR_ANY // 监听所有网卡
};
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
perror("bind"), exit(1);
listen(sockfd, BACKLOG); // 开始监听,BACKLOG是等待队列长度
printf("TCP server listening on :%d
", PORT);
while (1) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
// 接受连接:返回新的socket(专门处理这个客户端)
int new_fd = accept(sockfd, (struct sockaddr*)&client_addr, &addr_len);
if (new_fd == -1) perror("accept"), continue;
printf("连接来自: %s:%d
",
inet_ntoa(client_addr.sin_addr), // 网络IP→字符串
ntohs(client_addr.sin_port) // 网络端口→主机端口
);
// 收发数据:recv拿客户端消息,send发响应
char buf[1024];
int num = recv(new_fd, buf, sizeof(buf), 0);
if (num > 0) {
buf[num] = '