Days37 Linux C 网络编程:UDP、TCP 与 HTTP 协议实战详解

前言

在 Linux C 开发中,网络编程是嵌入式、后台开发非常核心的一块知识点。本文基于 Linux 套接字 socket 编程,从 UDP 通信讲起,梳理 UDP、TCP 全套 API、通信模型、三次握手四次挥手,最后讲解 HTTP 协议报文格式,附带核心代码思路,适合零基础入门网络编程,全部知识点来自实际开发学习笔记。

阅读范围:UDP 通信 → TCP 通信 → HTTP 协议,适合 C 语言有基础,了解 Linux 系统 IO 的同学。

一、UDP 通信(用户数据报协议)

1. UDP 协议特点

  1. 无连接:通信双方不需要提前建立连接,发完数据包就完事。
  2. 不可靠、不安全:没有确认重传机制,数据包可能丢失、乱序。
  3. 资源开销小:头部简单,处理逻辑少,实时性高。
  4. 面向数据报:以数据包为最小单位收发,不能拆分。

适用场景:直播、音视频、DNS 查询等可以容忍少量丢包,追求速度的业务。

2. UDP 核心 API 函数

UDP 通信流程:

发送端:socket()sendto() / recvfrom() 接收端:socket()bind()recvfrom() / sendto()

① socket 创建套接字
复制代码
int socket(int domain, int type, int protocol);
  • domain:协议族,IPv4 填AF_INET
  • type:套接字类型,UDP 使用SOCK_DGRAM(数据报套接字)
  • protocol:协议,填 0 自动匹配对应协议
  • 返回值:成功返回套接字文件描述符;失败返回-1
② sendto 发送 UDP 数据包
复制代码
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
  • sockfd:socket 返回的文件描述符
  • buf:待发送数据缓冲区首地址
  • len:发送数据字节长度
  • flags:标志位,一般写 0
  • dest_addr:对端的 IP + 端口结构体
  • addrlen:地址结构体大小,sizeof(struct sockaddr_in)
  • 返回:成功返回实际发送字节数;失败-1
③ recvfrom 接收 UDP 数据包
复制代码
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
  • src_addr:输出参数,保存发送方的 IP 和端口信息
  • addrlen:传入地址结构体最大长度,函数返回实际读到的地址长度
  • 返回:读到的字节数;0 代表对端关闭;失败-1
④ bind 绑定端口(接收端必须)
复制代码
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

将本机 IP 地址、端口号绑定到 socket 文件描述符上。

注意:只能绑定本机网卡 IP;端口被占用会绑定失败。

⑤ IPv4 地址结构体 sockaddr_in
复制代码
struct sockaddr_in {
    sa_family_t sin_family;    //协议族 AF_INET
    in_port_t sin_port;        //端口号,**必须网络大端字节序**
    struct in_addr sin_addr;   //ip地址(网络字节序)
};
struct in_addr {
    uint32_t s_addr;
};
字节序转换函数

网络统一使用大端字节序,主机字节序(小端)必须转换!

  1. htons():host to network short,把本机 short (16 位端口) 转为网络大端
  2. inet_addr():字符串 IP ("192.168.1.10") → 网络字节序整数 IP
  3. inet_ntoa():网络字节序 IP → 可读字符串 IP

3.UDP 简易通信思路

  • 服务端(接收方):socket 创建 udp 套接字 → 填充sockaddr_in本机端口 IP → bind 绑定 → 循环recvfrom接收数据,拿到客户端地址,可用 sendto 回复消息。
  • 客户端(发送方):socket 创建 udp 套接字,不需要 bind;直接填充服务端 IP 端口,调用 sendto 发送数据。

UDP 没有真正服务端客户端严格区分,bind 只是固定本地端口。

二、TCP 通信(传输控制协议)

