You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

无法接收multicast数据:multicast收发应用开发问题求助

排查多播接收失败的问题

我帮你梳理了代码里的几个关键问题,正是这些问题导致你接收不到多播数据,咱们一个个拆解:

1. 接收端绑定地址错误

你的接收线程里把socket绑定到了单播地址15.45.234.3,但多播数据包是发往多播组224.1.2.3的。当socket绑定到单播地址时,它只会接收发往该单播地址的流量,完全不会监听多播组的数据包。

正确的做法是绑定到通配地址INADDR_ANY(监听所有接口上的多播流量),或者直接绑定到目标多播组地址224.1.2.3

2. 缺少系统调用的错误检查

代码里的socket()setsockopt()bind()这些核心调用都没做返回值检查——万一某个步骤失败(比如添加多播成员失败),你根本不知道问题出在哪。必须加上错误处理,用perror()打印具体的错误信息,这是排查网络问题的基础。

3. 数据类型不规范

你用long存储socket文件描述符,虽然在部分系统上能运行,但socket fd本质是int类型,规范写法应该用int,避免潜在的兼容性问题。


修正后的完整代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/ioctl.h>
#include <netinet/in.h>
#include<pthread.h>

void* recvd(void* t) {
    printf("thread created\n");
    // 使用标准int类型存储socket文件描述符
    int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (socket_fd == -1) {
        perror("Failed to create socket");
        pthread_exit(NULL);
    }

    struct sockaddr_in muladdr;
    memset(&muladdr, 0, sizeof(muladdr));
    muladdr.sin_family = AF_INET;
    muladdr.sin_port = htons(3454);
    // 绑定到通配地址,接收所有接口上发往该端口的多播数据
    muladdr.sin_addr.s_addr = htonl(INADDR_ANY);

    struct ip_mreq mreq;
    memset(&mreq, 0, sizeof(mreq));
    inet_aton("224.1.2.3", &mreq.imr_multiaddr);
    inet_aton("15.45.234.3", &mreq.imr_interface);

    // 检查多播成员添加是否成功
    if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
        perror("Failed to add multicast membership");
        close(socket_fd);
        pthread_exit(NULL);
    }

    // 检查绑定操作是否成功
    if (bind(socket_fd, (struct sockaddr *)&muladdr, sizeof(muladdr)) == -1) {
        perror("Failed to bind socket");
        close(socket_fd);
        pthread_exit(NULL);
    }

    char buf[100];
    memset(buf, 0, 100);
    ssize_t lSize = recvfrom(socket_fd, buf, sizeof(buf)-1, 0, NULL, NULL);
    if (lSize > 0) {
        buf[lSize] = '\0'; // 确保字符串以null终止,避免printf乱码
        printf("recvd data: %s\n", buf);
    } else if (lSize == -1) {
        perror("Failed to receive data");
    }

    close(socket_fd);
    return NULL;
}

int main(int argc, char* argv[]) {
    pthread_t pp;
    if (pthread_create(&pp, NULL, recvd, NULL) != 0) {
        perror("Failed to create thread");
        exit(EXIT_FAILURE);
    }
    sleep(2);

    int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (socket_fd == -1) {
        perror("Failed to create socket");
        exit(EXIT_FAILURE);
    }

    int ttl = 25;
    if (setsockopt(socket_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) {
        perror("Failed to set multicast TTL");
        close(socket_fd);
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in muladdr;
    memset(&muladdr, 0, sizeof(muladdr));
    muladdr.sin_family = AF_INET;
    muladdr.sin_port = htons(3454);
    inet_aton("224.1.2.3", &muladdr.sin_addr);

    char arr[] = "Hello World!";
    printf("sending data\n");
    ssize_t result = sendto(socket_fd, arr, strlen(arr), 0, (struct sockaddr *)&muladdr, sizeof(muladdr));
    if (result == -1) {
        perror("Error in sending data");
        close(socket_fd);
        exit(EXIT_FAILURE);
    }
    printf("sent data\n");

    pthread_join(pp, NULL);
    close(socket_fd);
    return 0;
}

额外说明

  • 绑定INADDR_ANY是最通用的做法,能接收所有网络接口上的多播流量;如果只想在特定接口接收,也可以绑定到该接口的单播地址,但要确保多播数据包能到达这个接口。
  • 我在接收逻辑里给buf手动添加了null终止符,避免因为接收到的原始数据没有结束符导致printf输出乱码。
  • 所有系统调用都加上了错误检查,再出问题时你能直接看到具体是哪个步骤失败,方便快速定位。

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

火山引擎 最新活动