错误 setsockopt IP_ADD_MEMBERSHIP: 没有这样的设备

问题描述

我正在编写一个应用程序,它应该能够在套接字上接收 IPv4 或 IPv6 多播数据报。我编写了一个函数,可以通过 setsockopt 接收套接字的多播数据报(请参阅下面的代码)。我遇到的奇怪问题是 IPv4 案例 IP_ADD_MEMBERSHIP 的 setsockopt 有时因 errno No such device 而失败,而其他时候它按预期工作。我的应用程序在带有 raspbian 的 raspBerry pi 上运行。我非常感谢您的建议!

void setRecvMulticastAddr(int *socketFD,struct sockaddr *addr,char *interface,char *multicastaddr){

    // Cast the sockaddr to a sockaddr storage struct
    struct sockaddr_storage *addrStorage = (struct sockaddr_storage *) addr;

    // Check if it is an IPv4 or IPv6 socket
    if(addrStorage->ss_family == AF_INET){

        // IPv4 multicast request
        struct ip_mreq mreq;

        // Convert the multicast IPv4 address string to an in_addr
        struct in_addr multiaddr;
        if(inet_pton(AF_INET,multicastaddr,&multiaddr) != 1){
            printf("Could not convert the IPv4 multicast address: %s",multicastaddr);
            exit(ERR_INETPTON_Failed);
        }

        // Cast the sockaddr to a sockaddr_in struct
        struct sockaddr_in *addrin = (struct sockaddr_in *) addrStorage;

        // Fill out the IPv4 multicast request
        mreq.imr_interface = addrin->sin_addr;
        mreq.imr_multiaddr = multiaddr;

        // ### The setsockopt that fails sometimes: ###
        // Set the sockopt so the socket can receive on the multicast address
        if(setsockopt(*socketFD,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) != 0){
            perror("Error setsockopt IP_ADD_MEMBERSHIP Failed");
            exit(ERR_SETSOCKOPT_Failed);
        }

    }else{

        // IPv6 multicast request
        struct ipv6_mreq mreq;

        // Set the interace name in the ifr
        struct ifreq ifr;
        snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"%s",interface);

        // Get the ifrindex based on the interface name
        if(ioctl(*socketFD,SIOGIFINDEX,&ifr) < 0){
            printf("Could not get ifrindex: %s\n",strerror(errno));
        }

        // Convert the multicast addrress string to an in_addr6
        struct in6_addr multiaddr;
        if(inet_pton(AF_INET6,&multiaddr) != 1){
            printf("Could not convert the IPv6 multicast address: %s",multicastaddr);
            exit(ERR_INETPTON_Failed);
       }

       // Fill out the IPv6 multicast request
       mreq.ipv6mr_interface = ifr.ifr_ifindex;
       mreq.ipv6mr_multiaddr = multiaddr;

       // Set the sockopt so the socket can receive on the multicast address
       if(setsockopt(*socketFD,IPPROTO_IPV6,IPV6_JOIN_GROUP,sizeof(mreq)) != 0){
            perror("Error setsockopt IPV6_JOIN_GROUP Failed");
            exit(ERR_SETSOCKOPT_Failed);
       }

    }

    return;
}

解决方法

基于与@dbush 的讨论,我希望通过以下方式改进代码:

  1. 使用新的推荐结构 ip_mreqn 而不是旧的结构 ip_mreq
  2. 使用 ioctl 而不是使用 sockaddr 来确定接口的地址

这是 IPv4 案例的新代码:

    // IPv4 multicast request
    struct ip_mreqn mreqn;

    // Set the interace family and name in the ifr
    struct ifreq ifr;
    ifr.ifr_addr.sa_family = AF_INET;
    snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"%s",interface);

    // Get the ifrindex of the interface
    if(ioctl(*socketFD,SIOGIFINDEX,&ifr) < 0){
        printf("Error could not get the interface index: %s\n",strerror(errno));
        close(*socketFD);
        exit(ERR_IOCTL_INDEX);
    }

    mreqn.imr_ifindex = ifr.ifr_ifindex;

    // Get the IP address of the interface
    if(ioctl(*socketFD,SIOCGIFADDR,&ifr) != 0){
        printf("Error could not get the interface address: %s\n",strerror(errno));
        close(*socketFD);
        exit(ERR_IOCTL_ADDR);
    }

    // Cast the sockaddr to a sockaddr_in to get the in_addr value
    struct sockaddr_in *ifaddr = (struct sockaddr_in*) &ifr.ifr_addr;
    mreqn.imr_address = ifaddr->sin_addr;

    // Convert the multicast IPv4 address string to an in_addr
    struct in_addr multiaddr;
    if(inet_pton(AF_INET,multicastaddr,&multiaddr) != 1){
        printf("Error could not convert the IPv4 multicast address: %s",multicastaddr);
        exit(ERR_INETPTON_FAILED);
    }

    mreqn.imr_multiaddr = multiaddr;

    // Set the sockopt so the socket can receive on the multicast address
    if(setsockopt(*socketFD,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreqn,sizeof(mreqn)) != 0){
        perror("Error setsockopt IP_ADD_MEMBERSHIP failed");
        exit(ERR_SETSOCKOPT_FAILED);
    }

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...