1.TCP 协议特点

  1. 面向连接 :通信前必须通过三次握手 建立连接;结束通信执行四次挥手断开连接。
  2. 可靠传输:确认应答、重传机制、滑动窗口、拥塞控制,保证数据不丢、不乱序。
  3. 资源开销大:协议头复杂,需要维护连接状态。
  4. 字节流 :没有数据包边界,会出现粘包问题

适用场景:文件传输、HTTP、数据库等要求数据完整正确的业务。

2.TCP 核心 API

TCP 服务端流程: socket()bind()listen()accept()send()/recv()

TCP 客户端流程: socket()connect()send()/recv()

① connect 客户端建立连接
复制代码
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

向服务端发起三次握手连接请求;addr 填写服务端 IP 端口。成功返回 0。

② listen 设置监听
复制代码
int listen(int sockfd, int backlog);

将 socket 设置为监听状态,等待客户端连接;backlog代表未处理连接队列最大数目。

③ accept 阻塞等待客户端接入
复制代码
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

阻塞函数,有客户端连接到达就返回新的套接字描述符,专门用于和该客户端收发数据。

⚠注意:listen 的 fd 叫监听套接字;accept 返回 fd 叫通信套接字,二者不能混用。addr 输出客户端 IP 端口信息。

④ send /recv 收发 TCP 字节流
复制代码
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

flags 一般传 0;recv 返回0代表对端已经关闭连接。

3.TCP 关键机制

(1)三次握手建立连接
  1. 客户端发送 SYN报文(请求建立连接)
  2. 服务端回复 SYN+ACK(同意连接 + 确认收到客户端 SYN)
  3. 客户端回复 ACK,连接正式建立,开始传输业务数据。

目的:确认双方发送、接收能力都正常;协商初始序列号。

(2)四次挥手断开连接

TCP 是全双工,两边可以独立关闭读写通道:

  1. 主动关闭方发送FIN,告诉对方我不再发送数据
  2. 被动方回复ACK确认 FIN
  3. 被动方业务处理完成,发送自己的FIN
  4. 主动方回复ACK,连接彻底释放。

特殊情况:被动方收到 FIN 之后没有剩余数据要发送,可以合并第二步第三步,变成三次挥手。

(3)粘包问题

TCP 是字节流协议,没有数据包边界,多次发送的数据内核缓冲区会合并,接收方一次读到多组业务数据,就是粘包。

解决粘包常见方案:

  1. 定长包:每次收发固定长度数据;
  2. 增加分隔符 :比如\r\n作为数据结束标记;
  3. 头部携带数据长度:包头存放后续数据字节数,先读头部,再按长度读取数据。

❌简单延时 sleep 不能作为工程解决方案。

(4)TCP 头部标志位

表格

标志 含义
SYN 请求建立连接
FIN 请求关闭连接
ACK 确认应答(绝大多数报文都置 1)
RST 重置异常连接
PSH 推送数据,尽快交给应用层
URG 紧急数据有效

同时 TCP 还有序号、确认序号、滑动窗口、拥塞控制、掉线检测保证传输可靠性。

4.UDP 与 TCP 核心对比

表格

对比项 UDP TCP
连接属性 无连接 面向连接 (三次握手 / 四次挥手)
可靠性 不可靠,丢包不重传 可靠传输,确认 + 重传
传输模式 数据报 字节流,存在粘包
资源开销
适用场景 音视频直播、DNS HTTP、文件传输

三、HTTP 协议(基于 TCP 的应用层协议)

HTTP 是跑在 TCP 之上的应用层协议,浏览器、网络接口请求都使用 HTTP。

1. 模型:C/S 和 B/S

  1. C/S(客户端服务器):独立客户端程序 + 服务端;本地资源,速度快,但是多平台需要单独开发。
  2. B/S(浏览器服务器):浏览器充当客户端;资源全部放在服务端,无需安装客户端,跨平台通用,每次请求都从服务器拉取资源。

HTTP 属于 B/S 模型。

2.URL 统一资源定位符

格式:

