linux 在多网卡下的设备的UDP 组播问题总结

参考博文:
解决接收不到组播包的问题 - Justlinux2010的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justlinux2010/article/details/11140383

1、问题描述
实现在多网卡设备上发送组播消息,实现设备搜索,并接收通过组播返回的搜索信息。
设备为多网卡nvr,有一个常规网卡和一个PPPOE口;在进行发送组播搜索ipc时,只可以在常规网卡上接收到反馈的组播信息,而在ppoe上没有
2、问题原因及解决方法

关键代码(组播socket的初始化):

    int CreateMuticastSocket(const std::string ðx, int port)
    {
        int ret = 0;
        int sockfd = socket(AF_INET,SOCK_DGRAM,0);
        if(-1 == sockfd)
        {
            printf("socket error!!!\n");
            perror("socket:");
            return -1;
        }

        /*test ip*/
        struct sockaddr_in localaddr = {0};
        localaddr.sin_family = AF_INET;
        localaddr.sin_port = htons(port);
        localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        ret = bind(sockfd,(struct sockaddr*)&localaddr,sizeof(struct sockaddr));
        if(-1 == ret)
        {
            printf("bind localaddr error!!!\n");
            perror("bind:");
            close(sockfd);
            return -1;
        }
        int reuse = 1;
        if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
        {
            perror("Setting SO_REUSEADDR error");
            close(sockfd);
            return -1;
        }
 /*设置是否支持本地回环接收*/
    int loopBack=1;
        ret = setsockopt(sockfd,IPPROTO_IP, IP_MULTICAST_LOOP, &loopBack, sizeof(loopBack));
        if(-1 == ret)
        {
            printf("setsockopt broadcaset error!!!\n");
            perror("setsockopt:");
            close(sockfd);
            return -1;
        }

/*
将本地socket添加到多播组中,注意,此处针对struct ip_mreq结构体需要填充两个成员,
成员ipmr.imr_interface.s_addr的值指定的是将要发送的网卡的ip地址,
成员impr.imr_multiaddr指定的是组播地址;
如果指定为INADDR_ANY则系统会绑定一个默认网卡的具体ip(根据默认网关选择),则会出现特定网卡可以发送和接收组播信息,另一网卡不可以。即指定INADDR_ANY并不能把所有网卡都添加多播组中,必须明确指定对应网卡ip才可以。*/
    struct in_addr addr = {0};
    addr.s_addr=inet_addr(get_local_ip(ethx).c_str());
    struct ip_mreq ipmr;
    ipmr.imr_interface.s_addr = addr.s_addr;
    ipmr.imr_multiaddr.s_addr = inet_addr(CONST_MULTICAST_IP_V4);
    ret=setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(const char*)&ipmr,sizeof(ipmr));

/*此处指定组播数据的出口网卡,如果不设置则会根据路由表指定默认路由出口*/

    if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr)))
    {
        printf("set error IP_MULTICAST_IF %s\n", ethx.c_str());
        perror("Setting IP_MULTICAST_IF error:");
        close(sockfd);
        sockfd = -1;
    }

    struct timeval tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    ret = setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char*)&tv,sizeof(tv));
    if(-1 == ret)
    {
         printf("setsockopt recvtimeout error!!!\n");
         perror("setsockopt:");
         close(sockfd);
         return -1;
     }
     return sockfd;
 }

3、综述

在多网卡设备上实现组播需要特别注意:
1、使用setsockopt设置IP_ADD_MEMBERSHIP组播地址时,对应的本地地址必须被明确指定为要发送组播数据包的网卡的ip地址,而不可以使用INADDR_ANY设置;如果使用INADDR_ANY,则系统会默认根据路由表绑定一个明确的地址,则在接收组播信息时,无法从发送的网卡处接收到数据,发送的网卡没有被添加到组播组中。

2、必须使用setsockopt设置IP_MULTICAST_IF选项,从而修改默认的组播出口网卡。否则系统根据路由表发送到默认网关。而不一定是指定的网卡。
3、在多网卡实现多播(如设备搜索)相关的功能时,可以针对多个网卡分别执行一次操作,同时可以区分设备是从哪个网卡搜索到的。

你可能感兴趣的:(问题收集)