UDP套接字编程完整标准流程(服务端+客户端)

一、UDP通信核心特点

UDP 是无连接、面向数据报、不可靠的传输层协议。无需提前建立连接,直接通过数据报收发数据,通信开销小、速度快,适用于直播、语音、游戏等对实时性要求高、可容忍少量丢包的场景。

UDP 通信最大特征:服务端必须绑定端口,客户端无需绑定端口,内核自动分配临时端口。

二、UDP 标准通信总流程

1. 服务端流程(固定顺序)

socket() → 填充 serv_addr → bind() → 循环 recvfrom() 获取客户端地址 → sendto() 应答 → close()

2. 客户端流程(固定顺序)

socket() → 填充 serv_addr → sendto() 发送数据 → recvfrom() 接收应答 → close()


三、分步流程原理详解

(一)服务端流程详解

1. socket() 创建UDP套接字

调用 socket() 创建 SOCK_DGRAM 数据报套接字,指定 UDP 协议,生成通信文件描述符。

2. 填充 serv_addr 地址结构体

配置 IPv4 协议族、监听端口、本机任意网卡(INADDR_ANY),清零结构体防止脏数据导致通信异常。

3. bind() 绑定端口

将套接字与本机固定端口绑定,让客户端可以通过该端口精准发送数据到服务端。UDP服务端必须绑定端口。

4. 循环 recvfrom() 获取客户端地址与数据

recvfrom() 是 UDP 核心函数,不仅接收客户端数据,同时自动获取客户端IP和端口信息,为后续应答提供目标地址。

5. sendto() 应答客户端

利用 recvfrom() 获取到的客户端地址,通过 sendto() 向客户端返回应答数据。

6. close() 关闭套接字

通信结束后释放文件描述符,回收系统资源。

(二)客户端流程详解

1. socket() 创建UDP套接字

与服务端创建方式一致,客户端无需绑定端口,由操作系统自动分配临时端口。

2. 填充 serv_addr 服务端地址

手动填写服务端 IP 地址与监听端口,确定数据发送的目标地址。

3. sendto() 发送数据

UDP 无连接特性,无需握手,直接通过 sendto() 向指定服务端地址发送数据报。

4. recvfrom() 接收服务端应答

阻塞等待服务端返回的应答数据,完成一次完整通信交互。

5. close() 关闭套接字

交互结束,释放套接字资源。


四、完整可运行代码

1. UDP服务端代码(严格对应服务端流程)

cs 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 9999
#define BUF_LEN 1024

int main(void)
{
    // 1. socket() 创建UDP套接字
    int srv_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (srv_fd == -1)
    {
        perror("socket");
        return -1;
    }

    // 2. 填充 serv_addr 本地地址结构体
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port   = htons(PORT);
    serv_addr.sin_addr.s_addr = INADDR_ANY;

    // 3. bind() 绑定端口
    if (bind(srv_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
        perror("bind");
        close(srv_fd);
        return -1;
    }
    printf("UDP服务端启动成功,监听端口:%d\n", PORT);

    char recv_buf[BUF_LEN] = {0};
    struct sockaddr_in cli_addr;
    socklen_t cli_addr_len = sizeof(cli_addr);

    while (1)
    {
        memset(recv_buf, 0, BUF_LEN);
        // 4. 循环recvfrom,获取客户端地址 + 接收数据
        ssize_t recv_len = recvfrom(srv_fd, recv_buf, BUF_LEN, 0,
                                   (struct sockaddr *)&cli_addr, &cli_addr_len);
        if (recv_len <= 0)
        {
            perror("recvfrom");
            continue;
        }

        // 打印客户端信息
        char cli_ip[40] = {0};
        inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, sizeof(cli_ip));
        printf("收到客户端 [%s:%d] 消息:%s\n", cli_ip, ntohs(cli_addr.sin_port), recv_buf);

        // 5. sendto() 应答客户端
        char reply[BUF_LEN];
        snprintf(reply, BUF_LEN, "服务端已接收:%s", recv_buf);
        sendto(srv_fd, reply, strlen(reply), 0,
               (struct sockaddr *)&cli_addr, cli_addr_len);
    }

    // 6. close() 关闭套接字
    close(srv_fd);
    return 0;
}

