IP多播

                      1   多播地址                    

IP多播地址采用D类IP地址确定多播的组,地址范围是224.0.0.0 到 239.255.255.255.

                       2 组管理协议(IGMP)              

两个多播节点之间的所有路由器必须支持IGMP协议

任何没有开启IGMP的路由器仅简单的丢弃接收到的多播数据

                      3   使用IP多播                        

1     加入离开组  

     IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP

下面的代码展示了如何 加入组:

    ip_mreq    mcast;
    mcast.imr_interface.S_un.S_addr = INADDR_ANY;
    mcast.imr_multiaddr.S_un.S_addr = ::inet_addr("234.5.6.7"); // 多播地址为234.5.6.7 
  ::setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));

下面的代码显示了如何 退出组

    ip_mreq    mcast;
    mcast.imr_interface.S_un.S_addr = INADDR_ANY;
    mcast.imr_multiaddr.S_un.S_addr = ::inet_addr("234.5.6.7"); // 多播地址为234.5.6.7 
  ::setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mcast, sizeof(mcast));

2     接收多播数据    

主机在接收IP多播数据之前,必须成为IP多播组的成员。为了接收发送到特定端口的多播封包,有必要绑定到那个本地端口,而不是显示的指定本地地址

如果套接字使用SO_REUSEADDR选项,就可以不止一个进程可以绑定到UDP端口

如下代码所示:

    BOOL bReuse = TRUE;
    ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));

如此一来,每个来到这个共享端口的多播或者广播UDP封包都会发送给所绑定此端口的套接字

下面是接收多播封包的代码:

void main()
{
    SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0);

    // 允许其它进程使用绑定的地址
    BOOL bReuse = TRUE;
    ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));


    // 绑定到4567端口
    sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_port = ::ntohs(4567);
    si.sin_addr.S_un.S_addr = INADDR_ANY;
    ::bind(s, (sockaddr*)&si, sizeof(si));
    
    // 加入多播组
    ip_mreq    mcast;
    mcast.imr_interface.S_un.S_addr = INADDR_ANY;
    mcast.imr_multiaddr.S_un.S_addr = ::inet_addr("192.168.0.1");  // 多播地址为234.5.6.7
    ::setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));


    // 接收多播组数据
    printf(" 开始接收多播组上的数据... \n");
    char buf[1280];
    int nAddrLen = sizeof(si);
    while(TRUE)
    {
        int nRet = ::recvfrom(s, buf, strlen(buf), 0, (sockaddr*)&si, &nAddrLen);
        if(nRet != SOCKET_ERROR)
        {
            buf[nRet] = '\0';
            printf(buf);
        }
        else
        {
            int n = ::WSAGetLastError();
            break;
        }
    }
}

3   带源地址的IP多播      

带源地址的IP多播允许加入组时,指定要接收哪些成员的数据

1 包含方式:指定N个有效的源地址,套接字仅接收来自这些源地址的数据

2 排除方式:指定N个有效的源地址,套接字将接受这些源地址之外的数据

    SOCKET    s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    // 本地接口
    SOCKADDR_IN    localif;
    localif.sin_family = AF_INET;
    localif.sin_port   = htons(5150);
    localif.sin_addr.s_addr = htonl(INADDR_ANY);
    ::bind(s, (SOCKADDR *)&localif, sizeof(localif));
    
    // 设置ip_mreq_source结构
    struct ip_mreq_source mreqsrc;
    mreqsrc.imr_interface.s_addr = inet_addr("192.168.0.46");
    mreqsrc.imr_multiaddr.s_addr = inet_addr("234.5.6.7");
    
    
    // 添加源地址218.12.255.113
    mreqsrc.imr_sourceaddr.s_addr = inet_addr("218.12.255.113");
    ::setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&mreqsrc, sizeof(mreqsrc));
    // 添加源地址
    mreqsrc.imr_sourceaddr.s_addr = inet_addr("218.12.174.222");
    ::setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&mreqsrc, sizeof(mreqsrc));

移除地址可以使用IP_DROP_MEMBERSHIP选项

全部试验源码:

sender.cpp:

///////////////////////////////////
// sender.cpp文件

#include "initsock.h"
#include "stdio.h"
#include <windows.h>

CInitSock theSock;

