精华内容
下载资源
问答
  • 组播在双网卡或者多网卡下,系统会根据路由表自动选择网卡发送命令,但是会导致一些网卡发送数据异常,这时候就需要绑定ip来解决这个问题了。 组播报文在网络传播的时候是需要设置TTL的(除非网口都是2层交换机),...
  • linuxUDP组播接收不到数据的说明

    千次阅读 2020-12-07 13:04:33
    背景 在一个跨平台的桌面项目中,由于涉及多线程中对象的创建、销毁等,基于QT的对象绑定机制(QObject子类)来做实现时,需要相当心累的设计,...基于libuv的程序示例,在windows下,可以正常工作(接收到组播数据.

    背景

    在一个跨平台的桌面项目中,由于涉及多线程中对象的创建、销毁等,基于QT的对象绑定机制(QObject子类)来做实现时,需要相当心累的设计,才能避免跨线程的异常。由于QT的这个天然机制,在实现很多业务(非界面)模块时,都避免了基于QObject。网络模块中的UDP等功能,同样的也未基于QUdpSocket及其相关,而是包装了libuv(尽管,对桌面来说libuv也需要再折腾,但libuv作者已经明示了这个点了)

     

    现象

    基于libuv的程序示例,在windows下,可以正常工作(接收到组播数据),但是,同样的代码,在linux下无法接收到组播数据。

    大概的实现流程是:按照libuv的官方说明(初始化sockaddr结构udp操作),使用uv_ip4_addr、uv_udp_init、uv_udp_bind、uv_udp_set_membership等接口,很快就实现了一个可接收组播的原型,大概如下:

    uv_udp_t* udpHandle = new uv_udp_t;
    sockaddr_in addr;
    int iRet = -1;
    std::string localIP = "192.168.2.102";	// 本机的某个网卡的IP
    std::string multicastIP = "224.1.1.8";	// 加入的组播IP,以接收数据
    int localPort = 15909;
    iRet = ::uv_ip4_addr(localIP.c_str(), localPort, &addr);
    iRet = ::uv_udp_init(objLoop, udpHandle);
    iRet = ::uv_udp_bind(udpHandle, (const sockaddr*)&addr, UV_UDP_REUSEADDR);
    iRet = ::uv_udp_set_membership(udpHandle, multicastIP.c_str(),
    							   localIP.c_str(), UV_JOIN_GROUP);
    

    分析

    由于linux组播实操经历的缺失,先期主要是搜看各类文章:有些文章,直接采用了修改路由等各种配置解决;有些文章,也采用了linux下绑定0.0.0.0即any的方式来接所有;另外一些,也说明了linux下与windows中组播的区别(但是,并没有适当的关键字以方便大家的搜索)。

    最终,还是又继续翻看了UNIX网络编程(中文 第3版)的第21章。其中直接有关的是:

    21.9节开始的模糊不清的文字说明,以及图21-14的代码示例(2、12-17行),还有sap.mcast.net对应着(224.2.127.254);

    21.10节,这段话“我们想要给接收套接字捆绑多播组和端口,譬如说239.255.1.2端口8888。(回顾一下,我们可以只捆绑通配IP地址和端口8888,不过还是捆绑多播地址以防止目的端口同为8888的其他数据报到达本套接字)”(无论允许与否,我都想吐槽一下的:这种不加标点的长句子,是在锻炼国码奴的堆栈能力吧)。下面是英文版书中的对应文字(反倒是直观了):

    We want the receiving socket to bind the multicast group and port, say 239.255.1.2 port 8888. (Recall that we could just bind the wildcard IP address and port 8888, but binding the multicast address prevents the socket from receiving any other datagrams that might arrive destined for port 8888.)

     

    与本问题,间接有关的是,第21.2、21.3小节的原理性说明。

    由以上,在linux下接收组播数据时,终于可以舒适安心的采用先绑定组播IP的方式进行实现了。

     

    方案

    1、基于libuv的方案示例

    uv_udp_t* udpHandle = new uv_udp_t;
    sockaddr_in addr;
    int iRet = -1;
    std::string localIP = "192.168.2.102";	// 本机的某个网卡的IP
    std::string multicastIP = "224.1.1.8";	// 加入的组播IP,以接收数据
    int localPort = 15909;
    
    #if defined(_WIN32)
    iRet = ::uv_ip4_addr(localIP.c_str(), localPort, &addr);
    #else
    // 下面是linux下,与windows的不同点
    iRet = ::uv_ip4_addr(multicastIP.c_str(), localPort, &addr);	
    #endif
    
    // 以下是通用的
    iRet = ::uv_udp_init(libuvLoop, udpHandle);
    iRet = ::uv_udp_bind(udpHandle, (const sockaddr*)&addr, UV_UDP_REUSEADDR);
    iRet = ::uv_udp_set_membership(udpHandle, multicastIP.c_str(),
    							   localIP.c_str(), UV_JOIN_GROUP);
    

    2、使用原生实现的方案示例

    可能你在项目中,并不使用libuv,因此,本文也给出一个大概的示例

    int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    
    sockaddr_in addr;
    int iRet = -1;
    std::string localIP = "192.168.2.102";	// 本机的某个网卡的IP
    std::string multicastIP = "224.1.1.8";	// 加入的组播IP,以接收数据
    int localPort = 15909;
    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(localRecvPort);				// 本地想接收的端口
    
    #if defined(_WIN32)
    addr.sin_addr.s_addr = inet_addr(localIP.c_str());	// 本机的某个网卡的IP
    #else
    // 下面是linux下,与windows的不同点
    // linux加入组播时,本机需绑定0.0.0.0或者组播地址
    addr.sin_addr.s_addr = inet_addr(multicastIP.c_str());	
    #endif
    
    bind(udpSocket, (sockaddr *)&saddr, sizeof(addr));
    int reuseOn = 1;
    setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
    ip_mreq mreq;  
    mreq.imr_multiaddr.s_addr = inet_addr(multicastIP.c_str());  
    mreq.imr_interface.s_addr = inet_addr(localIP.c_str());  
    setsockopt(udpSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
    

     

    相关文章

    https://blog.csdn.net/rcfalcon/article/details/35221759

    https://blog.csdn.net/xqligong/article/details/106124408

    https://stackoverflow.com/questions/32824029/upnp-bind-with-multicast

    https://www.cnblogs.com/lifan3a/articles/6780760.html

    https://blog.csdn.net/qing310820/article/details/103408235

    展开全文
  • 那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。 224.0.0.0~224.0.0.255 为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用; 224.0.1.0~224.0.1.255 ...

    广播

    广播很简单,原理不解释了,这里实现的重点在于:
    1.对服务器端的套接字使用setsockopt函数开放广播权限
    2.指定要发送的IP(广播地址)+端口号
    3.客户端必须显式绑定端口号(和2的端口号要一样)
    例子:
    server.c

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <net/if.h>
    
    #define SERVER_PORT 8000                                    /* 无关紧要 */
    #define MAXLINE 1500
    
    #define BROADCAST_IP "192.168.127.255"
    #define CLIENT_PORT 9000                                    /* 重要 */
    
    int main(void)
    {
        int sockfd;
        struct sockaddr_in serveraddr, clientaddr;
        char buf[MAXLINE];
    
        /* 构造用于UDP通信的套接字 */
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    
        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;                        /* IPv4 */
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);         /* 本地任意IP INADDR_ANY = 0 */
        serveraddr.sin_port = htons(SERVER_PORT);
    
        bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    
        int flag = 1;
        setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));//给sockfd开放广播权限
    
        /*构造 client 地址 IP+端口  192.168.7.255+9000 */
        bzero(&clientaddr, sizeof(clientaddr));
        clientaddr.sin_family = AF_INET;
        inet_pton(AF_INET, BROADCAST_IP, &clientaddr.sin_addr.s_addr);
        clientaddr.sin_port = htons(CLIENT_PORT);
    
        int i = 0;
        while (1) {
            sprintf(buf, "Drink %d glasses of water\n", i++);
            //fgets(buf, sizeof(buf), stdin);
            sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&clientaddr, sizeof(clientaddr));//发送给192.168.7.255+9000
            sleep(1);
        }
        close(sockfd);
        return 0;
    }
    
    

    client.c

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    #define SERVER_PORT 8000
    #define MAXLINE 4096
    #define CLIENT_PORT 9000
    
    int main(int argc, char *argv[])
    {
        struct sockaddr_in localaddr;
        int confd;
        ssize_t len;
        char buf[MAXLINE];
    
        //1.创建一个socket
        confd = socket(AF_INET, SOCK_DGRAM, 0);
    
        //2.初始化本地端地址
        bzero(&localaddr, sizeof(localaddr));
        localaddr.sin_family = AF_INET;
        inet_pton(AF_INET, "0.0.0.0" , &localaddr.sin_addr.s_addr);
        localaddr.sin_port = htons(CLIENT_PORT);
    
        int ret = bind(confd, (struct sockaddr *)&localaddr, sizeof(localaddr));  //显式绑定不能省略
        if (ret == 0)
            printf("...bind ok...\n");
    
        while (1) {
            len = recvfrom(confd, buf, sizeof(buf), 0, NULL, 0);
            write(STDOUT_FILENO, buf, len);
        }
        close(confd);
    
        return 0;
    }
    
    
    

    在这里插入图片描述

    组播

    组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
    224.0.0.0~224.0.0.255 为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
    224.0.1.0~224.0.1.255 是公用组播地址,可以用于Internet;欲使用需申请。
    224.0.2.0~238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效;
    239.0.0.0~239.255.255.255 为本地管理组播地址,仅在特定的本地范围内有效。

    可使用ip ad命令查看网卡编号,如:
    在这里插入图片描述
    查看等会要用到的一个结构体的定义:

    grep "struct ip_mreqn {" -r /usr/include/ -n 
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    例子:
    注意,服务器使用setsockopt函数开放组播权限,客户端使用setsockopt函数加入或退出组播组。
    在这里插入图片描述
    server.c

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <net/if.h>
    
    #define SERVER_PORT 8000
    #define CLIENT_PORT 9000
    #define MAXLINE 1500
    
    #define GROUP "239.0.0.2"
    
    int main(void)
    {
        int sockfd;
        struct sockaddr_in serveraddr, clientaddr;
        char buf[MAXLINE];
        struct ip_mreqn group;
    
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);                /* 构造用于UDP通信的套接字 */
        
        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;                        /* IPv4 */
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);         /* 本地任意IP INADDR_ANY = 0 */
        serveraddr.sin_port = htons(SERVER_PORT);
    
        bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    
        inet_pton(AF_INET, GROUP, &group.imr_multiaddr.s_addr);        /* 设置组地址 */
        inet_pton(AF_INET, "0.0.0.0", &group.imr_address);      /* 本地任意IP */
        group.imr_ifindex = if_nametoindex("ens32");             /* 给出网卡名,转换为对应编号: ens32 --> 编号  命令:ip ad */
    
        setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group.imr_multiaddr.s_addr, sizeof(group.imr_multiaddr.s_addr));  /* 对sockfd开放组播权限 */
    
        bzero(&clientaddr, sizeof(clientaddr));                 /* 构造 client 地址 IP+端口 */
        clientaddr.sin_family = AF_INET;
        inet_pton(AF_INET, GROUP, &clientaddr.sin_addr.s_addr); /* IPv4  239.0.0.2+9000 */
        clientaddr.sin_port = htons(CLIENT_PORT);
    
        int i = 0;
        while (1) {
            sprintf(buf, "shenhang %d\n", i++);
            //fgets(buf, sizeof(buf), stdin);
            sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&clientaddr, sizeof(clientaddr));
            sleep(1);
        }
    
        close(sockfd);
    
        return 0;
    }
    
    

    client.c

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <net/if.h>
    
    
    #define CLIENT_PORT 9000
    
    #define GROUP "239.0.0.2"
    
    int main(int argc, char *argv[])
    {
        struct sockaddr_in localaddr;
        int confd;
        ssize_t len;
        char buf[BUFSIZ];
    
        struct ip_mreqn group;                                                  /* 组播结构体 */
    
        confd = socket(AF_INET, SOCK_DGRAM, 0);
    
        bzero(&localaddr, sizeof(localaddr));                                   /* 初始化 */
        localaddr.sin_family = AF_INET;
        inet_pton(AF_INET, "0.0.0.0" , &localaddr.sin_addr.s_addr);
        localaddr.sin_port = htons(CLIENT_PORT);
    
        bind(confd, (struct sockaddr *)&localaddr, sizeof(localaddr));
    
        inet_pton(AF_INET, GROUP, &group.imr_multiaddr.s_addr);                        /* 设置组地址 */
        inet_pton(AF_INET, "0.0.0.0", &group.imr_address);                      /* 使用本地任意IP添加到组播组 */
        group.imr_ifindex = if_nametoindex("ens32");                             /* 通过网卡名-->编号 ip ad */
        
        setsockopt(confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));/* 设置client 加入多播组 */
    
        while (1) {
            len = recvfrom(confd, buf, sizeof(buf), 0, NULL, 0);
            write(STDOUT_FILENO, buf, len);
        }
        close(confd);
    
        return 0;
    }
    
    
    

    在这里插入图片描述

    展开全文
  • linux udp 单播 组播 广播实现

    千次阅读 2016-06-02 15:55:27
    1、组播和广播需要在局域网内才能实现,另外得查看linux系统是否支持多播和广播:ifconfig  UP BROADCAST MULTICAST MTU:1500 跃点数:1  说明该网卡支持 2、发送多播包的主机需要设置网关,否则运行sendto

    转载 http://blog.sina.com.cn/s/blog_4fc7368a0100okbq.html


    1、组播和广播需要在局域网内才能实现,另外得查看linux系统是否支持多播和广播:ifconfig

             UP BROADCAST MULTICAST MTU:1500   跃点数:1
             说明该网卡支持
    2、发送多播包的主机需要设置网关,否则运行sendto()会出现"network is unreachable",网卡可以随便设置,但是一定要设。还要添加路由240.0.0.0,即:
               route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
               route add default gw "192.168.40.1 " dev eth0
    3 、出现:“setsockopt:No such device”。的提示,说明多播IP设置出现问题,系统所需要的uint32_t格式的网络地址的开头不是1110,检验通不过。解决办法:在把地址字 符串"*.*.*.*"转化为uint32_t时采用htonl(inet_network(“*.*.*.*”))或者inet_aton函 数,inet_aton(GRUPO, &srv.sin_addr)

    另外有文章:http://unix-cd.com/unixcd12/article_5577.html

    11.3 多 播
     

    单 播用于两个主机之间的端对端通信,广播用于一个主机对整个局域网上所有主机上的数据通信。单播和广播是两个极端,要么对一个主机进行通信,要么对整个局域 网上的主机进行通信。实际情况下,经常需要对一组特定的主机进行通信,而不是整个局域网上的所有主机,这就是多播的用途。

    11.3.1  多播的概念

    多播,也称为“组播”,将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的        数据。

    在 广域网上广播的时候,其中的交换机和路由器只向需要获取数据的主机复制并转发数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择 地复制并传输数据,将数据仅仅传输给组内的主机。多播的这种功能,可以一次将数据发送到多个主机,又能保证不影响其他不需要(未加入组)的主机的其他通 信。

    相对于传统的一对一的单播,多播具有如下的优点:

    q  具有同种业务的主机加入同一数据流,共享同一通道,节省了带宽和服务器的优点,具有广播的优点而又没有广播所需要的带宽。

    q  服务器的总带宽不受客户端带宽的限制。由于组播协议由接收者的需求来确定是否进行数据流的转发,所以服务器端的带宽是常量,与客户端的数量无关。

    q  与单播一样,多播是允许在广域网即Internet上进行传输的,而广播仅仅在同一局域网上才能进行。

    组播的缺点:

    q  多播与单播相比没有纠错机制,当发生错误的时候难以弥补,但是可以在应用层来实现此种功能。

    q  多播的网络支持存在缺陷,需要路由器及网络协议栈的支持。

    多播的应用主要有网上视频、网上会议等。

    11.3.2  广域网的多播

    多播的地址是特定的,D类地址用于多播。DIP地址就是多播IP地址,即224.0.0.0239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:

    q  局部多播地址:在224.0.0.0224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。

    q  预留多播地址:在224.0.1.0238.255.255.255之间,可用于全球范围(如Internet)或网络协议。

    q  管理权限多播地址:在239.0.0.0239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

    11.3.3  多播的编程

    多播的程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的,其选项值和含义参见11.5所示。

    11.5  多播相关的选项

    getsockopt()/setsockopt()的选项

        

    IP_MULTICAST_TTL

    设置多播组数据的TTL

    IP_ADD_MEMBERSHIP

    在指定接口上加入组播组

    IP_DROP_MEMBERSHIP

    退出组播组

    IP_MULTICAST_IF

    获取默认接口或设置接口

    IP_MULTICAST_LOOP

    禁止组播数据回送

    1.选项IP_MULTICASE_TTL

    选项IP_MULTICAST_TTL允许设置超时TTL,范围为0255之间的任何值,例如:

     

    unsigned char ttl=255;

    setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));

    2.选项IP_MULTICAST_IF

    选项IP_MULTICAST_IF用于设置组播的默认默认网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据。例如:

     

    struct in_addr addr;

    setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr));

     

    参数addr是希望多播输出接口的IP地址,使用INADDR_ANY地址回送到默认接口。

    默认情况下,当本机发送组播数据到某个网络接口时,在IP层,数据会回送到本地的回环接口,选项IP_MULTICAST_LOOP用于控制数据是否回送到本地的回环接口。例如:

     

    unsigned char loop;

    setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));

     

    参数loop设置为0禁止回送,设置为1允许回送。

    3.选项IP_ADD_MEMBERSHIPIP_DROP_MEMBERSHIP

    加入或者退出一个组播组,通过选项IP_ADD_MEMBERSHIPIP_DROP_MEMBER- SHIP,对一个结构struct ip_mreq类型的变量进行控制,struct ip_mreq原型如下:

     

    struct ip_mreq          

    {

          struct in_addr imn_multiaddr;      /*加入或者退出的广播组IP地址*/

          struct in_addr imr_interface;      /*加入或者退出的网络接口IP地址*/

    };

     

    选项IP_ADD_MEMBERSHIP用于加入某个广播组,之后就可以向这个广播组发送数据或者从广播组接收数据。此选项的值为mreq结构,成员imn_multiaddr是需要加入的广播组IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。例如:

     

    struct ip_mreq mreq;

    setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

     

     

     

     

    使用IP_ADD_MEMBERSHIP选项每次只能加入一个网络接口的IP地址到多播组,但并不是一个多播组仅允许一个主机IP地址加入,可以多次调用IP_ADD_MEMBERSHIP选项来实现多个IP地址加入同一个广播组,或者同一个IP地址加入多个广播组。当imr_ interfaceINADDR_ANY时,选择的是默认组播接口。

     

    4.选项IP_DROP_MEMBERSHIP

    选项IP_DROP_MEMBERSHIP用于从一个广播组中退出。例如:

     

    struct ip_mreq mreq;

    setsockopt(s,IPPROTP_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(sreq));

     

    其中mreq包含了在IP_ADD_MEMBERSHIP中相同的值。

    5.多播程序设计的框架

    要进行多播的编程,需要遵从一定的编程框架,其基本顺序如图11.6所示。

    多播程序框架主要包含套接字初始化、设置多播超时时间、加入多播组、发送数据、接收数据以及从多播组中离开几个方面。其步骤如下:

    1)建立一个socket

    2)然后设置多播的参数,例如超时时间TTL、本地回环许可LOOP等。

    3)加入多播组。

    4)发送和接收数据。

    5)从多播组离开。

    11.3.4  内核中的多播

    Linux内核中的多播是利用结构struct ip_mc_socklist来将多播的各个方面连接起来的,其示意图如图11.7所示。

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

    11.7  多播的内核结构

    struct inet_sock {

        ...

        __u8                mc_ttl;     /*多播TTL*/

        ...

        __u8                ...

                            mc_loop:1;      /*多播回环设置*/

        int                 mc_index;       /*多播设备序号*/

        __be32              mc_addr;        /*多播地址*/

        struct ip_mc_socklist   *mc_list;   /*多播群数组*/

        ...

    };

     

    q  结构成员mc_ttl用于控制多播的TTL

    q  结构成员mc_loop表示是否回环有效,用于控制多播数据的本地发送;

    q  结构成员mc_index用于表示网络设备的序号;

    q  结构成员mc_addr用于保存多播的地址;

    q  结构成员mc_list用于保存多播的群组。

    1.结构ip_mc_socklist

    结构成员mc_list的原型为struct ip_mc_socklist,定义如下:

     

    struct ip_mc_socklist

    {

        struct ip_mc_socklist   *next;

        struct ip_mreqn     multi;

        unsigned int        sfmode;     /*MCAST_{INCLUDE,EXCLUDE}*/

        struct ip_sf_socklist   *sflist;

    };

     

    q  成员参数next指向链表的下一个节点。

    q  成员参数multi表示组信息,即在哪一个本地接口上,加入到哪一个多播组。

    q  成员参数sfmode是过滤模式,取值为 MCAST_INCLUDEMCAST_EXCLUDE,分别表示只接收sflist所列出的那些源的多播数据报,和不接收sflist所列出的那些源的多播数据报。

    q  成员参数sflist是源列表。

    2.结构ip_mreqn

    multi成员的原型为结构struct ip_mreqn,定义如下:

     

    struct ip_mreqn

    {

        struct in_addr  imr_multiaddr;      /*多播组的IP地址*/

        struct in_addr  imr_address;        /*本地址网络接口的IP地址*/

        int         imr_ifindex;            /*网络接口序号*/

    };

     

    该结构体的两个成员分别用于指定所加入的多播组的组IP地址,和所要加入组的那个本地接口的IP地址。该命令字没有源过滤的功能,它相当于实现IGMPv1的多播加入服务接口。

    3.结构ip_sf_socklist

    成员sflist的原型为结构struct ip_sf_socklist,定义如下:

     

    struct ip_sf_socklist

    {

        unsigned int    sl_max;      /*当前sl_addr数组的最大可容纳量*/

        unsigned int    sl_count;    /*源地址列表中源地址的数量*/

        __u32         sl_addr[0];     /*源地址列表*/

    };

     

    q  成员参数sl_addr表示是源地址列表;

    q  成员参数sl_count表示是源地址列表中源地址的数量;

    q  成员参数sl_max表示是当前sl_addr数组的最大可容纳量(不确定)。

    4.选项IP_ADD_MEMBERSHIP

    选项IP_ADD_MEMBERSHIP用于把一个本地的IP地址加入到一个多播组,在内核中其处理过程如图11.8所示,在应用层调用函数setsockopt()函数的选项IP_ADD_MEMBE- RSHIP后,内核的处理过程如下,主要调用了函数ip_mc_join_group()

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

    11.8  选项IP_ADD_MEMBERSHIP的内核处理过程

    1)将用户数据复制如内核。

    2)判断广播IP地址是否合法。

    3)查找IP地址对应的网络接口。

    4)查找多播列表中是否已经存在多播地址。

    5)将此多播地址加入列表。

    6)返回处理值。

    5.选项IP_DROP_MEMBERSHIP

    选项IP_DROP_MEMBERSHIP用于把一个本地的IP地址从一个多播组中取出,在内核中其处理过程如图11.9所示,在应用层调用setsockopt()函数的选项IP_DROP_ MEMBERSHIP后,内核的处理过程如下,主要调用了函数ip_mc_leave_group()

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

    11.9  选项IP_DROP_MEMBERSHIP的内核处理过程

    1)将用户数据复制入内核。

    2)查找IP地址对应的网络接口。

    3)查找多播列表中是否已经存在多播地址。

    4)将此多播地址从源地址中取出。

    5)将此地址结构从多播列表中取出。

    6)返回处理值。

    11.3.5  一个多播例子的服务器端

    下面是一个多播服务器的例子。多播服务器的程序设计很简单,建立一个数据包套接字,选定多播的IP地址和端口,直接向此多播地址发送数据就可以了。多播服务器的程序设计,不需要服务器加入多播组,可以直接向某个多播组发送数据。

    下面的例子持续向多播IP地址"224.0.0.88"8888端口发送数据"BROADCAST TEST DATA",每发送一次间隔5s

     

    /*

    *broadcast_server.c - 多播服务程序

    */

    #define MCAST_PORT 8888;

    #define MCAST_ADDR "224.0.0.88"/    /*一个局部连接多播地址,路由器不进行转发*/

    #define MCAST_DATA "BROADCAST TEST DATA"            /*多播发送的数据*

    #define MCAST_INTERVAL 5                            /*发送间隔时间*/

    int main(int argc, char*argv)

    {

        int s;

        struct sockaddr_in mcast_addr;     

        s = socket(AF_INET, SOCK_DGRAM, 0);         /*建立套接字*/

        if (s == -1)

        {

            perror("socket()");

            return -1;

        }

       

        memset(&mcast_addr, 0, sizeof(mcast_addr));/*初始化IP多播地址为0*/

        mcast_addr.sin_family = AF_INET;                /*设置协议族类行为AF*/

        mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);/*设置多播IP地址*/

        mcast_addr.sin_port = htons(MCAST_PORT);        /*设置多播端口*/

       

                                                        /*向多播地址发送数据*/

        while(1) {

            int n = sendto(s,                           /*套接字描述符*/

                                        MCAST_DATA,     /*数据*/

                                        sizeof(MCAST_DATA),    /*长度*/

                                        0,

                                        (struct sockaddr*)&mcast_addr,

                                        sizeof(mcast_addr)) ;

            if( n < 0)

            {

                perror("sendto()");

                return -2;

            }      

           

            sleep(MCAST_INTERVAL);                          /*等待一段时间*/

        }

       

        return 0

    }

    11.3.6  一个多播例子的客户端

    多播组的IP地址为224.0.0.88,端口为8888,当客户端接收到多播的数据后将打印         出来。

    客户端只有在加入多播组后才能接受多播组的数据,因此多播客户端在接收多播组的数据之前需要先加入多播组,当接收完毕后要退出多播组。

     

    /*

    *broadcast_client.c - 多播的客户端

    */

    #define MCAST_PORT 8888;

    #define MCAST_ADDR "224.0.0.88"     /*一个局部连接多播地址,路由器不进行转发*/

    #define MCAST_INTERVAL 5                        /*发送间隔时间*/

    #define BUFF_SIZE 256                           /*接收缓冲区大小*/

    int main(int argc, char*argv[])

    {  

        int s;                                      /*套接字文件描述符*/

        struct sockaddr_in local_addr;              /*本地地址*/

        int err = -1;

       

        s = socket(AF_INET, SOCK_DGRAM, 0);     /*建立套接字*/

        if (s == -1)

        {

            perror("socket()");

            return -1;

        }  

       

                                                    /*初始化地址*/

        memset(&local_addr, 0, sizeof(local_addr));

        local_addr.sin_family = AF_INET;

        local_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        local_addr.sin_port = htons(MCAST_PORT);

       

                                                    /*绑定socket*/

        err = bind(s,(struct sockaddr*)&local_addr, sizeof(local_addr)) ;

        if(err < 0)

        {

            perror("bind()");

            return -2;

        }

       

                                                    /*设置回环许可*/

        int loop = 1;

        err = setsockopt(s,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop));

        if(err < 0)

        {

            perror("setsockopt():IP_MULTICAST_LOOP");

            return -3;

        }

       

        struct ip_mreq mreq;                                    /*加入广播组*/

        mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); /*广播地址*/

        mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*网络接口为默认*/

                                                            /*将本机加入广播组*/

        err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof
        (mreq));

        if (err < 0)

        {

            perror("setsockopt():IP_ADD_MEMBERSHIP");

            return -4;

        }

       

        int times = 0;

        int addr_len = 0;

        char buff[BUFF_SIZE];

        int n = 0;

                                            /*循环接收广播组的消息,5次后退出*/

        for(times = 0;times<5;times++)

        {

            addr_len = sizeof(local_addr);

            memset(buff, 0, BUFF_SIZE);                 /*清空接收缓冲区*/

                                                        /*接收数据*/

            n = recvfrom(s, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr,
            &addr_len);

            if( n== -1)

            {

                perror("recvfrom()");

            }

                                                        /*打印信息*/

            printf("Recv %dst message from server:%s\n", times, buff);

            sleep(MCAST_INTERVAL);

        }

       

                                                        /*退出广播组*/

        err = setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof
        (mreq));

           

        close(s);

        return 0;

    }


    11.2 广 播
     

    前面介绍的TCP/IP知识都是基于单播,即一对一的方式,本节介绍一对多的广播方式。广播是由一个主机发向一个网络上所有主机的操作方式。例如在一个局域网内进行广播,同一子网内的所有主机都可以收到此广播发送的数据。

    11.2.1  广播的IP地址

    要使用广播,需要了解IPv4特定的广播地址。IP地址分为左边的网络ID部分以及右边的主机ID部分。广播地址所用的IP地址将表示主机ID的位全部设置为1。网卡正确配置以后,可以用下面的命令来显示所选用接口的广播地址。

     

    # ifconfig eth0

    eth0 Link encap:Ethernet HWaddr 00:A0:4B:06:F4:8D

          inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0

          UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1

          RX packets:1955 errors:0 dropped:0 overruns:0 frame:31

          TX packets:1064 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:100

          Interrupt:9 Baseaddress:0xe400

     

    第二行输出信息说明eth0网络接口的广播地址为192.168.0.255。这个广播IP地址的前3个字节为网络ID,即192.168.0。这个地址的主机ID部分为255,值255是表示主机ID全为1的十进制数。

    广播地址255.255.255.255是一种特殊的广播地址,这种格式的广播地址是向全世界进行广播,但是却有更多的限制。一般情况下,这种广播类型不会被路由器路由,而一个更为特殊的广播地址,例如192.168.0.255也许会被路由,这取决于路由器的配置。

    通用的广播地址在不同的环境中的含义不同。例如,IP地址255.255.255.255,一些UNIX系统将其解释为在主机的所有网络接口上进行广播,而有的UNIX内核只会选择其中的一个接口进行广播。当一个主机有多个网卡时,这就会成为一个问题。

    如果必须向每个网络接口广播,程序在广播之前应执行下面的步骤。

    1)确定下一个或第一个接口名字。

    2)确定接口的广播地址。

    3)使用这个广播地址进行广播。

    4)对于系统中其余的活动网络接口重复执行步骤(1)~步骤(3)。

    在执行完这些步骤以后,就可以认为已经对每一个接口进行广播。

    11.2.2  广播与单播的比较

    广播和单播的处理过程是不同的,单播的数据只是收发数据的特定主机进行处理,而广播的数据整个局域网都进行处理。

    例如在一个以太网上有3个主机,主机的配置如表11.4所示。

    11.4  某局域网中主机的配置情况

        

    A

    B

    C

    IP地址

    192.168.1.150

    192.168.1.151

    192.168.1.158

    MAC地址

    00:00:00:00:00:01

    00:00:00:00:00:02

    00:00:00:00:00:03

    单播的示意图如图11.3所示,主机A向主机B发送UDP数据报,发送的目的IP192.168.1.151,端口为80,目的MAC地址为00:00:00:00:00:02。此数据经过UDP层、IP层,到达数据链路层,数据在整个以太网上传播,在此层中其他主机会判断目的MAC地址。主机CMAC地址为00:00:00:00:00:03,与目的MAC地址00:00:00:00:00:02不匹配,数据链路层不会进行处理,直接丢弃此数据。

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

    11.3  单播的以太网示意图

    主机BMAC地址为00:00:00:00:00:02,与目的MAC地址00:00:00:00:00:02一致,此数据会经过IP层、UDP层,到达接收数据的应用程序。

    广播的示意图如图11.4所示,主机A向整个网络发送广播数据,发送的目的IP192.168.1.255,端口为80,目的MAC地址为FF:FF:FF:FF:FF:FF。此数据经过UDP层、IP层,到达数据链路层,数据在整个以太网上传播,在此层中其他主机会判断目的MAC地址。由于目的MAC地址为FF:FF:FF:FF:FF:FF,主机C和主机B会忽略MAC地址的比较(当然,如果协议栈不支持广播,则仍然比较MAC地址),处理接收到的数据。

    主机B和主机C的处理过程一致,此数据会经过IP层、UDP层,到达接收数据的应用程序。

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

    11.4  广播的以太网示意图

    11.2.3  广播的示例

    本节中是一个服务器地址发现的代码,假设服务器为A,客户端为B。客户端在某个局域网启动的时候,不知道本局域网内是否有适合的服务器存在,它会使用广播在本局域网内发送特定协议的请求,如果有服务器响应了这种请求,则使用响应请求的IP地址进行连接,这是一种服务器/客户端自动发现的常用方法。

    1.广播例子简介

    如图11.5所示为使用广播的方法发现局域网上服务器的IP地址。服务器在局域网上侦听,当有数据到来的时候,判断数据是否有关键字IP_FOUND,当存在此关键字的时候,发送IP_FOUND_ACK到客户端。客户端判断是否有服务器的响应IP_FOUND请求,并判断响应字符串是否包含IP_FOUND_ACK来确定局域网上是否存在服务器,如果有服务器的响应,则根据recvfrom()函数的from变量可以获得服务器的IP地址。

     

    linux <wbr>udp <wbr>单播 <wbr>组播 <wbr>广播实现

    11.5  利用广播进行服务器IP地址的发现

    2.广播的服务器端代码

    服务器的代码如下,服务器等待客户端向某个端口发送数据,如果数据的格式正确,则服务器会向客户端发送响应数据。

     

    01 

    02      #define IP_FOUND "IP_FOUND"                  /*IP发现命令*/

    03      #define IP_FOUND_ACK "IP_FOUND_ACK"     /*IP发现应答命令*/

    04      void    HandleIPFound(void*arg)

    05      {

    06      #define BUFFER_LEN 32

    07          int ret = -1;

    08          SOCKET sock = -1;

    09          struct sockaddr_in local_addr;          /*本地地址*/

    10          struct sockaddr_in from_addr;           /*客户端地址*/

    11      int from_len;

    12          int count = -1;

    13          fd_set readfd;

    14          char buff[BUFFER_LEN];

    15          struct timeval timeout;

    16          timeout.tv_sec = 2;                     /*超时时间2s*/

    17          timeout.tv_usec = 0;

    18     

    19          DBGPRINT("==>HandleIPFound\n");

    20         

    21           sock = socket(AF_INET, SOCK_DGRAM, 0);  /*建立数据报套接字*/

    22          if( sock < 0 )

    23          {

    24              DBGPRINT("HandleIPFound: socket init error\n");

    25              return;

    26          }

    27         

    28          /*数据清零*/

    29          memset((void*)&local_addr, 0, sizeof(struct sockaddr_in));
                                                            /
    *清空内存内容*/

    30          local_addr.sin_family = AF_INET;            /*协议族*/

    31           local_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*本地地址*/

    32          local_addr.sin_port = htons(MCAST_PORT);        /*侦听端口*/

    33          /*绑定*/

    34          ret = bind(sock, (struct sockaddr*)&local_addr, sizeof(local_
                addr));

    35          if(ret != 0)

    36          {

    37              DBGPRINT("HandleIPFound:bind error\n");

    38              return;

    39          }

    40     

    41           /*主处理过程*/

    42          while(1)

    43          {

    44              /*文件描述符集合清零*/

    45              FD_ZERO(&readfd);

    46              /*将套接字文件描述符加入读集合*/

    47              FD_SET(sock, &readfd);

    48              /*select侦听是否有数据到来*/

    49              ret = selectsocket(sock+1, &readfd, NULL, NULL, &timeout);

    50              switch(ret)

    51               {

    52                  case -1:

    53                  /*发生错误*/

    54                      break;

    55                  case 0:

    56                      /*超时*/

    57                      //超时所要执行的代码

    58                     

    59                      break;

    60                  default:

    61                   /*有数据到来*/

    62                      if( FD_ISSET( sock, &readfd ) )

    63                      {

    64                              /*接收数据*/

    65                          count = recvfrom( sock, buff, BUFFER_LEN, 0,
                                ( struct sockaddr
    *) &from_addr, &from_len );

    66                          DBGPRINT( "Recv msg is %s\n", buff );

    67                          if( strstr( buff, IP_FOUND ) )
                                    /
    *判断是否吻合*/

    68                          {

    69                              /*将应答数据复制进去*/

    70                              memcpy(buff, IP_FOUND_ACK,strlen(IP_
                                    FOUND_ACK)+1);

    71                               /*发送给客户端*/

    72                              count = sendto( sock, buff, strlen( buff ),
                                    0, ( struct sockaddr
    *) &from_addr, from_
                                    len );

    73                          }

    74                      }

    75              }

    76          }

    77          PRINT("<==HandleIPFound\n");

    78

    79          return;

    80      }

     

    服务器端分为如下步骤:

    q  16行和第17行定义了服务器等待的超时时间,为2s

    q  29行将地址结构清零。

    q  30行定义地址协议族为AF_INET

    q  31行设置IP地址为任意本地地址。

    q  32行设置侦听的端口。

    q  34行将本地的地址绑定到一个套接字文件描述符上。

    q  42行开始为主处理过程,使用select函数,按照2s的超时时间侦听是否有数据到来。

    q  45行文件描述符集合清零。

    q  47行将套接字文件描述符加入读集合。

    q  49select侦听是否有数据到来。

    q  50行查看select的返回值。

    q  52select发生错误。

    q  55select超时。

    q  60行有可读的数据到来。

    q  65行接收数据。

    q  67行查看接收到的数据是否匹配。

    q  70行复制响应数据。

    q  72行发送响应数据到客户端。

    3.广播的客户端代码

    广播的客户端函数代码如下,客户端向服务器端发送命令IP_FOUND,并等待服务器端的回复,如果有服务器回复,则向服务器发送IP_FOUND_ACK,否则发送10遍后退出。

     

    01      #define IP_FOUND "IP_FOUND"                  /*IP发现命令*/

    02      #define IP_FOUND_ACK "IP_FOUND_ACK"     /*IP发现应答命令*/

    03      #define IFNAME "eth0"

    04      void    IPFound(void*arg)

    05      {

    06      #define BUFFER_LEN 32

    07          int ret = -1;

    08          SOCKET sock = -1;

    09          int so_broadcast = 1;

    10          struct ifreq ifr;          

    11          struct sockaddr_in broadcast_addr;      /*本地地址*/

    12          struct sockaddr_in from_addr;           /*服务器端地址*/

    13          int from_len;

    14          int count = -1;

    15          fd_set readfd;

    16          char buff[BUFFER_LEN];

    17          struct timeval timeout;

    18          timeout.tv_sec = 2;                 /*超时时间2s*/

    19          timeout.tv_usec = 0;

    20     

    21         

    22           sock = socket(AF_INET, SOCK_DGRAM, 0);/*建立数据报套接字*/

    23          if( sock < 0 )

    24          {

    25              DBGPRINT("HandleIPFound: socket init error\n");

    26              return;

    27          }

    28          /*将需要使用的网络接口字符串名字复制到结构中*/

    29          strcpy(ifr.ifr_name,IFNAME,strlen(IFNAME));

    30          /*发送命令,获取网络接口的广播地址*/

    31          if(ioctl(sock,SIOCGIFBRDADDR,&ifr) == -1)

    32              perror("ioctl error"),exit(1);

    33          /*将获得的广播地址复制给变量broadcast_addr*/

    34          memcpy(&broadcast_addr, &ifr.ifr_broadaddr, sizeof(struct
                sockaddr_in ));

    35          broadcast_addr.sin_port = htons(MCAST_PORT);/*设置广播端口*/

    36         

    37          /*设置套接字文件描述符sock为可以进行广播操作*/

    38          ret = setsockopt(sock,

    39                  SOL_SOCKET,

    40                  SO_BROADCAST,

    41              &so_broadcast,

    42              sizeof so_broadcast);

    43             

    44           /*主处理过程*/

    45          int times = 10;

    46          int i = 0;

    47          for(i=0;i<times;i++)

    48          {

    49              /*广播发送服务器地址请求*/

    50              ret = sendto(sock,

    51                          IP_FOUND,

    52                          strlen(IP_FOUND),

    53                          0,

    54                          (struct sockaddr*)&broadcast_addr,

    55                          sizeof(broadcast_addr));

    56              if(ret == -1){

    57                  continue;  

    58              }

    59              /*文件描述符集合清零*/

    60              FD_ZERO(&readfd);

    61              /*将套接字文件描述符加入读集合*/

    62              FD_SET(sock, &readfd);

    63              /*select侦听是否有数据到来*/

    64              ret = selectsocket(sock+1, &readfd, NULL, NULL, &timeout);

    65              switch(ret)

    66               {

    67                  case -1:

    68                      /*发生错误*/

    69                      break;

    70                  case 0:

    71                      /*超时*/

    72                      //超时所要执行的代码

    73                     

    74                      break;

    75                  default:

    76                   /*有数据到来*/

    77                      if( FD_ISSET( sock, &readfd ) )

    78                      {

    79                          /*接收数据*/

    80                          count = recvfrom( sock, buff, BUFFER_LEN, 0,
                                ( struct sockaddr
    *) &from_addr, &from_len );

    81                          DBGPRINT( "Recv msg is %s\n", buff );

    82                          if(strstr(buff, IP_FOUND_ACK))/*判断是否吻合*/

    83                          {

    84                              printf("found server, IP is %s\n",inet_ntoa
                                    (from_addr.sin_addr));

    85                          }

    86                      break;/*成功获得服务器地址,退出*/

    07                      }

    08              }

    09          }  

    90          return;

    91      }

     

    客户端分为如下步骤:

    q  18行和第19行定义了服务器等待的超时时间,为2s

    q  22行建立数据报套接字。

    q  29行复制网络接口名称。

    q  31行获得与网络接口名称对应的广播地址。

    q  34行和第35行设置广播的地址和端口。

    q  3842行设置可广播地址,因为默认情况下是不可广播的。

    q  47行开始为主处理过程,发送多次广播数据,查看网络上是否有服务器存在。

    q  5055行发送服务器请求到整个局域网上。

    q  60行文件描述符集合清零。

    q  62行将套接字文件描述符加入读集合。

    q  64select侦听是否有数据到来。

    q  65行查看select的返回值。

    q  67select发生错误。

    q  70select超时。

    q  75行有可读的数据到来。

    q  65行接收数据。

    q  80行查看接收到的数据是否匹配。

    展开全文
  • linux udp组播接收问题及原理分析

    千次阅读 2016-06-29 11:38:19
    在某个网络环境下,同一个udp组播源(igmpv2),同样的收流代码,在windows上能收到,linux上收不到。排除网络拓扑结构、编程语言、硬件驱动等问题,我们就此问题来分析原理及解决方案。 环境: 交换机...

    转自 http://blog.csdn.net/rcfalcon/article/details/35221759

    现象:

    在某个网络环境下,同一个udp组播源(igmpv2),同样的收流代码,在windows上能收到,linux上收不到。排除网络拓扑结构、编程语言、硬件驱动等问题,我们就此问题来分析原理及解决方案。


    环境:

    交换机出,组播地址224.X.X.X,机器多网卡,eth0收流

    配置静态IP地址,已关闭NetworkManager服务,iptables规则配置没有问题


    实验:

    1、在不同的LINUX发行版(opensuse、centos、ubuntu)上进行测试:均收不到。

    2、直接tcpdump:收不到

    3、配置一条路由 route add -net 224.0.0.1 netmask 255.0.0.0 dev eth0,重启网卡:能收到流,约1分钟以后断掉,重启收流程序没有反应

    4、重复3测试,发现规律:每次重启网卡后,配置路由,大概能收流1分钟,就再也收不到流了

    5、将路由配置成 route add -net 0.0.0.0 netmask 0.0.0.0 dev eth0,重启网卡:能稳定收流了


    分析:

    1、与LINUX发行版无关,而是LINUX内核统一的问题。

    2、IGMP V2客户端不请求的时候,交换机不会将组播分配到网卡。

    3、4、5: 这里需要结合一下IGMP V2协议的原理:


    ①交换机将询问所有的网内机器,谁需要加入组播组

    ②若一台主机需要加入组播组,需要发送入组报文

    ③交换机收到入组报文,给主机分发IP数据报文

    ④退组机制有两种:1、主机主动退出(发出退出报文) 2、交换机轮询存活主机没有响应(超时)


    可以看到,问题大概就出在这里。

    具体的我查阅了IGMP原理手册,IGMP各种信令报文走的无外乎224.0.0.1 或224.1.1.1(不知是否可配),怀疑255.0.0.0将主机对于IGMPV2的存活查询反馈报文给过滤了,而这种反馈报文每次未必走的一样的地址,包括入组报文,后续也发不出去了。


    解决方法:

    1、暴力流:直接将路由的全局出口均走收流网卡( route add -net 0.0.0.0 netmask 0.0.0.0 dev eth0),其他网卡若需要通信,单独配路由。

    2、待研究:需要仔细研究IGMPV2的信令发送(不知是否和交换机配置相关),来配置合适的路由规则。


    这里要说一下windows为什么不用关心这些,我理解windows自身有一个编写的比较好的NetworkManager服务,能够根据应用程序的请求灵活配置路由规则,这里不详细研究了。总之,若同样的网络环境,LINUX收组播流有各种奇葩问题,基本都是路由配置的问题。

    展开全文
  • <p>2、用2套光纤收发器传送2路udp组播数据,每路均含有多个组播包,分别接入交换机2、3口即接入vlan2、vlan3。 <p>2、要通过交换机4口将vlan2、vlan3的多个udp组播包指定接入专用IP解码设备解码,...
  • udp组播通信实现(c++)

    千次阅读 2019-09-01 10:23:14
    前几天需要做一个和第三方平台配套的单向通信,反向隔离的通信接口,原本确定是采用UDP组播方式实现,通信设备中间加正向网络隔离装置防止反通信。我想着赶紧先行测试验证一下,哪知我好不容易花了一个下午编译测试...
  • 1.4实际情况下,经常需要对一组特定的主机进行通信,而不是所有局域网上的主机,这时候就有了组播 1.5IP组播(也称多址广播或多播),是一种允许一台或多台主机发送数据包到多台主机的TCP/IP网路技术。 1.6多播是 ...
  • linux udp 单播组播广播实现

    千次阅读 2014-04-19 00:58:09
    多播广播是用于建立分步式系统:例如网络游戏、ICQ聊天...1、组播和广播需要在局域网内才能实现,另外得查看linux系统是否支持多播和广播:# ifconfig UP BROADCAST MULTICAST MTU:1500 跃点数:1说明该网卡支持 2、
  • linux组播的实现

    千次阅读 2019-05-19 14:59:14
    linux组播的实现 组播介绍 在交换机中有三种通信方式:单播(unicast)、广播(broadcast)、组播(multicast) 单播解决了点对点通信的需求; 广播是点对多点的通信,其存在两个缺点: 1)只能在同一网段内...
  • Linux UDP 单播 组播 广播实现

    千次阅读 2014-03-07 21:24:30
    1、组播和广播需要在局域网内才能实现,另外得查看linux系统是否支持多播和广播:ifconfig UP BROADCAST MULTICAST MTU:1500 跃点数:1 说明该网卡支持 2、发送多播包的主机需要设置网关,否则运行sendto()会出现...
  • linux组播(多播)的实现

    千次阅读 2020-08-28 00:05:33
    参考:https://blog.csdn.net/jmq_0000/article/details/7095727 ... ... 一、组播地址划分 局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。 .
  • 在软件设计过程中可能需要发送某个组播或者广播报文,利用指定的ip地址。以下为此代码的实例: #defineSOCKET_INIT -1 #define DEST_IP "239.194.0.18" intlocalIpAddr = 0xa010101; int sockOpen(void ) { ...
  • 实现igmp v3协议下通过udp组播加组的过程,区别于普通的udp组播通信
  • 首先,指定源组播linux 和windows编程稍微有些不同: Linux:bind的是组播地址和组播端口 windows:bind的是接收网卡的地址(local_ip)和组播端口 对于一个网卡收流,其他网卡不用收流 现象1):组播...
  • 项目需求需要在windows2008 r2的环境上搭建虚拟机Linux系统,并用Linux系统接收组播数据。在windows2008 r2搭建虚拟机,已经介绍过了。关键的地方在与,桥接网卡,静态IP要设置好。 组播是基于IP/TCP协议簇下的多播...
  • Linux配置及测试IP多(Multicast)

    千次阅读 2016-08-11 14:53:18
    224.0.0.0到239.255.255.255 这些都叫组播地址我举个不太严谨 的例子有几个主机已经加入了 224.1.1.1 这个组192.168.1.1---192.168.1.2---192.168.1.3---比如这三个ip地址加入了组224.1.1.1然后组播源10.1.1.1--- 向...
  • UDP组播

    千次阅读 2013-05-09 11:32:43
    ​对某些应用而言,如分布式数据库开发,一个开发工作有很多人需在不同的地点协作并经常交换情况。 ​在的规模比较小的情况下,只需点对点交换信息即可; ​如果的规模比较大,点对点交换信息不管对网络还是...
  • 组播概述02. 组播应用分类03. 组播地址04. 组播地址与 MAC 地址的关系05. 套接字选项06. 程序示例07. 附录 01. 组播概述 单播用于两个主机之间的端对端通信,广播用于一个主机对整个局域网上所有主机上的数据通信。...
  • linux网络编程中,组播通信在setsockopt接口中IP_ADD_MEMBERSHIP添加组播成员。但组播成员个数有限制,默认是20个。若需要添加更多的个数,则需要修改系统配置文件。本文档给出了怎么去修改默认的组播数。
  • 1. UDP协议简介 UDP是User Datagram Protocol的简称,全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供...
  • UDP 单播 组播 广播

    2013-07-01 19:46:41
    多播广播是用于建立分步式系统:例如网络游戏、ICQ...1、组播和广播需要在局域网内才能实现,另外得查看linux系统是否支持多播和广播:# ifconfig UP BROADCAST MULTICAST MTU:1500 跃点数:1说明该网卡支持 2、发送
  • 地址表示某一IP接口,单和广播是寻址方案中的两个极端(要么单个要么全部),多则意在两者之间提供一种折中方案。多是用于建立分布式系统的重要工具,例如:网络游戏、ICQ聊天构建、远程视频会议系统等。...
  • Linux网络编程】多播、组播

    千次阅读 2016-08-29 22:29:07
    概述 单播用于两个主机之间的端对端通信,广播用于一个主机对整个局域网上所有主机上的数据通信。...IP 多播(也称多址广播或组播)技术,是一种允许一台或多台主机(多播源)发送单一数据包到多台主机(一次的,同

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,909
精华内容 5,563
关键字:

linuxudp组播配置

linux 订阅