UNIX下C语言编程与实践62-UNIX UDP 编程:socket、bind、sendto、recvfrom 函数的使用

在 UNIX 网络编程中,UDP(用户数据报协议)以其"无连接、轻量级"的特性,成为实时性要求高(如流媒体、游戏)场景的首选。 udps1.c(UDP 服务器端)与 udpk1.c(UDP 客户端)的核心实例,本文将详细讲解 UDP 编程的四个核心函数------socket(创建数据报套接字)、bind(绑定地址端口)、sendto(发送数据报)、recvfrom(接收数据报),通过完整代码演示 UDP 通信流程,深入解析 UDP 与 TCP 编程的差异,并梳理常见错误与拓展场景。

一、UDP 编程核心函数解析

UDP 编程与 TCP 的核心差异在于"无连接"特性------无需 listenacceptconnect 流程,直接通过 sendtorecvfrom 完成数据收发。以下从函数原型、参数含义、实例关联三个维度展开解析。

1.1 socket 函数:创建 UDP 数据报套接字

UDP 套接字的创建是 UDP 编程的第一步,与 TCP 不同,其类型需指定为 SOCK_DGRAM(数据报套接字),以匹配 UDP 协议的无连接特性。

(1)函数原型与参数

函数定义在 <sys/socket.h> 头文件中,原型与 TCP 共享,但参数取值不同:

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

UDP 场景下三个参数的核心取值与实例对应关系如下表所示:

参数名 数据类型 UDP 场景取值 实例中的使用说明
domain int AF_INET UDP 编程唯一使用的协议族,对应 IPv4 网络,支持 UDP 数据报传输(与 TCP 一致)
type int SOCK_DGRAM 数据报套接字,对应 UDP 协议,无连接、不可靠、面向数据报( udps1.cudpk1.c 均使用此类型,区别于 TCP 的 SOCK_STREAM
protocol int 0 由内核根据 domainAF_INET)与 typeSOCK_DGRAM)自动选择 UDP 协议(无手动指定场景)
(2)返回值与关键说明

执行成功时返回非负的套接字描述符 (用于后续 bindsendtorecvfrom 操作);失败时返回 -1,并通过 errno 标识错误(如 EAFNOSUPPORT 表示协议族不支持)。

实例印证udps1.c 中通过 socket(AF_INET, SOCK_DGRAM, 0) 创建服务器端套接字,udpk1.c 客户端创建套接字的方式完全相同,体现了 UDP 客户端与服务器端套接字类型的一致性。

1.2 bind 函数:UDP 服务器端的地址绑定

UDP 服务器端必须调用 bind 函数将套接字绑定到固定的本地端口------客户端需要通过该端口定位服务器,而 UDP 客户端通常无需绑定(系统自动分配临时端口)。

(1)函数原型与参数

函数原型与 TCP 完全一致,但 UDP 场景下的使用场景更单一(仅服务器端必要),参数含义如下(与文中 CreateUdpSock 函数逻辑一致):

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);

三个参数的 UDP 场景解读与实例关联如下表所示:

参数名 数据类型 UDP 服务器端使用逻辑 实例代码片段
sockfd int socket 函数返回的 UDP 套接字描述符(服务器端专用) CreateUdpSock 函数中 *pnSock(创建的 UDP 套接字)
my_addr struct sockaddr * 指向 sockaddr_in 结构的指针,存储服务器端本地 IP 与端口(需强制转换为通用地址结构) struct sockaddr_in addrin; memset(&addrin, 0, sizeof(addrin)); addrin.sin_family = AF_INET; addrin.sin_addr.s_addr = htonl(INADDR_ANY); addrin.sin_port = htons(nPort);(绑定所有本地 IP 与指定端口)
addrlen socklen_t sockaddr_in 结构的字节大小,告诉内核地址结构的长度 sizeof(addrin)CreateUdpSock 函数的取值)
(2)UDP 与 TCP 绑定的差异

通过实例可总结两者差异: - TCPbind 后需调用 listen 进入侦听状态; - UDPbind 后直接进入数据接收状态(无需侦听,因无连接),客户端可随时发送数据报。

1.3 sendto 函数:发送 UDP 数据报

UDP 无连接特性决定了其发送数据时需明确指定目标地址------sendto 函数正是为此设计,它将数据报发送到指定的服务器端地址,无需提前建立连接(区别于 TCP 的 send)。