void main()
{
    SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0);
    // 有效SO_BROADCAST选项
    BOOL bBroadcast = TRUE;
    ::setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL));

    // 设置广播地址,这里的广播端口号(电台)是4567
    SOCKADDR_IN bcast;
    bcast.sin_family = AF_INET;
    bcast.sin_addr.s_addr =  ::inet_addr("255.255.255.255");
    bcast.sin_port = htons(4567);

    // 发送广播
    char sz[] = "this is xingoo 123. \r\n";
    while(TRUE)
    {
        ::sendto(s, sz, strlen(sz), 0, (sockaddr*)&bcast, sizeof(bcast));
        ::Sleep(1000);
    }
}

recv.cpp:

#include "Initsock.h"

#include <stdio.h>
#include <windows.h>
#include <Ws2tcpip.h>



// 初始化Winsock库
CInitSock theSock;

void main()
{
    SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0);

    // 允许其它进程使用绑定的地址
    BOOL bReuse = TRUE;
    ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));


    // 绑定到4567端口
    sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_port = ::ntohs(4567);
    si.sin_addr.S_un.S_addr = INADDR_ANY;
    ::bind(s, (sockaddr*)&si, sizeof(si));
    
    // 加入多播组
    ip_mreq    mcast;
    mcast.imr_interface.S_un.S_addr = INADDR_ANY;
    mcast.imr_multiaddr.S_un.S_addr = ::inet_addr("192.168.0.1");  // 多播地址为234.5.6.7
    ::setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));


    // 接收多播组数据
    printf(" 开始接收多播组上的数据... \n");
    char buf[1280];
    int nAddrLen = sizeof(si);
    while(TRUE)
    {
        int nRet = ::recvfrom(s, buf, strlen(buf), 0, (sockaddr*)&si, &nAddrLen);
        if(nRet != SOCKET_ERROR)
        {
            buf[nRet] = '\0';
            printf(buf);
        }
        else
        {
            int n = ::WSAGetLastError();
            break;
        }
    }
}

运行结果:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 二叉堆

    容易证明: 一棵高为h的完全二叉树有2^h 到 2^(h+1)-1个结点。 这就意味着,完全二叉树的高是[logN] 特点: 任意位置i: 左儿子在位置2i上,...

    用户1154259
  • 快速排序

    快速排序时间复杂度为O(nlogn),由于是在原数组上面利用替换来实现,因此不需要额外的存储空间。 算法思想:   通过设置一个岗哨,每次跟这个岗哨进行比较,比...

    用户1154259
  • VS报错:DEBUG Assertion Failed!

    使用vs2010时,遇到如下错误 ? 然后点击继续后: ? 点击终止: ? 观察变量: ? 根据提示发现,有可能是断点问题,于是猜想可能是指针的错误。 goog...

    用户1154259
  • 业界第一个真正意义上开源100 Gbps NIC Corundum介绍

    来源:内容由「网络交换FPGA」编译自「FCCM2020」,谢谢。FCCM2020在5月4日开始线上举行,对外免费。我们有幸聆听了其中一个有关100G开源NIC...

    网络交换FPGA
  • Netty ChannelHandler与ChannelPipeline源码解读

      ChannelHandler基本上是我们第一次接触Netty就会碰到的对象,我们自定义的各种ChannelHandler主要用于处理我们系统的各种业务逻辑,...

    良辰美景TT
  • Android获取栈顶的应用包名方法

    有时候我们需要判断栈顶的应用是否是我们的应用,于是获取栈顶的应用包名的需求就出现了。

    砸漏
  • java api使用ElastichSearch指南

    比如想要addr是beijing的,同时必须满足条件:name是 paxi,或者,phoneNumber是 1234567890

    爬蜥
  • 韩国如何发展成为区域性的科技创业枢纽

    韩国政府投资30亿美元致力于区域性科技创业枢纽的努力获得了回报,吸引了像谷歌和脸谱,以及风险投资和孵化器入驻首尔。 韩国拥有世界上最高的宽带普及率达到97%,在...

    点滴科技资讯
  • 因为 Django ORM update,我今天差点「从删库到跑路」

    经过周末两天回血,今天早早来到公司,准备把上周遗留的 BUG 修了,然后再多写几个 BUG。

    AlwaysBeta
  • golang实现ping命令

    zolo® golang实现ping命令 // Copyright 2009 The Go Authors. All rights reserved. // ...

    李海彬

扫码关注云+社区

领取腾讯云代金券