You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单网卡Ubuntu主机UDP组播测试异常:环回发送NIC接收故障排查

Ubuntu单NIC环境下UDP组播测试失败排查

场景描述

在Ubuntu个人环境中测试UDP组播,仅有1块NIC,计划从环回设备发送组播数据,由NIC接收。已执行以下配置命令:

ifconfig lo multicast
ip route add 239.0.0.2 dev lo
route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
ifconfig lo multicast

同时编写了sender.c和listener.c代码:

sender.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    const int delay_secs = 0;
    const char *message2 = "Hello, World!";

    unsigned long counter = 0;

    char message[8192] = {0};

    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        perror("socket(1)");
        return 1;
    }

    struct ip_mreq mreq;
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_interface.s_addr = inet_addr("192.168.2.6"); 

    if (
        setsockopt(
            fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt(1)");
        return 1;
    }

    char loopch = 1;

        if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)
        {
        perror("setsockopt(2)");
        return 1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("239.0.0.2");
    addr.sin_port = htons(1234);

    while (1)
    {
        char ch = 0;
        int len=snprintf(message, 8192, "%s-%lu", message2, ++counter);
        message[len]='\0';
        int nbytes = sendto(
            fd,
            message,
            len,
            0,
            (struct sockaddr *)&addr,
            sizeof(addr));
        if (nbytes < 0)
        {
            perror("sendto");
            return 1;
        }
        sleep(delay_secs);
    }

    return 0;
}

listener.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define MSGBUFSIZE 8192

int main()
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        perror("socket");
        return 1;
    }

    u_int yes = 1;
    if (
        setsockopt(
            fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0)
    {
        perror("Reusing ADDR failed");
        return 1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("239.0.0.2"); 
    addr.sin_port = htons(1234);

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

    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.2");
    mreq.imr_interface.s_addr = inet_addr("192.168.2.6"); //local wifi address
    if (
        setsockopt(
            fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt");
        return 1;
    }

    while (1)
    {
        char msgbuf[MSGBUFSIZE];
        int addrlen = sizeof(addr);
        int nbytes = recvfrom(
            fd,
            msgbuf,
            MSGBUFSIZE,
            0,
            (struct sockaddr *)&addr,
            &addrlen);
        if (nbytes < 0)
        {
            perror("recvfrom");
            return 1;
        }
        msgbuf[nbytes] = '\0';
        puts(msgbuf);
    }

    return 0;
}

但测试未达到预期效果,问题出在哪里?


问题根源分析与修复方案

1. 发送端接口配置与需求矛盾

你的发送端通过IP_MULTICAST_IF指定了物理网卡192.168.2.6作为组播发送接口,但需求是从环回设备lo发送数据。这导致组播数据包直接从物理网卡发出,完全不经过环回设备,和测试逻辑不符。

修复:将发送端的IP_MULTICAST_IF配置改为环回地址127.0.0.1:

struct ip_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_interface.s_addr = inet_addr("127.0.0.1"); // 替换为环回地址

2. 路由配置阻断物理网卡接收

你配置了route add -net 224.0.0.0 netmask 240.0.0.0 dev lo,这条命令会让所有组播流量都走环回设备,物理网卡根本接收不到组播包,和“由NIC接收”的需求完全冲突。同时需要开启系统组播转发,让环回的组播包能转发到物理网卡。

修复:

  • 删除错误路由:
    route del -net 224.0.0.0 netmask 240.0.0.0 dev lo
    
  • 开启组播转发:
    echo 1 > /proc/sys/net/ipv4/ip_forward
    echo 1 > /proc/sys/net/ipv4/conf/all/mc_forwarding
    

3. 监听端绑定地址限制流量接收

监听端直接绑定到组播地址239.0.0.2,会限制仅接收该地址的流量,但无法确保能接收到从环回转发过来的组播包。绑定到INADDR_ANY(0.0.0.0)可以接收所有接口上的该端口组播流量,更符合测试场景。

修复:修改监听端的绑定地址:

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 替换为INADDR_ANY
addr.sin_port = htons(1234);

4. 物理网卡组播功能验证

确保物理网卡的组播功能已开启:

ifconfig wlan0 multicast # 替换为你的物理网卡名称,比如eth0

最终测试步骤

  1. 完成上述配置和代码修改
  2. 编译程序:
    gcc sender.c -o sender
    gcc listener.c -o listener
    
  3. 先启动监听端,再启动发送端,查看是否能正常接收组播消息

内容的提问来源于stack exchange,提问作者Nagaraj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 20:05:09