UDP 多点通信

一、setsockopt/getsockopt 函数详解

1. 函数原型

c

复制代码
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
2. 功能
  • setsockopt:设置套接字选项(修改网络属性)。
  • getsockopt:获取套接字选项(查询网络属性)。
3. 参数说明
参数 描述
sockfd 套接字文件描述符
level 协议层级: SOL_SOCKET(通用选项) IPPROTO_IP(IP 层选项) IPPROTO_UDP(UDP 层选项)
optname 选项名称(依赖于level
optval 选项值(指针类型,具体类型取决于optname
optlen optval的字节长度(getsockopt需传入指针,setsockopt需传入值)
4. 常用选项及示例
4.1 SOL_SOCKET 层级
  • SO_REUSEADDR :允许重用本地地址和端口(解决端口占用问题)。

    c

    复制代码
    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  • SO_BROADCAST :允许发送广播数据(仅 UDP 可用)。

    c

    复制代码
    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); // 发送方需设置
  • SO_RCVTIMEO/SO_SNDTIMEO :设置接收 / 发送超时时间(struct timeval类型)。

    c

    复制代码
    struct timeval timeout = {3, 0}; // 3秒超时
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
4.2 IPPROTO_IP 层级(组播相关)
  • IP_ADD_MEMBERSHIP :加入组播组。

    c

    复制代码
    struct ip_mreqn mreq;
    mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3"); // 组播IP
    mreq.imr_address.s_addr = inet_addr("192.168.1.100"); // 本地IP
    mreq.imr_ifindex = 0; // 网络接口索引(0表示自动选择)
    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
  • IP_DROP_MEMBERSHIP :退出组播组(参数同IP_ADD_MEMBERSHIP)。

4.3 IPPROTO_TCP 层级
  • TCP_NODELAY :禁用 Nagle 算法(提升实时性)。

    c

    复制代码
    int optval = 1;
    setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));

二、UDP 多点通信

1. 广播通信(Broadcast)
  • 特点

    • 一对多通信,数据发送到局域网内所有主机。
    • 广播地址示例:192.168.1.255(网络号 + 全 1 主机号)或255.255.255.255(受限广播)。
    • 仅 UDP 支持,数据不可跨路由器。
  • 实现步骤

    广播接收者(UDP 服务器)
    1. 创建 UDP 套接字:socket(AF_INET, SOCK_DGRAM, 0)

    2. 绑定广播地址(如192.168.1.2550.0.0.0)。

      c

      复制代码
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(8888);
      addr.sin_addr.s_addr = inet_addr("192.168.1.255");
      bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    3. 接收数据:recvfrom()

    广播发送者(UDP 客户端)
    1. 创建 UDP 套接字。

    2. 设置SO_BROADCAST选项。

    3. 发送数据到广播地址:sendto()

      c

      复制代码
      int optval = 1;
      setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
      struct sockaddr_in dest_addr;
      dest_addr.sin_family = AF_INET;
      dest_addr.sin_port = htons(8888);
      dest_addr.sin_addr.s_addr = inet_addr("192.168.1.255");
      sendto(sockfd, "Hello", 5, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
2. 组播通信(Multicast)
  • 特点

    • 一对一组通信,仅加入组播组的主机可接收数据。
    • 组播 IP 范围:224.0.0.0~239.255.255.255
    • 需通过IP_ADD_MEMBERSHIP加入组播组。
  • 实现步骤

    组播接收者(UDP 服务器)
    1. 创建 UDP 套接字。

    2. 加入组播组:setsockopt()设置IP_ADD_MEMBERSHIP

    3. 绑定组播 IP 和端口(或绑定0.0.0.0接收所有组播数据)。

      c

      复制代码
      struct ip_mreqn mreq;
      mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3");
      mreq.imr_address.s_addr = INADDR_ANY; // 本地IP设为任意
      setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(8888);
      addr.sin_addr.s_addr = INADDR_ANY; // 绑定任意IP
      bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    组播发送者(UDP 客户端)
    1. 创建 UDP 套接字。

    2. 直接发送数据到组播 IP:sendto()

      c

      复制代码
      struct sockaddr_in dest_addr;
      dest_addr.sin_family = AF_INET;
      dest_addr.sin_port = htons(8888);
      dest_addr.sin_addr.s_addr = inet_addr("224.1.2.3");
      sendto(sockfd, "Hello", 5, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));