2. UDP客户端代码(严格对应客户端流程)

cs 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 9999
#define BUF_LEN 1024

int main(void)
{
    // 1. socket() 创建UDP套接字
    int cli_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (cli_fd == -1)
    {
        perror("socket");
        return -1;
    }

    // 2. 填充 serv_addr 服务端地址结构体
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr);
    socklen_t serv_addr_len = sizeof(serv_addr);

    char send_buf[BUF_LEN] = {0};
    char recv_buf[BUF_LEN] = {0};

    while (1)
    {
        printf("请输入消息(quit退出):");
        fgets(send_buf, BUF_LEN, stdin);
        send_buf[strcspn(send_buf, "\n")] = '\0';

        if (strcmp(send_buf, "quit") == 0)
            break;

        // 3. sendto() 向服务端发送数据
        sendto(cli_fd, send_buf, strlen(send_buf), 0,
               (struct sockaddr *)&serv_addr, serv_addr_len);

        // 4. recvfrom() 接收服务端应答
        memset(recv_buf, 0, BUF_LEN);
        recvfrom(cli_fd, recv_buf, BUF_LEN, 0, NULL, NULL);
        printf("服务端应答:%s\n\n", recv_buf);
    }

    // 5. close() 关闭套接字
    close(cli_fd);
    return 0;
}

五、编译与运行方法

1. 编译命令

cs 复制代码
gcc udp_server.c -o server 
gcc udp_client.c -o client

2. 运行步骤

第一步:终端1运行服务端

cs 复制代码
./server

第二步:终端2运行客户端,输入内容交互

cs 复制代码
./client

六、核心流程总结

  • 服务端核心:必须 bind 绑定端口,依靠 recvfrom 获取客户端地址,才能回复数据。

  • 客户端核心:无需 bind,直接指定服务端地址,sendto 发数据、recvfrom 收应答。

  • UDP 本质:无连接通信,每次收发数据都需要明确对方地址。

相关推荐
zhangrelay2 小时前
新版|-|单片机原理与接口技术-2026持续改进优化-从MEGA2560-ESP32与Wokwi到ROS2-Lyrical与具身智能栈
笔记·单片机·嵌入式硬件·学习
comyccccc3 小时前
STM32F103C8T6最小系统板设计全解析学习笔记
单片机·1024程序员节
Industio_触觉智能3 小时前
把NPU检测直接嵌进采集终端|RK3576 AI采集终端全链路拆解教程-下
嵌入式硬件·ai·硬件架构·npu·ai视觉·rk3576·采集终端
en.en..4 小时前
STM32 GPIO ODR 与 BSRR 寄存器完整对比详解
单片机·嵌入式硬件
HQB20267 小时前
EG1182|屹晶微电子 60V 内置高压管异步降压 DC‑DC
嵌入式硬件·硬件工程
LCG米8 小时前
STM32硬件SPI驱动SSD1306 OLED实测:上电黑屏排查、页寻址模式与取模扫描方向陷阱
stm32·单片机·嵌入式硬件·嵌入式
深圳老胡9 小时前
STM32C5A3ZG 网络调试总结:从启动失败到连续 Ping 稳定
笔记·stm32·单片机·代码规范
硅基造物主9 小时前
STM32 从 0 到 1 做智能家居(八):PWM 控制风扇、加热片与 SG90 窗帘
stm32·嵌入式硬件·智能家居·hal库·pwm
简单电磁炮9 小时前
关于adc采集
stm32·单片机
HQB202610 小时前
EG1163 (S)|屹晶微电子 600V 高压同步降压半桥控制器
嵌入式硬件·硬件工程