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 本质:无连接通信,每次收发数据都需要明确对方地址。

相关推荐
斑马1391 小时前
单片机学习笔记(一)——STC89C52的GPIO、LED、数码管、定时器和中断
单片机·stc89c52
安河桥。1 小时前
SOME/IP 技术说明文档
嵌入式硬件·车载系统·信息与通信
Kuakewei8882 小时前
Type-C快充取电芯片产品介绍:PD与QC双协议取电
嵌入式硬件
开发笔记-阿牛2 小时前
做工业报警器语音提示,CK6159A 为什么更合适?
人工智能·stm32·单片机·嵌入式硬件·音频
恶魔泡泡糖3 小时前
stm32F103C8T6标准库串口接收轮询方式1
stm32·单片机·嵌入式硬件
国科安芯3 小时前
寄存器级 Flash 擦除与六轮迭代踩坑实录
嵌入式硬件·mcu·架构·flash·抗辐射·eflash
艾芯微科技3 小时前
B16WS SOD-323 肖特基二极管参数、应用电路与选型踩坑指南
网络·单片机·嵌入式硬件·集成测试·51单片机
天空'之城3 小时前
单片机基础核心知识点汇总(三十七)
单片机·嵌入式硬件
Groundwork Explorer3 小时前
ESP32-C3 SuperMini 排查WIFI收发故障
python·单片机·嵌入式硬件·mcu