复制代码
<协议>://<主机IP/域名>:<端口>/资源路径
  • http 默认端口80;https 加密默认端口443
  • DNS 域名解析会把域名转换为 IP 地址。

3.HTTP 通信完整流程

  1. 浏览器解析 URL 得到域名、端口、路径;DNS 解析域名得到服务器 IP。
  2. 浏览器客户端和服务器建立 TCP 连接(三次握手)
  3. 浏览器发送HTTP 请求报文
  4. 服务器处理请求,返回HTTP 响应报文
  5. TCP 四次挥手断开连接。

4.HTTP 请求报文格式

整体分为:请求行、首部行、空行 CRLF、实体主体(POST 才带数据)

  1. 请求行方法 URL HTTP版本
    • GET:获取服务器资源;
    • POST:向服务器提交数据;PUT、DELETE 等。
  2. 首部行 :键值对,字段名: 值\r\n,可以携带浏览器信息、内容长度、编码。
  3. \r\n:单独一行空行,代表头部结束。
  4. 实体主体:POST 提交的表单、JSON 数据。

示例极简 GET 请求报文:

复制代码
GET /index.html HTTP/1.1
Host: 127.0.0.1:8080
Connection: close

5.HTTP 响应报文格式

分为:状态行、响应首部、空行 CRLF、响应实体主体

  1. 状态行版本 状态码 描述
    • 1xx:提示信息;
    • 2xx:请求成功(最常见 200 OK);
    • 3xx:重定向跳转;
    • 4xx:客户端错误,404 资源找不到;
    • 5xx:服务器内部错误。
  2. 响应首部:服务器返回的元信息,如Content-LengthContent-Type
  3. 空行分割头部和 body。
  4. 实体主体:返回 HTML、JSON、图片二进制数据。

拓展实战小练习

使用 C 语言 socket 写 TCP 客户端,访问天气 API,拿到 HTTP 响应,再使用cJSON库解析返回 JSON,提取天气数据打印终端。 关键点:发送 http GET 请求字符串;从 socket 读取完整响应报文;剥离 HTTP 头部,只解析后面 JSON 内容。

四、总结

  1. UDP 无连接,简单快速,适合实时业务;TCP 面向连接可靠传输,适合业务数据交互。
  2. UDP 使用sendto/recvfrom;TCP 使用connect/listen/accept/send/recv;TCP 必须理解三次握手四次挥手和粘包。
  3. HTTP 构建在 TCP 字节流之上,要掌握请求报文、响应报文结构、状态码含义。
  4. 网络编程务必注意主机字节序、网络字节序转换问题;IPv4 结构体sockaddr_in是所有套接字编程基础。

后续可以自己手写最小版 UDP 聊天程序、TCP 回声服务器,加深对 API 理解。

相关推荐
好评12426 分钟前
【Linux】传输层协议UDP
linux·运维·udp
rcms1527026921829 分钟前
LAM V7668A-131000 单板计算机
网络
GreatVicent43 分钟前
AI Agent互操作标准化加速:A2A协议正式加入Linux基金会
linux·运维·人工智能·agent·aws·a2a
rcms152702692181 小时前
Parker Balston LCMS-5001TNA 气体发生器
网络
我爱cope1 小时前
【计算机网络 | 网络层补充:内部网关协议:RIP 与 OSPF 如何在自治系统内选择路由?】
网络·学习·计算机网络·智能路由器
早九晚十二1 小时前
达梦 DM8 双机实时主备部署
网络
huainingning1 小时前
华三华为锐捷迈普中兴交换机二三层接口切换命令
服务器·网络·华为
是隼人1 小时前
buuctf-pwn ciscn_2019_n_5题解(学习过程持续 更新)
c语言·学习·安全·pwn入门·ctf入门
科技风向标go1 小时前
奥维云网×洛图科技:2026监控品牌“功能延伸”vs垂直品牌“底层设计”——品牌选型与产业趋势
网络·人工智能·科技·户外安防