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 发送数据。
相关推荐
BingoGo7 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack8 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo1 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack1 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack3 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab
dustcell.4 天前
haproxy七层代理
java·开发语言·前端