精华内容
下载资源
问答
  • 自己做的C#组播程序测试例子,winXP-win10下测试可用,发送接收稳定,界面简洁,编写网络控制涉及组播内容的可以参考,理清了以往地址和端口设置不清楚的部分内容,可以修改反复试验,结合wireshark抓包观察,弄清楚...
  • C#组播程序

    2014-04-22 15:47:16
    基于C#网络应用编程,采用组播形式分享信息。采用最新消息始终当前显示。
  • UDP组播程序

    2012-06-20 00:10:32
    UDP组播程序,实现网内UDP的组播,分客户端和服务器两个程序
  • 16 QT 的网络组播程序

    千次阅读 2017-05-15 11:40:21
    2. 创建udp对象,绑定地址与端口号,并指定使用哪个网卡设备发出组播包 udp = new QUdpSocket(this); if ( ! udp -> bind(QHostAddress ::AnyIPv4 , PORT)) { QMessageBox ::critical (this, "error...

    在一个linux系统下,有可能有多个网卡, 每网卡又有可能有多个IP地址.
    在QT里用QNetworkInterface对象表示一个网卡, QNetworkAddressEntry表示一个网卡的IP地址

    //以下代码表示获取一个系统里的所有网卡信息,每个网卡的IP地址

        QList<QNetworkInterface> list;
        QList<QNetworkAddressEntry> list_addrs;
        QNetworkInterface intf;
        list = QNetworkInterface::allInterfaces(); //获取系统里所有的网卡对象
        for (int i = 0; i < list.size(); i++)
        {
            intf = list.at(i);
            qDebug() << intf.name() << "  " << intf.flags(); // 获取网卡的状态, 是否激活,能否广播组播等 .
    
            list_addrs = intf.addressEntries(); // 一个网卡可以设多个地址,获取当前网卡对象的所有ip地址
            for (int j = 0; j < list_addrs.size(); j++)
                qDebug() << list_addrs.at(j).ip().toString();
    
            qDebug() << "###############";
        }

    程序执行的输出:
    “lo” QFlags(0x1|0x2|0x8)
    “127.0.0.1”
    “::1”
    ###############
    “enp1s0” QFlags(0x1|0x4|0x20)
    “192.168.1.10”
    ###############
    “enp3s0” QFlags(0x1|0x2|0x4|0x20)
    “192.168.250.250”
    “192.168.2.250”
    “fe80::2e0:4cff:fe1d:7b1f%enp3s0”
    ###############
    “virbr0” QFlags(0x1|0x4|0x20)
    “192.168.122.1”
    ###############
    “virbr0-nic” QFlags(0x4|0x20)
    ###############

    //
    组播的实现:

    发送端:
    1. 发送端找到处在工作状态,并能支持组播的网卡设备` QList list;
    QNetworkInterface intf;
    list = QNetworkInterface::allInterfaces(); //获取系统里所有的网卡对象
    for (int i = 0; i < list.size(); i++)
    {
    intf = list.at(i);

        QNetworkInterface::InterfaceFlags flags = intf.flags();
        //找出处在执行状态,能支持组播的网卡对象
        if ((flags & QNetworkInterface::IsRunning) && (flags & QNetworkInterface::CanMulticast))
            break;
    }
    

    `
    2. 创建udp对象,绑定地址与端口号,并指定使用哪个网卡设备发出组播包

        udp = new QUdpSocket(this);
        if (!udp->bind(QHostAddress::AnyIPv4, PORT))
        {
            QMessageBox::critical(this, "error", "bind failed");
            exit(0);
        }
        udp->setMulticastInterface(intf);   

    3 往组播地址发出数据

    udp->writeDatagram(str.toUtf8(), QHostAddress(MULTI_ADDR), PORT);

    ///
    接收端:

    1. 接收端创建udp对象,绑定地址端口号
        udp = new QUdpSocket(this);
        if (!udp->bind(QHostAddress::AnyIPv4, PORT))
        {
            QMessageBox::critical(this, "error", "bind failed");
            exit(0);
        }
    

    2 加入网络组

        if (!udp->joinMulticastGroup(QHostAddress(MULTI_ADDR)))
        {
            QMessageBox::critical(this, "error", "join multicast group failed");
            exit(0);
        }

    3 连接udp的readyRead信号到一个槽函数,在槽函数里readDatagram接收数据

    完整工程源码:
    http://download.csdn.net/detail/jklinux/9842550

    展开全文
  • qt网络组播程序 

    2017-05-15 11:31:29
    qt网络组播程序 
  • C++组播和单播代码,包括server端和client端的全部封装代码和测试代码。支持windows经过72小时性能测试,没有问题。
  • VS2010组播程序

    2012-03-01 13:05:31
    这是一个基于VS2010的组播程序,界面美观,且程序清晰!
  • 组播程序源码

    2012-02-15 09:28:31
    实现服务器和客户端的组播测试,希望能有用。
  • 一个简单的C++实现UDP组播发送和接收的程序 一个简单的C++实现UDP组播发送和接收的程序 一个简单的C++实现UDP组播发送和接收的程序 一个简单的C++实现UDP组播发送和接收的程序
  • UDP的组播客户端程序

    2018-11-08 16:37:13
    测试可用的UDP组播发送客户端程序,可自行更改为服务端,收发一体。
  • java课程设计,基于UDP协议的局域网聊天程序,可以实现多人聊天,一对一私聊等功能,有重复用户名检测功能。有用户列表,上下线提醒,在线人数显示等。
  • UDP广播组播程序

    2014-11-07 19:30:03
    使用C#开发的UDP广播组播程序,使用C#开发的UDP广播组播程序
  • VS2008中集成的Qt实现的网络组播程序,调试通过可以运行
  • Linux下组播测试程序(IGMP),有服务器端和客户端源码和Makefile,及系统中如何添加到组播
  • windows下使用UDP的简单实例,包括单、广播和多的实例代码,使用VS2010编写
  • 已实现的功能: 1、用户登录 2、在线用户列表 3、实时聊天:群聊、私聊 4、好友上线提示 JFrame界面简洁、操作简单!
  • 基于MFC对话框的UDP组播收发源代码程序,含发送和接收2个工程。点击发送端按钮,接收端可异步接收,经测试,Release编译后可成功运行。
  • Linux下组播源码,含客户端和服务器 接收端 zb_recv.c 发送端 zb_recv.c
  • 使用Qt5.11.2的官方例子,在ubuntu18.04的虚拟机下向239.255.255.250 去发送组播可以发送出去。 但是在windows7下使用这个版本的官方例子怎么向239.255.255.250发送组播包就发不出去呢?wirshark就住不到包.哪位...
  • UDP组播的发送和接收程序

    热门讨论 2008-12-29 13:45:02
    采用C#语言,通过UDP协议实现组播数据的上传和下载,组播地址为224.100.100.4.
  • linux 下 qt 编写的一个简单组播应用程序 初学者 还望大家多多指教
  • 组播测试软件

    2018-12-06 17:06:02
    简单好用的组播测试软件,可以在实验环境中使用,也可以直接在真实网络里测试,非常方便
  • linux网络编程:多播(组播)编程

    千次阅读 2018-04-24 11:12:36
    什么是多播 单播用于两个主机之间的端对端通信,广播用于一个主机对整个局域网上所有主机... 多播,也称为“组播”,将局域网中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进...

    什么是多播    

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

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

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

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

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

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

    属于永久组的地址:

    224.0.0.1   所有组播主机

    224.0.0.2   所有组播路由器

    224.0.0.4    DRMRP路由器

    224.0.0.5    所有OSPF的路由器

    224.0.0.6    OSPF指派路由器

    224.0.0.9    RPIv2路由器

    224.0.0.10  EIGRP路由器

    224.0.0.13  PIM路由器

    224.0.0.22   IGMPv3

    224.0.0.25   RGMP

    224.0.1.1      NTP网络时间协议

    IP到以太网地址映射
        因为以太网支持多种协议,所以要采取措施分配多播地址,避免冲突。IEEE管理以太网多播地址分配。IEEE把一块以太网多播地址分给IANA以支持IP多播。块的地址都以01:00:5e开头。
    第25位为0,低23位为IPv4组播地址的低23位。IPv4组播地址与MAC地址的映射关系如图所示:



    由于多播组号中的最高5bit在映射过程中被忽略,因此每个以太网多播地址对应的多播组是不唯一的。32个不同的多播组号被映射为一个以太网地址。例如,多播地址
    224.128.64.32(十六进制e0.80.40.20)和224.0.64.32(十六进制e0.00.40.20)都映射为同一以太网地址01:00:5e:00:40:20。


    多播主机

    多播主机分为三个级别:

        0级:主机不能发送或接收I P多播。

    这种主机应该自动丢弃它收到的具有D类目的地址的分组。

        1级:主机能发送但不能接收I P多播。

    在向某个I P多播组发送数据报之前,并不要求主机加入该组。多播数据报的发送方式与单播一样,除了多播数据报的目的地址是 I P多播组之外。网络驱动器必须能        够识别出这个地址,把在本地网络上多播数据报。

        2级:主机能发送和接收I P多播。

    为了接收I P多播,主机必须能够加入或离开多播组,而且必须支持IGMP,能够在至少一个接口上交换组成员信息。多接口主机必须支持在它的接口的一个子网上的多播N     et/3符合2级主机要求,可以完成多播路由器的工作。与单播IP选路一样,我们假定所描述的系统是一个多播路由器,并加上了Net/3多播选路的程序


    linux多播编程

    linux多播编程步骤:

    1>建立一个socket;

    2>设置多播的参数,例如超时时间TTL,本地回环许可LOOP等

    3>加入多播组

    4>发送和接收数据

    5>从多播组离开

    多播程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的。


    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));

    多播编程实例

      

    服务器端

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

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

    /*
    *broadcast_server.c - 多播服务程序
    */
    #include<stdio.h>
    #include<stdlib.h>  
    #include<string.h>     
    #include<unistd.h>      
    #include<sys/types.h>    
    #include<sys/socket.h>     
    #include<arpa/inet.h>    
    #include<netinet/in.h> 
    
    #define MCAST_PORT 		8888
    #define MCAST_ADDR 		"224.0.0.100"    /*一个局部连接多播地址,路由器不进行转发*/
    #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;
    }
    

    客户端

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

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

    /*
    *broadcast_client.c - 多播的客户端
    */
    #include<stdio.h>
    #include<stdlib.h>  
    #include<string.h>     
    #include<unistd.h>      
    #include<sys/types.h>    
    #include<sys/socket.h>     
    #include<arpa/inet.h>    
    #include<netinet/in.h>    
     
    #define MCAST_PORT 		8888
    #define MCAST_ADDR 		"224.0.0.100"     /*一个局部连接多播地址,路由器不进行转发*/
    #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;
    }
    


    代码下载路径:linux网络编程:多播组播应用实例


    本文参考博客:http://blog.csdn.net/jmq_0000/article/details/7095727





    展开全文
  • 使用socket类实现c# UDP组播的发送和接收 ;可以指定专门网卡接收消息;
  • UDP 组播/多播示例程序,实现数据多播发送客户端功能。
  • 如何创建组播应用程序

    千次阅读 2015-10-24 19:15:51
    本文试图成为学习TCP/IP网络组播技术的入门材料。...在文章的最后给出了一个简单的C语言套接字编程例子,说明如何创建组播应用程序。    一、导言    在网络中,主机间可以用三种不同的地址进行
    本文试图成为学习TCP/IP网络
    组播
    技术的入门材料。文中介绍了组播通信的概念及原理,以及用于组播应
    
    用编程的 Linux API的详细资料。为了使读者更加完整的了解Linux 组播的整体概念,文中对实现该技术的核
    心函数也做了介绍。在文章的最后给出了一个简单的C语言套接字编程例子,说明如何创建组播应用程序。
      
        一、导言
      
        在网络中,主机间可以用三种不同的地址进行通信:
      
        单播地址(unicast):即在子网中主机的唯一地址(接口)。如IP地址:192.168.100.9或MAC地址:
    80:C0:F6:A0:4A:B1。
      
        广播地址:这种类型的地址用来向子网内的所有主机(接口)发送数据。如广播IP地址是192.168.100.255
    ,MAC广播地址:FF:FF:FF:FF:FF。
      
         组播 地址:通过该地址向子网内的多个主机即主机群(接口)发送数据。
      
        如果只是向子网内的部分主机发送报文,组播地址就很有用处了;在需要向多个主机发送多媒体信息
    (如实时音频、视频)的情况下,考虑到其所需的带宽,分别向每一客户端主机发送数据并不是个好办法,如
    果发送主机与某些接收端的客户主机不在子网之内,采用广播方式也不是一个好的解决方案。
      
        二、组播地址
      
        大家知道,IP地址空间被划分为A、B、C三类。第四类即D类地址被保留用做组播地址。在第四版的IP
    协议(IPv4)中,从224.0.0.0到239.255.255.255间的所有IP地址都属于D类地址。
      
        组播地址中最重要的是第24位到27位间的这四位,对应到十进制是224到239,其它28位保留用做组
    播的组标识,如下图所示:
        
      图1 组播地址示意图
      
        IPv4的组播地址在网络层要转换成网络物理地址。对一个单播的网络地址,通过ARP协议可以获取
    与IP地址对应的物理地址。但在组播方式下ARP协议无法完成类似功能,必须得用其它的方法获取物理地址。
    在下面列出的RFC文档中提出了完成这个转换过程的方法:
      
      RFC1112:Multicast IPv4 to Ethernet physical address correspondence 
      RFC1390:Correspondence to FDDI 
      RFC1469:Correspondence to Token-Ring networks
      
        在最大的以太网地址范围内,转换过程是这样的:将以太网地址的前24位最固定为01:00:5E,这几位
    是重要的标志位。紧接着的一位固定为0,其它23位用IPv4组播地址中的低23位来填充。该转换过程如下图所示:
       
      图2 地址转换示意图
      
        例如,组播地址为224.0.0.5其以太网物理地址为01:00:5E:00:00:05。
      
        还有一些特殊的IPv4组播地址:
      
        224.0.0.1:标识子网中的所有主机。同一个子网中具有组播功能的主机都是这个组的成员。
      
        224.0.0.2:该地址用来标识网络中每个具有组播功有的路由器。
      
        224.0.0.0----224.0.0.255范围内的地址被分配给了低层次的协议。向这些范围内的地址发送数据包,
    有组播功能的路由器将不会为其提供路由。
      
        239.0.0.0----239.255.255.255间的地址分配用做管理用途。这些地址被分配给局部的每一个组织,
    但不可以分配到组织外部,组织内的路由器不向在组织外的地址提供路由。
      
        除了上面列出的部分组播地址外,还有许多的组播地址。在最新版本的RFC文档“Assinged Numbers”
    中有完整的介绍。
      
        下面的表中列出了全部的组播地址空间,同时还列出了相应的地址段的常用名称及其TTL(IP包的
    活时间)。在IPv4组播方式下,TTL有双重意义:正如大家所知的,TTL原本用来控制数据包在网络中的存活时
    间,防止由于路由器配置错误导致出现数据包传播的死循环;在组播方式下,它还代表了数据包的活动范
    围,如:数据包在网络中能够传送多远?这样就可以基于数据包的分类来定义其传送范围。
      
        范围 TTL 地址区间 描述
      
        节点(Node) 0 只能向本机发送的数据包,不能向网络中的其它接口传送
      
        链路(Link) 1 224.0.0.0-224.0.0.255 只能在发送主机所在的一个子网内的传送,不会通过路由器转发。
      
        部门 32 239.255.0.0-239.255.255.255 只在整个组织下的一个部门内(Department) 传送
      
        组织 64 239.192.0.0--239.195.255.255 在整个组织内传送(Organization)
      
        全局(Global)255 224.0.1.0--238.255.255.255 没有限制,可全局范围内传送
      
        三、组播的工作过程
      
        在局域网内,主机的网络接口将到目的主机的数据包发送到高层,这些数据包中的目的地址是物理接口地
    址或广播地址。
      
        如果主机已经加入到一个组播组中,主机的网络接口就会识别出发送到该组成员的数据包。
      
        因此,如果主机接口的物理地址为80:C0:F6:A0:4A:B1,其加入的组播组为224.0.1.10,则发送给主机的数
    据包中的目的地址必是下面三种类型之一:
      
        接口地址:80:C0:F6:A0:4A:B1
      
        广播地址:FF:FF:FF:FF:FF:FF:FF:FF
      
        组播地址:01:00:5E:00:01:0A
      
        广域网中,路由器必须支持组播路由。当主机中运行的进程加入到某个组播组中时,主机向子网中的所
    有组播路由器发送IGMP(Internet分组管理协议)报文,告诉路由器凡是发送到这个组播组的组播报文都必须发
    送到本地的子网中,这样主机的进程就可以接收到报文了。子网中的路由器再通知其它的路由器,这些路由器就
    知道该将组播报文转发到哪些子网中去。
      
        子网中的路由器也向224.0.0.1发送一个IGMP报文(224.0.0.1代表组中的全部主机),要求组中的主
    机提供组的相关信息。组中的主机收到这个报文后,都各将计数器的值设为随机值,当计数器递减为0 时再向路
    由器发送应答。这样就防止了组中所有的主机同时向路由器发送应答,造成网络拥塞。主机向组播地址发送一
    个报文做为对路由器的应答,组中的其它主机一旦看到这个应答报文,就不再发送应答报文了,因为组中的主
    机向路由器提供的都是相同的信息,所以子网路由器只需得到组中一个主机提供的信息就可以了。
      
        如果组中的主机都退出了,路由器就收不到应答,因此路由器认为该组目前没有主机加入,遂停止到
    该子网报文的路由。IGMPv2的解决方案是:组中的主机在退出时向224.0.0.2 发送报文通知组播路由器。
      
        四、应用编程接口(API)
      
        如果你有套接字编程的经验,就会发现,对组播选项所进行的操作只需五个新的套接字操作。函数
    setsockopt()及getsockopt()用来建立和读取这五个选项的值。下表中列出了组播的可选项,并列出其数据类型和描述:
      
        IPv4 选项 数据类型 描 述
      
        IP_ADD_MEMBERSHIP struct ip_mreq 加入到组播组中
      
        IP_ROP_MEMBERSHIP struct ip_mreq 从组播组中退出
      
        IP_MULTICAST_IF struct ip_mreq 指定提交组播报文的接口
      
        IP_MULTICAST_TTL u_char 指定提交组播报文的TTL
      
        IP_MULTICAST_LOOP u_char 使组播报文环路有效或无效
      
        在头文件中定义了ip_mreq结构:
      
      struct ip_mreq {
      struct in_addr imr_multiaddr; /* IP multicast address of group */
      struct in_addr imr_interface; /* local IP address of interface */
      };
      
        在头文件中组播选项的值为:
      
      #define IP_MULTICAST_IF 32
      #define IP_MULTICAST_TTL 33
      #define IP_MULTICAST_LOOP 34
      #define IP_ADD_MEMBERSHIP 35
      #define IP_DROP_MEMBERSHIP 36
      IP_ADD_MEMBERSHIP
      
        若进程要加入到一个组播组中,用soket的setsockopt()函数发送该选项。该选项类型是ip_mreq
    结构,它的第一个字段imr_multiaddr指定了组播组的地址,第二个字段imr_interface指定了接口的IPv4地址。
      
        IP_DROP_MEMBERSHIP
      
        该选项用来从某个组播组中退出。数据结构ip_mreq的使用方法与上面相同。
      
        IP_MULTICAST_IF
      
        该选项可以修改网络接口,在结构ip_mreq中定义新的接口。
      
        IP_MULTICAST_TTL
      
        设置组播报文的数据包的TTL(生存时间)。默认值是1,表示数据包只能在本地的子网中传送。
      
        IP_MULTICAST_LOOP
      
         组播 组中的成员自己也会收到它向本组发送的报文。这个选项用于选择是否激活这种状态。
      
        五、一个组播通信的例子
      
        下面给出一个简单的例子实现文中阐述的思想:由一个进程向一个组播组发送报文,组播组中
    的相关进程接收报文,并将报文显示到屏幕上。
      
        下面的代码实现了一个服务进程,它将标准输入接口输入的信息全部发送到组播组224.0.1.1。
    你会发现,将信息发送到组播组不需要特别的操作,只要设置好组播组的目的地址就足够了。若在开发过
    程中,Loopback和TTL这两个选项的默认值不适合应用程序,可以加以调整。
      
        服务程序
      
        将标准输入端口的输入发送到组播组224.0.1.1。
      
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #define MAXBUF 256
      #define PUERTO 5000
      #define GRUPO "224.0.1.1"
      int main(void) {
      int s;
      struct sockaddr_in srv;
      char buf;
      bzero(&srv, sizeof(srv));
      srv.sin_family = AF_INET;
      srv.sin_port = htons(PUERTO);
      if (inet_aton(GRUPO, &srv.sin_addr) < 0) {
      perror("inet_aton");
      return 1;
      }
      if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
      perror("socket");
      return 1;
      }
      while (fgets(buf, MAXBUF, stdin)) {
      if (sendto(s, buf, strlen(buf), 0,
      (struct sockaddr *)&srv, sizeof(srv)) < 0) {
      perror("recvfrom");
      } else {
      fprintf(stdout, "Enviado a %s: %s
      ", GRUPO, buf);
      }
      }
      }
      
        客户端程序
      
        下面的代码是客户端程序,它负责接收由服务程序发送到组播组中的信息,将收到的报文在标准
    输出设备中显示。程序中唯一与接收UDP报文过程不同是它设置了IP_ADD_MEMBERSHIP选项。
      
      #include 
      #include 
      #include 
      #include 
      #include 
      #define MAXBUF 256
      #define PUERTO 5000
      #define GRUPO "224.0.1.1"
      int main(void) {
      int s, n, r;
      struct sockaddr_in srv, cli;
      struct ip_mreq mreq;
      char buf;
      bzero(&srv, sizeof(srv));
      srv.sin_family = AF_INET;
      srv.sin_port = htons(PUERTO);
      if (inet_aton(GRUPO, &srv.sin_addr) < 0) {
      perror("inet_aton");
      return 1;
      }
      if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
      perror("socket");
      return 1;
      }
      if (bind(s, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
      perror("bind");
      return 1;
      }
      if (inet_aton(GRUPO, &mreq.imr_multiaddr) < 0) {
      perror("inet_aton");
      return 1;
      }
      mreq.imr_interface.s_addr = htonl(INADDR_ANY);
      if (setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))
      < 0) {
      perror("setsockopt");
      return 1;
      }
      n = sizeof(cli);
      while (1) {
      if ((r = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *)
      &cli, &n)) < 0) {
      perror("recvfrom");
      } else {
      buf = 0;
      fprintf(stdout, "Mensaje desde %s: %s
      ",
      inet_ntoa(cli.sin_addr), buf);
      }
      }
      }
      
        六、内核与组播
      
        在上面的例子中我们看到:如果一个进程要加入到组播组中,就要使用setsockopt()函数在IP
    层设置IP_ADD_MEMBERSHIP。 
      
      在/usr/src/linux/net/ipv4/ip_sockglue.c文件中可以找见该函数的源代码。 其中设置IP_ADD_MEMBERSHIP
    和IP_DROP_MEMBERSHIP的部分代码如下:
      
      struct ip_mreqn mreq;
      if (optlen < sizeof(struct ip_mreq))
      return -EINVAL;
      if (optlen >= sizeof(struct ip_mreqn)) {
      if(copy_from_user(&mreq,optval,sizeof(mreq)))
      return -EFAULT;
      } else {
      memset(&mreq, 0, sizeof(mreq));
      if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
      return -EFAULT;
      }
      if (optname == IP_ADD_MEMBERSHIP)
      return ip_mc_join_group(sk,&mreq);
      else
      return ip_mc_leave_group(sk,&mreq);
      
        程序一开始先检查输入参数ip_mreq结构的长度是否正确,并将其从用户区复制到内核区。在得到参数
    的值后,接着调用ip_mc_join_group()加入到 组播组 或调用ip_mc_leave_group()退出组播组。
      
      在/usr/src/linux/net/ipv4/igmp.c中可以找到这些函数的代码。加入组播组的源程序代码如下:
      
      int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
      {
      int err;
      u32 addr = imr->imr_multiaddr.s_addr;
      struct ip_mc_socklist, *iml, *i;
      struct in_device *in_dev;
      int count = 0;
      
        在开始部分用MULTICAST宏检查组的地址,确认其在保留的组播组地址范围内。只要检查IP地
    址的第一部分是不是224就可以确认地址是否有效:
      
      if (!MULTICAST(addr))
      return -EINVAL;
      rtnl_shlock();
      
        检查完组播地址后,接着就要设置网络接口了。如果不能通过接口索引获得网络接口(如在IPv6下)
    ,在这种情况下可以调用函数 ip_mc_find_dev()获取网络接口。在本文中不存在这个问题,因为我们的工作
    都是在IPv4下进行的。若地址的格式是INADDR_ANY,内核就依照路由表的定义,按照组地址在路由表中
    查找网络接口。
      
      if (!imr->imr_ifindex)
      in_dev = ip_mc_find_dev(imr);
      else
      in_dev = inetdev_by_index(imr->imr_ifindex);
      if (!in_dev) {
      iml = NULL;
      err = -ENODEV;
      goto done;
      }
      
        接着给ip_mc_socklist结构分配内存,然后比较套接字的每个组地址和接口。只要发现了一个匹配项就
    跳出该函数,因为有一个匹配项就可以了。若网络接口地址不是INADDR_ANY,相应的计数器值就要增加。
      
      iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml),
      GFP_KERNEL);
      err = -EADDRINUSE;
      for (i=sk->ip_mc_list; i; i=i->next) {
      if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
      /* New style additions are reference counted */
      if (imr->imr_address.s_addr == 0) {
      i->count ;
      err = 0;
      }
      goto done;
      }
      count ;
      }
      err = -ENOBUFS;
      if (iml == NULL' 'count >= sysctl_igmp_max_memberships)
      goto done;
      
        到这里,就可以用新创建的套接字与组播组建立链接了,这时还必须创建一个新的记录,记录下属于
    该套接字的组的列表。首先还是要预先分配内存,然后只要给相关结构中的几个字段赋值,就完成了这个操作:
      
      memcpy(&iml->multi,imr, sizeof(*imr));
      iml->next = sk->ip_mc_list;
      iml->count = 1;
      sk->ip_mc_list = iml;
      ip_mc_inc_group(in_dev,addr);
      iml = NULL;
      err = 0;
      done:
      rtnl_shunlock();
      if (iml)
      sock_kfree_s(sk, iml, sizeof(*iml));
      return err;
      }
      
        用函数ip_mc_leave_group()从一个组播组中退出,它的工作过程比前面的函数要来得简单。首先在
    套接字记录中查找组播组及接口地址,找到后,将调用这个接口地址的进程数的值递减,若该值为0,就删除
    该计数器,因为与组播组相关的进程至少要有一个。
      
      int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
      {
      struct ip_mc_socklist *iml, **imlp;
      for (imlp=&sk->ip_mc_list;(iml=*imlp)!=NULL; imlp=&iml->next) {
      if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr
       && iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
       (!imr->imr_ifindex' 'iml->multi.imr_ifindex==imr->imr_ifindex)) {
      struct in_device *in_dev;
      if (--iml->count)
      return 0;
      *imlp = iml->next;
      synchronize_bh();
      in_dev = inetdev_by_index(iml->multi.imr_ifindex);
      if (in_dev)
      ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
      sock_kfree_s(sk, iml, sizeof(*iml));
      return 0;
      }
      }
      return -EADDRNOTAVAIL;
      }
    展开全文
  • 组播(多播)的C程序实战

    千次阅读 2016-04-14 14:59:23
     加入组播的接收程序为: [cpp]   view plain   copy   #include    #include    #include    #pragma comment(lib, "ws2_32.lib")     ...

    from:http://blog.csdn.net/stpeace/article/details/44657671


    每个人都有不同的认知规律和习惯, 有的人喜欢搞一套严密的大理论, 论述起来滔滔不绝, 不管自己懂不懂, 反正读者/听者是没搞懂。 有的人喜欢从实践出发, 没看到代码, 不运行一下, 不看到结果, 就不太舒服。 我感觉, 我偏向后者, 必须有一个结果作用于我, 我才有感触。

           比如初学C语言, 我也很不喜欢老师总在讲什么原码, 反码和补码, 讲什么 32767 + 1 为什么等于-32768, 讲什么m.nf%控制输出的具体含义.  我更喜欢搞个阶乘运行一下来玩玩, 搞个简单的界面问答系统感知一下, 搞个排序或者汉诺塔什么的玩玩。 等玩到一定程度, 再回去学那些略显枯燥的无聊知识。

           好吧, 不扯了, 毕竟每个人的学习习惯不一样。 


           在本文中, 我就不大讲特讲组播了,  至于组播概念和原理, 度娘谷哥会告诉你, 请自学去, 微笑其实也很简单微笑。当然, 如果后续有必要涉及, 我也会介绍一下。 现在仅仅来实战一下组播。


          (有必要说明一下, 下面两个程序必须运行在相同的机器上才有效)


           发送程序为:

    [cpp]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. #include <stdio.h>  
    2. #include <winsock2.h>  
    3. #include <ws2tcpip.h>  
    4. #pragma comment(lib, "ws2_32.lib")  
    5.   
    6. int main()  
    7. {  
    8.     WSADATA wsaData;  
    9.     WSAStartup(MAKEWORD(2, 2), &wsaData);     
    10.   
    11.     SOCKET sock = socket(AF_INET,SOCK_DGRAM,0);  
    12.       
    13.     int n = 0; // 注意, 这个n值很重要, 下面我会讲到  
    14.     setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&n, sizeof(n));  
    15.       
    16.     sockaddr_in addr;  
    17.     addr.sin_addr.S_un.S_addr = inet_addr("234.2.2.2");  
    18.     addr.sin_family = AF_INET;  
    19.     addr.sin_port = htons(8888);  
    20.       
    21.     static int i = 0;  
    22.     while(1)  
    23.     {     
    24.         char buf[100] = {0};  
    25.         sprintf(buf, "blablablabla:%d", i++);  
    26.         sendto(sock, buf, strlen(buf) + 1, 0, (sockaddr*)&addr, sizeof(sockaddr));  
    27.         Sleep(500);   
    28.     }  
    29.   
    30.     closesocket(sock);  
    31.     WSACleanup();  
    32.   
    33.     return 0;  
    34. }  
           运行它。


           加入组播的接收程序为:

    [cpp]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. #include <stdio.h>  
    2. #include <winsock2.h>  
    3. #include <ws2tcpip.h>  
    4. #pragma comment(lib, "ws2_32.lib")  
    5.   
    6. int main()  
    7. {  
    8.     WSADATA wsaData;  
    9.     WSAStartup(MAKEWORD(2, 2), &wsaData);  
    10.       
    11.     SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);  
    12.   
    13.     sockaddr_in addr;  
    14.     addr.sin_family = AF_INET;  
    15.     addr.sin_addr.S_un.S_addr = INADDR_ANY;  
    16.     addr.sin_port = htons(8888);  
    17.       
    18.     bind(sock, (sockaddr*)&addr, sizeof(addr));  
    19.       
    20.     // 加入组播啦  
    21.     ip_mreq multiCast;  
    22.     multiCast.imr_interface.S_un.S_addr = INADDR_ANY;  
    23.     multiCast.imr_multiaddr.S_un.S_addr = inet_addr("234.2.2.2");  
    24.     setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multiCast, sizeof(multiCast));  
    25.       
    26.     int len = sizeof(sockaddr);  
    27.     while(true)  
    28.     {     
    29.         char buf[100] = {0};  
    30.         recvfrom(sock, buf, sizeof(buf) - 1, 0, (sockaddr*)&addr, &len);  
    31.         printf("%s\n",buf);  
    32.     }  
    33.       
    34.     closesocket(sock);  
    35.     WSACleanup();  
    36.   
    37.     return 0;  
    38. }  
          运行它。


          结果如下(两个进程通信了):


           上面我们说过, 如上两个程序必须运行在同一电脑上才有效, 那要运行在不同的电脑上, 该怎么办呢? 我们注意到, 在发送程序中有int n = 0; 这个n为零表示发送端和接收端必须位于同一机器上, 如果n为1, 那么发送端和接收端就可以位于不同机器上了, 只需要他们位于同一个子网内即可。 实战过程如下: 假设pc0, pc1, pc2, pc3...在同一个子网内,将发送端的n改为 1且, 运行在pc0上, 然后在pc1, pc2, pc3...上运行接收端, 可以发现, pc1, pc2, pc3都接收到了组播消息, 与我们预期的结果吻合。

    展开全文
  • 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包。 .
  • 1、支持双向收发的组播,仅供参看,若有错误,请指出;...2、带一个网卡接口的PC1跑send_recv, 带两个网口的PC跑recv_send程序,PC1发送两个组播数据分别给PC2的两个网卡收,PC2的第一个网卡发送组播数据给PC1收;

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 126,279
精华内容 50,511
关键字:

组播程序