如果启用了环回,为啥发送方收不到它的多播 UDP 数据包?

Posted

技术标签:

【中文标题】如果启用了环回,为啥发送方收不到它的多播 UDP 数据包?【英文标题】:Why doesn't sender receive its multicast UDP packet if loopback is enabled?如果启用了环回,为什么发送方收不到它的多播 UDP 数据包? 【发布时间】:2014-09-18 22:01:16 【问题描述】:

过去两天一直在努力解决以下问题:如果发送方订阅了多播组,环回的数据包是否会通过接入点返回发送方?即使不是这样,是否可以通过AP强制环回?

另外,为什么环回不能使用以下代码?

char * server_addr_name = "239.255.0.1"; // multicast group
int port_number = 8888;
int enable_loopback = 1;
int udp_socket;
struct message msg; // random message
char buffer[BUFFER_SIZE];

/* create socket */
struct sockaddr_in server_addr, rcv_addr;
socklen_t server_addr_size = sizeof(struct sockaddr_in);
socklen_t rcv_addr_size = sizeof(struct sockaddr_in);

/* initialize socket */
memset(& server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(server_addr_name);
server_addr.sin_port = htons(port_number);
if ((udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 
    fprintf(stderr, "Error initializing UDP socket.\n");
    exit(EXIT_FAILURE);


struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(server_addr_name);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, & mreq, sizeof(mreq)) < 0) 
    fprintf(stderr, "Error on setting multicast membership on socket.\n");
    exit(EXIT_FAILURE);

unsigned char do_enable = (unsigned char) enable_loopback;
if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
               & do_enable, sizeof(do_enable)) < 0) 
    fprintf(stderr, "Error on setting multicast loopback on socket.\n");
    exit(EXIT_FAILURE);


int read_size = 0;
while (1) 

    /* send the packet */
    if (sendto(udp_socket, &msg, sizeof(struct message), 0,
            (struct sockaddr *) & server_addr, server_addr_size) == -1) 
        fprintf(stderr, "Error on sending UDP packet.\n");
    
    else
        printf("Sent my message.\n");

    /* get response from the server/multicast address */
    read_size = recvfrom(udp_socket, buffer, BUFFER_SIZE, 0,
                            (struct sockaddr *) & rcv_addr, & rcv_addr_size);
    if (read_size < 1)
        break;
    else
        printf("Got my packet!\n");

任何帮助表示赞赏。

【问题讨论】:

你的测试怎么样?您是否处于发送方和接收方在同一台机器上的测试环境中?如果是这样,我建议尝试使用 127.0.0.1 作为 IP 并修改您的主机文件添加一行: 127.0.0.1 hostname ,其中主机名是计算机名。我有时会遇到这个问题。 @Picarus 这只会让一切变得更糟。 @EJP,我的意思是首先,调试问题,看看是否是网络问题,其次,传达有时问题不在代码中,而是在系统配置中。为什么你认为情况可能更糟?它可能解决问题或不能解决问题,但它可以帮助带来光明 Loopback在本地工作,与AP无关。从逻辑上讲,AP 不能发回数据包 - 如果这样做,您将收到两次相同的数据包。 @Picarus 在客户端和服务器都无效时调查环回,因为您无法区分正确的消息接收和环回。它肯定不会“解决问题”。 【参考方案1】:

setsockopt() 的参数应该是 int,而不是 unsigned char。

【讨论】:

好吧,它仍然没有解决我的问题。根本没有环回的迹象。 unsigned char do_enable -> int do_enable 虽然 linux 手册页 (linux.die.net/man/2/setsockopt) 说参数必须是 socklen_t,它是 unsigned int 的 typedef。我认为错误不在 setsockopt() 中,因为 getsockopt() 显示环回已启用(它应该默认启用,我写了这行代码只是为了确定)。【参考方案2】:

刚刚遇到同样的问题。 Linux 不会在同一个套接字上镜像数据包,即使它与地址:端口匹配。 您应该创建另一个套接字 udp_socket_receiver,就像您的 udp_socket 和 recvfrom 一样。看起来 IP_MULTICAST_LOOP 意味着在除发送方套接字之外的所有侦听器上本地环回多播数据包。

【讨论】:

您应该通过列出待办事项来分享您的想法 这是错误的。 Linux(我在 4.4.0 上)默认将多播数据报镜像到发送套接字。您只需要确保套接字绑定到多播 sockaddr,订阅组(使用ip maddr 检查),并且未连接。【参考方案3】:

如果发送方订阅了多播组,环回的数据包是否会通过接入点返回发送方?

我不确定,但乍一看答案是一个胖。如果网关将数据包反弹回它来自的地方,您将复制流量,这会破坏多播的意义。

但是,发送者仍然会收到消息,因为本地网络中订阅该组的任何人都会收到消息,并且发送者已订阅。

即使不是这样,是否可以通过AP强制环回?

我不知道,但你可能不需要那个。见下文。

为什么环回不能使用以下代码?

因为你忘了bind 套接字。你没有告诉套接字它应该在哪里监听,所以即使多播可能会正确弹跳,UDP 也会丢弃数据包,因为它连接到一个随机端口,可能不是 8888。

socket()setsockopt(IP_ADD_MEMBERSHIP) 之间添加以下块。为我工作:

struct sockaddr_in src;
memset(&src, 0, sizeof(src));
src.sin_family = AF_INET;
src.sin_port = htons(port_number);
src.sin_addr.s_addr = INADDR_ANY;
if (bind(udp_socket, (struct sockaddr*)&src, sizeof(src))) 
    perror("bind() failed");
    close(udp_socket);
    return EXIT_FAILURE;

您也应该在使用完套接字后close()ing。

【讨论】:

以上是关于如果启用了环回,为啥发送方收不到它的多播 UDP 数据包?的主要内容,如果未能解决你的问题,请参考以下文章

如何在服务器中隔离 UDP 多播地址 + 端口

在 WinSock 中通过 127.0.0.1 发送的多播可以用 INADDR_ANY 读取吗?

Aeron - 跨交换机的多播问题

发送 UDP 到本地服务器,但不通过环回接口

C# UDP 客户端读取多播 IP(本地接口),并将单播 UDP 发送到 ***

将 Canvas 流发送到 UDP 多播地址