三、关键对比

特性 广播(Broadcast) 组播(Multicast)
地址范围 局域网内所有主机(如192.168.1.255 组播组(如224.1.2.3
网络层支持 依赖链路层广播 依赖 IP 组播协议
跨路由 不可跨路由 可通过配置跨路由(需路由器支持)
资源消耗 高(所有主机接收) 低(仅组成员接收)
适用场景 局域网内通知(如 DHCP) 实时流媒体、在线会议(如视频直播)

四、代码示例:UDP 广播聊天

1. 广播接收者(服务器)

c

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

#define PORT 8888
#define BUF_SIZE 128

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

    // 允许端口重用
    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    // 绑定广播地址
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = inet_addr("192.168.1.255"); // 或 INADDR_BROADCAST

    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind error"); return -1;
    }

    // 接收广播数据
    char buf[BUF_SIZE] = {0};
    struct sockaddr_in cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    while (1) {
        ssize_t n = recvfrom(sockfd, buf, BUF_SIZE-1, 0, 
                            (struct sockaddr*)&cli_addr, &cli_len);
        if (n > 0) {
            buf[n] = '\0';
            printf("Received: %s\n", buf);
        }
    }
    close(sockfd);
    return 0;
}
2. 广播发送者(客户端)

c

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

#define PORT 8888
#define BUF_SIZE 128

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

    // 允许发送广播
    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));

    // 广播地址
    struct sockaddr_in dest_addr;
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(PORT);
    dest_addr.sin_addr.s_addr = inet_addr("192.168.1.255");

    // 发送数据
    char buf[BUF_SIZE] = {0};
    while (fgets(buf, BUF_SIZE, stdin)) {
        buf[strcspn(buf, "\n")] = 0; // 去除换行符
        sendto(sockfd, buf, strlen(buf), 0, 
               (struct sockaddr*)&dest_addr, sizeof(dest_addr));
        memset(buf, 0, BUF_SIZE);
    }
    close(sockfd);
    return 0;
}

五、总结

  • setsockopt/getsockopt:是网络编程中配置套接字行为的核心函数,通过不同层级和选项可实现端口重用、超时控制、组播加入等高级功能。
  • UDP 广播:适用于局域网内的一对多通信,但存在广播风暴风险,且无法跨路由。
  • UDP 组播:通过组播组实现高效的一对多通信,节省网络资源,适合实时数据传输。
  • 注意事项
    • 广播发送方需设置SO_BROADCAST选项,接收方需绑定广播地址。
    • 组播接收方需通过IP_ADD_MEMBERSHIP加入组播组,发送方直接向组播 IP 发送数据。
相关推荐
uyeonashi1 小时前
【Boost搜索引擎】构建Boost站内搜索引擎实践
开发语言·c++·搜索引擎
再睡一夏就好1 小时前
从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区
linux·服务器·c语言·开发语言·学习笔记
TIF星空2 小时前
【使用 C# 获取 USB 设备信息及进行通信】
开发语言·经验分享·笔记·学习·microsoft·c#
zm3 小时前
TCP 粘包
服务器·网络·php
Smile丶凉轩4 小时前
Qt 界面优化(绘图)
开发语言·数据库·c++·qt
reasonsummer4 小时前
【办公类-100-01】20250515手机导出教学照片,自动上传csdn+最大化、最小化Vs界面
开发语言·python
苏三福6 小时前
ros2 hunmle bag 数据包转为图片数据 python版
开发语言·python·ros2humble
qqxhb7 小时前
零基础学Java——第十一章:实战项目 - 桌面应用开发(JavaFX入门)
java·开发语言·javafx
大神薯条老师7 小时前
Python零基础入门到高手8.4节: 元组与列表的区别
开发语言·爬虫·python·深度学习·机器学习·数据分析
z人间防沉迷k7 小时前
堆(Heap)
开发语言·数据结构·笔记·python·算法