(1)函数原型与参数

函数定义在 <sys/socket.h> 头文件中,原型如下(文中 SendMsgByUdp 函数的核心封装对象):

复制代码
#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

六个参数的 UDP 场景含义与实例对应关系如下表所示:

参数名 数据类型 核心作用 实例(SendMsgByUdp 函数)
s int UDP 套接字描述符(客户端/服务器端均可使用) 客户端创建的临时套接字(nSock = socket(AF_INET, SOCK_DGRAM, 0)
msg const void * 指向待发送数据缓冲区的指针(如字符串、二进制数据) 客户端要发送的字符串(如 "第0次发送"
len size_t 待发送数据的字节数(不含字符串结束符 '\0' strlen(szBuf)(文中客户端发送字符串的长度)
flags int 发送标志,通常设为 0(默认方式),特殊场景用 MSG_DONTWAIT(非阻塞) 文中设为 0(默认阻塞发送,数据交给内核即返回)
to const struct sockaddr * 指向目标地址结构(sockaddr_in)的指针,存储服务器端 IP 与端口 初始化服务器端地址:addrin.sin_addr.s_addr = inet_addr(szAddr); addrin.sin_port = htons(nPort);
tolen socklen_t 目标地址结构(sockaddr_in)的字节大小 sizeof(struct sockaddr)(文中通用取值)
(2)返回值与关键特性

执行成功时返回实际发送的字节数;失败时返回 -1。文中特别强调 UDP sendto 的"成功"含义------仅表示数据已交给内核发送缓冲区,不保证对方能接收 (UDP 无确认机制),这是与 TCP send(成功表示数据已写入 TCP 发送缓冲区,TCP 会负责重传)的核心差异。

1.4 recvfrom 函数:接收 UDP 数据报

UDP 服务器端通过 recvfrom 函数接收客户端发送的数据报,同时获取发送方(客户端)的地址信息,以便后续回复(如回声服务器)。文中 RecvMsgByUdp 函数正是对该函数的封装。

(1)函数原型与参数

函数定义在 <sys/socket.h> 头文件中,原型如下(与 sendto 参数结构对称):

复制代码
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

六个参数的 UDP 场景含义与实例对应关系如下表所示:

参数名 数据类型 核心作用 实例(RecvMsgByUdp 函数)
s int 已绑定端口的 UDP 套接字描述符(服务器端专用) udps1.cCreateUdpSock 返回的 nSock
buf void * 指向接收数据缓冲区的指针,需预先分配内存 char szBuf[256];(文中服务器端接收缓冲区)
len size_t 接收缓冲区的最大容量(字节数),避免数据溢出 sizeof(szBuf)(文中服务器端缓冲区大小)
flags int 接收标志,通常设为 0(默认阻塞),非阻塞场景用 MSG_DONTWAIT 文中设为 0(默认阻塞,直到接收数据报或出错)
from struct sockaddr * 输出参数,指向 sockaddr_in 结构,存储发送方(客户端)的地址信息 文中简化处理设为 NULL(仅接收数据,不回复);若需回复,需初始化该结构
fromlen socklen_t * 输入输出参数,传入时为 from 结构的大小,返回时为实际地址结构的大小 文中简化处理设为 NULL;若需回复,需设为 &from_lenfrom_len = sizeof(struct sockaddr_in)
(2)返回值与关键特性

执行成功时返回实际接收的字节数;失败时返回 -1;无数据且连接关闭(UDP 无连接,此情况极少)时返回 0。文中强调其默认阻塞特性 ------若套接字无数据可接收,recvfrom 会一直阻塞,直到有数据报到达或被信号中断(如 SIGINT)。

二、实战实例:UDP 服务器端与客户端完整流程

文中 udps1.cudpk1.cudp.h 头文件的设计思想,编写完整的 UDP 服务器端与客户端程序,演示"客户端循环发送数据报→服务器端接收并打印"的完整流程,还原文中的编程风格与核心逻辑。

2.1 头文件封装(udp.h)

文中的封装思路,将 UDP 核心函数(创建套接字、发送数据、接收数据)声明在头文件中,提高代码复用性:

复制代码
#ifndef _UDP_H_
#define _UDP_H_

#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 创建 UDP 服务器端套接字(绑定指定端口)
int CreateUdpSock(int *pnSock, int nPort);

// 发送 UDP 数据报到指定服务器
int SendMsgByUdp(void *pMsg, int nSize, const char *szAddr, int nPort);

// 从 UDP 套接字接收数据报
int RecvMsgByUdp(int nSock, void *pData, int *pnSize);

#endif

2.2 UDP 服务器端程序(udps1.c)

服务器端流程:创建 UDP 套接字→绑定本地端口(9999)→循环调用 recvfrom 接收数据→打印数据内容,符合文中 udps1.c 的核心逻辑:

复制代码
#include "udp.h"

// 实现 CreateUdpSock:创建并绑定 UDP 服务器端套接字
int CreateUdpSock(int *pnSock, int nPort) {
    struct sockaddr_in addrin;
    struct sockaddr *paddr = (struct sockaddr *)&addrin;

    // 参数合法性检查
    assert(pnSock != NULL && nPort > 0 && nPort <= 65535);
    memset(&addrin, 0, sizeof(addrin));

    // 1. 创建 UDP 套接字
    *pnSock = socket(AF_INET, SOCK_DGRAM, 0);
    if (*pnSock <= 0) {
        perror("socket failed");
        return 1;
    }

    // 2. 初始化地址结构(绑定所有本地 IP + 指定端口)
    addrin.sin_family = AF_INET;
    addrin.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定所有本地 IP
    addrin.sin_port = htons(nPort);             // 端口转换为网络字节顺序

    // 3. 绑定套接字到本地地址
    if (bind(*pnSock, paddr, sizeof(addrin)) != 0) {
        perror("bind failed");
        close(*pnSock);
        return 1;
    }

    printf("UDP server bind port %d success\n", nPort);
    return 0;
}

// 实现 RecvMsgByUdp:接收 UDP 数据报
int RecvMsgByUdp(int nSock, void *pData, int *pnSize) {
    assert(nSock > 0 && pData != NULL && pnSize != NULL && *pnSize > 0);

    // 接收数据报(简化处理,不获取发送方地址)
    ssize_t recv_len = recvfrom(nSock, pData, *pnSize, 0, NULL, NULL);
    if (recv_len < 0) {
        perror("recvfrom failed");
        return 1;
    } else if (recv_len == 0) {
        printf("no data received (connection closed)\n");
        return 1;
    }

    // 更新实际接收的字节数
    *pnSize = recv_len;
    return 0;
}

// 服务器端主函数:循环接收客户端数据
int main() {
    int nSock;
    char szBuf[256];
    int nBufSize = sizeof(szBuf);

    // 1. 创建并绑定 UDP 套接字(端口 9999)
    if (CreateUdpSock(&nSock, 9999) != 0) {
        fprintf(stderr, "Create UDP server failed\n");
        return 1;
    }

    // 2. 循环接收数据
    printf("UDP server waiting for data...\n");
    while (1) {
        // 重置缓冲区
        memset(szBuf, 0, sizeof(szBuf));
        nBufSize = sizeof(szBuf);

        // 接收数据报
        if (RecvMsgByUdp(nSock, szBuf, &nBufSize) != 0) {
            continue;
        }

        // 打印接收的数据
        printf("Received UDP data: [len=%d] %s\n", nBufSize, szBuf);
    }

    // 理论上不会执行到此处(循环无退出条件)
    close(nSock);
    return 0;
}

2.3 UDP 客户端程序(udpk1.c)

客户端流程:创建 UDP 套接字→循环调用 sendto 发送数据(每隔 1 秒)→打印发送结果,符合文中 udpk1.c 的周期性发送逻辑:

复制代码
#include "udp.h"
#include 

// 实现 SendMsgByUdp:发送 UDP 数据报到服务器
int SendMsgByUdp(void *pMsg, int nSize, const char *szAddr, int nPort) {
    int nSock;
    struct sockaddr_in addrin;

    // 参数合法性检查
    assert(pMsg != NULL && nSize > 0 && szAddr != NULL && nPort > 0 && nPort <= 65535);
    memset(&addrin, 0, sizeof(addrin));

    // 1. 创建 UDP 客户端套接字(无需绑定,系统自动分配临时端口)
    nSock = socket(AF_INET, SOCK_DGRAM, 0);
    if (nSock <= 0) {
        perror("socket failed");
        return 1;
    }

    // 2. 初始化服务器端地址结构
    addrin.sin_family = AF_INET;
    // IP 地址转换:字符串 → 网络字节顺序整数
    if (inet_aton(szAddr, &addrin.sin_addr) == 0) {
        fprintf(stderr, "invalid server IP: %s\n", szAddr);
        close(nSock);
        return 1;
    }
    // 端口转换为网络字节顺序
    addrin.sin_port = htons(nPort);

    // 3. 发送 UDP 数据报
    ssize_t send_len = sendto(nSock, pMsg, nSize, 0, 
                             (struct sockaddr *)&addrin, sizeof(addrin));
    if (send_len != nSize) {
        perror("sendto failed");
        close(nSock);
        return 1;
    }

    // 4. 关闭临时套接字(客户端每次发送可创建新套接字)
    close(nSock);
    return 0;
}

// 客户端主函数:循环发送数据到服务器
int main() {
    int i = 0;
    char szBuf[100];
    const char *server_ip = "127.0.0.1"; // 服务器端 IP(本地回环)
    int server_port = 9999;              // 服务器端端口

    // 循环发送数据(每隔 1 秒)
    while (1) {
        // 构造发送数据
        snprintf(szBuf, sizeof(szBuf), "第%d次发送", i);
        int data_len = strlen(szBuf);

        // 发送数据报
        if (SendMsgByUdp(szBuf, data_len, server_ip, server_port) == 0) {
            printf("Send success: %s\n", szBuf);
        } else {
            printf("Send failed: %s\n", szBuf);
        }

        // 休眠 1 秒
        sleep(1);
        i++;
    }

    return 0;
}

2.4 程序执行与结果

  1. 编译程序

    编译服务器端

    gcc udps1.c -o udps1

    编译客户端

    gcc udpk1.c -o udpk1

  2. 启动服务器端

    ./udps1
    UDP server bind port 9999 success
    UDP server waiting for data...

  3. 启动客户端(新终端)

    ./udpk1
    Send success: 第0次发送
    Send success: 第1次发送
    Send success: 第2次发送
    Send success: 第3次发送
    ...

  4. 服务器端输出udps1.c 执行结果一致):

    Received UDP data: [len=9] 第0次发送
    Received UDP data: [len=9] 第1次发送
    Received UDP data: [len=9] 第2次发送
    Received UDP data: [len=9] 第3次发送
    ...

三、UDP 编程关键特性与差异解析

基于实例与 UDP 协议特性,梳理 UDP 编程与 TCP 的核心差异,以及 UDP 客户端/服务器端的特殊设计逻辑,帮助理解 UDP 的适用场景。

3.1 UDP 服务器端绑定端口的必要性

文中反复强调,UDP 服务器端必须调用 bind 绑定固定端口,原因如下: - 客户端定位服务器的需要 :UDP 无连接,客户端发送数据报时必须知道服务器端的 IP 与端口(如文中客户端指定服务器端口 9999),若服务器端不绑定固定端口,客户端无法定位; - 避免端口动态变化 :若服务器端不绑定端口,系统会自动分配临时端口,但服务器端重启后端口可能变化,导致客户端无法连接; - 端口复用与业务标识:固定端口便于业务识别(如 DNS 用 53 端口、DHCP 用 67/68 端口),文中选择 9999 作为示例端口,正是基于固定端口的设计思想。

3.2 UDP 客户端不绑定端口的原因

文中 udpk1.c 客户端未调用 bind,而是由系统自动分配临时端口,原因如下: - 简化客户端逻辑 :客户端无需管理端口,系统分配的临时端口(通常在 1024~65535 之间)可避免端口冲突; - 无固定端口需求 :服务器端接收数据报后,若需回复,可通过 recvfrom 获取客户端的临时端口,无需客户端绑定固定端口; - 多客户端并发:多个客户端同时运行时,系统分配不同的临时端口,避免端口冲突(若客户端绑定固定端口,多实例运行会失败)。

实例印证 :文中 udpk1.c 客户端每次发送数据都创建新套接字,系统自动分配临时端口,发送完成后关闭套接字,完全无需绑定操作,体现了客户端的简化设计。

3.3 UDP 与 TCP 编程核心差异

对比维度 UDP 编程(实例) TCP 编程(实例)
套接字类型 SOCK_DGRAM(数据报套接字) SOCK_STREAM(流式套接字)
连接流程 无连接,无需 listen/accept/connect 面向连接,需 socket→bind→listen→accept(服务器端)、socket→connect(客户端)
数据收发函数 sendto(需指定目标地址)、recvfrom(可获取发送方地址) sendrecv(基于已连接套接字,无需指定地址)
数据可靠性 不可靠,sendto 成功不保证对方接收(无确认、重传) 可靠,TCP 协议保证数据有序、无丢失、无重复
数据边界 有边界,recvfrom 一次接收一个完整数据报 无边界,recv 可能分批次接收数据(需处理粘包)
服务器端核心逻辑 socket→bind→循环 recvfromudps1.c socket→bind→listen→循环 accept→处理连接tcp1.c

四、UDP 编程常见错误与解决方案

结合文中的错误处理思想(如 VerifyErr 宏)与 UDP 协议特性,梳理 socketbindsendtorecvfrom 函数使用过程中常见的错误场景,分析原因并给出解决方案。

  • 错误 1:sendto 发送失败,errno = EINVAL

    原因: - 目标地址结构(sockaddr_in)初始化错误(如未设置 sin_family = AF_INET,或端口号未转换为网络字节顺序); - tolen 参数取值错误(如设为 sizeof(int),而非地址结构大小);

    解决方案: 1. 确保地址结构初始化完整:

    复制代码
    struct sockaddr_in addrin;
    memset(&addrin, 0, sizeof(addrin));
    addrin.sin_family = AF_INET; // 必须设置为 AF_INET
    addrin.sin_addr.s_addr = inet_addr("127.0.0.1");
    addrin.sin_port = htons(9999); // 端口必须转换为网络字节顺序
    1. tolen 参数设为 sizeof(struct sockaddr_in)sizeof(struct sockaddr),确保与地址结构匹配。
  • 错误 2:recvfrom 接收数据截断,实际长度小于发送长度

    原因:接收缓冲区(buf)大小(len 参数)小于 UDP 数据报的实际长度,导致超出部分被内核截断(UDP 无数据分片重组机制,超出缓冲区的数据直接丢弃);

    解决方案: 1. 接收缓冲区大小设为 UDP 最大数据报长度(通常为 65507 字节,因 IP 数据报最大为 65535 字节,减去 IP 头 20 字节与 UDP 头 8 字节); 2. 接收前通过 setsockopt 设置 UDP 接收缓冲区大小,避免内核缓冲区溢出:

    复制代码
    int recv_buf_size = 65507;
    setsockopt(nSock, SOL_SOCKET, SO_RCVBUF, &recv_buf_size, sizeof(recv_buf_size));
    1. 文中 udps1.c 使用 256 字节缓冲区,适用于小数据场景;若传输大数据,需增大缓冲区。
  • 错误 3:UDP 数据报丢失,服务器端未接收但客户端发送成功

    原因: - UDP 协议本身不可靠,网络丢包、延迟会导致数据报丢失; - 服务器端未绑定正确端口或 IP,客户端发送到错误地址; - 服务器端 recvfrom 调用不及时,内核接收缓冲区满,新数据报被丢弃;

    解决方案: 1. 应用层实现确认机制:服务器端接收数据后,通过 sendto 回复确认信息,客户端未收到确认则重发(文中未涉及,但工业级 UDP 应用必备); 2. 验证客户端发送地址:确保服务器端 IP 与端口正确(如文中客户端指定 127.0.0.1:9999); 3. 增大内核 UDP 接收缓冲区:通过 setsockopt 设置 SO_RCVBUF,减少缓冲区满导致的丢包。

  • 错误 4:UDP 服务器端 bind 失败,errno = EADDRINUSE

    原因:服务器端要绑定的端口已被其他进程占用(如文中重复启动 udps1.c,9999 端口已被占用);

    解决方案: 1. 通过 netstat -uln | grep <port> 查看端口占用情况(如 netstat -uln | grep 9999),停止占用进程; 2. 设置 SO_REUSEADDR 选项,允许端口复用(即使端口处于 TIME_WAIT 状态):

    复制代码
    int opt = 1;
    setsockopt(nSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    1. 更换未被占用的端口(如文中使用 9999,可改为 8888)。

五、拓展:UDP 广播与多播编程

文中未涉及 UDP 广播与多播,但基于 UDP 的无连接特性,这两种通信方式在局域网场景(如设备发现、实时通知)中广泛应用。以下简要介绍其实现原理与编程方法,拓展 UDP 编程的应用范围。

5.1 UDP 广播

原理 :UDP 广播是指客户端向局域网内的所有主机发送数据报(目标地址为广播地址,如 192.168.1.255),局域网内所有绑定对应端口的 UDP 服务器端均可接收。

编程关键步骤: 1. 创建 UDP 套接字(与普通 UDP 一致); 2. 设置套接字允许广播(默认不允许):

复制代码
int opt = 1;
setsockopt(nSock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
  1. 发送数据报到广播地址(如 192.168.1.255:9999):

    struct sockaddr_in broadcast_addr;
    memset(&broadcast_addr, 0, sizeof(broadcast_addr));
    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_addr.s_addr = inet_addr("192.168.1.255"); // 广播地址
    broadcast_addr.sin_port = htons(9999);
    sendto(nSock, "broadcast data", strlen("broadcast data"), 0,
    (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr));

  2. 服务器端绑定对应端口(9999),即可接收广播数据报。

5.2 UDP 多播

原理 :UDP 多播(组播)是指客户端向特定的多播组地址(如 224.0.0.0~239.255.255.255)发送数据报,只有加入该多播组的 UDP 服务器端才能接收,避免广播的"泛洪"问题。

编程关键步骤: 1. 服务器端加入多播组:

复制代码
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.1"); // 多播组地址
mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 绑定的本地网卡
// 加入多播组
setsockopt(nSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
  1. 客户端发送数据报到多播组地址(224.0.0.1:9999); 3. 服务器端绑定对应端口(9999),即可接收多播数据报; 4. 服务器端退出时离开多播组:

    setsockopt(nSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));

六、总结

基于《精通UNIX下C语言编程与项目实践笔记.pdf》中 UDP 编程的核心实例,本文系统梳理了 UDP 编程的四个核心函数与关键特性,可总结为以下关键点:

  • 函数核心作用socket 创建 SOCK_DGRAM 类型套接字,bind 为服务器端绑定固定端口,sendto 向指定地址发送数据报,recvfrom 接收数据报并可选获取发送方地址,四者共同构成 UDP 通信的基础;
  • 客户端/服务器端差异 :服务器端必须 bind 固定端口(客户端定位需要),客户端无需绑定(系统分配临时端口),体现了 UDP 简化客户端设计的思想;
  • 协议特性影响 :UDP 无连接、不可靠的特性导致 sendto 成功不保证接收,recvfrom 需处理数据截断,实际应用需在应用层补充确认、重传机制;
  • 拓展场景:UDP 广播与多播基于普通 UDP 编程扩展,适用于局域网内多设备通信,是 UDP 协议在实时场景中的重要应用。

掌握 UDP 编程的核心函数与特性,是理解 UNIX 网络编程中"无连接"通信模型的关键。在实际开发中,需结合文中的简化设计思想(如客户端无需绑定),针对 UDP 的不可靠性补充应用层保障机制,并根据场景选择普通 UDP、广播或多播,才能构建出高效、稳定的 UDP 应用程序。

相关推荐
DIY全栈开发4 小时前
《MCU职位》面试问题
单片机·嵌入式硬件·面试
清风6666666 小时前
基于单片机的智能点滴输液速度与液位控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计
小龙报6 小时前
《彻底理解C语言指针全攻略(2)》
c语言·开发语言·c++·visualstudio·github·学习方法
电子工程师成长日记-C518 小时前
基于51单片机的交通灯智能调节系统
单片机·嵌入式硬件·51单片机
迎風吹頭髮8 小时前
UNIX下C语言编程与实践60-UNIX TCP 套接字关闭:close 与 shutdown 函数的区别与使用场景
c语言·网络·unix
点灯小铭8 小时前
基于单片机的智能水瓶温度控制系统
单片机·嵌入式硬件·毕业设计·课程设计
点灯小铭9 小时前
基于单片机的四点位水位控制与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计
是大强9 小时前
肖特基二极管作用及应用
单片机·嵌入式硬件
是大强9 小时前
stm32 vdd引脚和vss引脚连锡会短路
stm32·单片机·嵌入式硬件