转:https://blog.csdn.net/xbgprogrammer/article/details/51383202

网络中的一台主机如果希望能够接收到来自网络中其它主机发往某一个组播组的数据报,那么这么主机必须先加入该组播组,然后就可以从组地址接收数据包。在广域网中,还涉及到路由器支持组播路由等,但本文希望以一个最为简单的例子解释清楚协议栈关于组播的一个最为简单明了的工作过程,甚至,我们不希望涉及到IGMP包。
    我们先从一个组播客户端的应用程序入手来解析组播的工作过程:

    #include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <string.h>#include "my_inet.h"#include <arpa/inet.h>#define MAXBUF 256#define PUERTO 5000#define GRUPO "224.0.1.1"int main(void){int fd, n, r;struct sockaddr_in srv, cli;struct ip_mreq mreq;char buf[MAXBUF];memset( &srv, 0, sizeof(struct sockaddr_in) );memset( &cli, 0, sizeof(struct sockaddr_in) );memset( &mreq, 0, sizeof(struct ip_mreq) );srv.sin_family = MY_AF_INET;srv.sin_port = htons(PUERTO);if( inet_aton(GRUPO, &srv.sin_addr ) < 0 ) {perror("inet_aton");return -1;}if( (fd = socket( MY_AF_INET, SOCK_DGRAM, MY_IPPROTO_UDP) ) < 0 ){perror("socket");return -1;}if( bind(fd, (struct sockaddr *)&srv, sizeof(srv)) < 0 ){perror("bind");return -1;}if (inet_aton(GRUPO, &mreq.imr_multiaddr) < 0) {perror("inet_aton");return -1;}inet_aton( "172.16.48.2", &(mreq.imr_interface) );if( setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq)) < 0 ){perror("setsockopt");return -1;}n = sizeof(cli);while(1){if( (r = recvfrom(fd, buf, MAXBUF, 0, (struct sockaddr *)&cli, (socklen_t*)&n)) < 0 ){perror("recvfrom");}else{buf[r] = 0;fprintf(stdout, "Mensaje desde %s: %s", inet_ntoa(cli.sin_addr), buf);}}}

这是一个非常简单的组播客户端,它指定从组播组224.0.1.1的5000端口读数据,并显示在终端上,下面我们通过分析该程序来了解内核的工作过程。
    前面我们讲过,bind操作首先检查用户指定的端口是否可用,然后为socket的一些成员设置正确的值,并添加到哈希表myudp_hash中。然后,协议栈每次收到UDP数据,就会检查该数据报的源和目的地址,还有源和目的端口,在myudp_hash中找到匹配的socket,把该数据报放入该socket的接收队列,以备用户读取。在这个程序中,bind操作把socket绑定到地址224.0.0.1:5000上, 该操作产生的直接结果就是,对于socket本身,下列值受影响:
    struct inet_sock{
        .rcv_saddr = 224.0.0.1;
        .saddr = 0.0.0.0;
        .sport = 5000;
        .daddr = 0.0.0.0;
        .dport = 0;
    }
    这五个数据表示,该套接字在发送数据包时,本地使用端口5000,本地可以使用任意一个网络设备接口,发往的目的地址不指定。在接收数据时,只接收发往IP地址224.0.0.1的端口为5000的数据。
    程序中,紧接着bind有一个setsockopt操作,它的作用是将socket加入一个组播组,因为socket要接收组播地址224.0.0.1的数据,它就必须加入该组播组。结构体struct ip_mreq mreq是该操作的参数,下面是其定义:
    struct ip_mreq
    {
        struct in_addr imr_multiaddr;   // 组播组的IP地址。
        struct in_addr imr_interface;   // 本地某一网络设备接口的IP地址。
    };
    一台主机上可能有多块网卡,接入多个不同的子网,imr_interface参数就是指定一个特定的设备接口,告诉协议栈只想在这个设备所在的子网中加入某个组播组。有了这两个参数,协议栈就能知道:在哪个网络设备接口上加入哪个组播组。为了简单起见,我们的程序中直接写明了IP地址:在172.16.48.2所在的设备接口上加入组播组224.0.1.1。(louis:这个172.16.48.2的ip地址实际上是为了指明网卡IF)
    这个操作是在网络层上的一个选项,所以级别是SOL_IP,IP_ADD_MEMBERSHIP选项把用户传入的参数拷贝成了struct ip_mreqn结构体:
    struct ip_mreqn
    {
        struct in_addr  imr_multiaddr;
        struct in_addr  imr_address;
        int             imr_ifindex;
    };
    多了一个输入接口的索引,暂时被拷贝成零。
    该操作最终引发内核函数myip_mc_join_group执行加入组播组的操作。首先检查imr_multiaddr是否为合法的组播地址,然后根据imr_interface的值找到对应的struct in_device结构。接下来就要为socket加入到组播组了,在inet_sock的结构体中有一个成员mc_list,它是一个结构体struct ip_mc_socklist的链表,每一个节点代表socket当前正加入的一个组播组,该链表是有上限限制的,缺省值为IP_MAX_MEMBERSHIPS(20),也就是说一个socket最多允许同时加入20个组播组。下面是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;
    };
    struct ip_sf_socklist
    {
        unsigned int    sl_max;
        unsigned int    sl_count;
        __u32           sl_addr[0];
    };
    除了multi成员,它还有一个源过滤机制。如果我们新添加的struct ip_mreqn已经存在于这个链表中(表示socket早就加入这个组播组了),那么不做任何事情,否则,创建一个新的struct ip_mc_socklist:
    struct ip_mc_socklist
    {
        .next = inet->mc_list;      //新节点放到链表头。
        .multi = 传入的参数;        //这是关键的组信息。
        .sfmode = MCAST_EXCLUDE;    //过滤掉sflist中的所有源。
        .sflist = NULL;             //没有源需要过滤。
    };
    最后,调用myip_mc_inc_group函数在struct in_device和struct net_device的mc_list链表中都添上相应的组播组节点,关于这部分的细节可以在前一篇文章《初识组播2》中找到。不再重复。
    到此为止,我们完成了最为简单的加入组播组的操作,对于同一子网内的情况,socket已经可以接收组播数据了,关于组播数据如何接收,下回分解。

加入一个组播组过程分析相关推荐

  1. 第十二章:组播 — 组播路由协议

    和单播路由协议类似,组播路由器在进行数据转发时也要依赖于组播路由协议.组播路由器依靠自己维护的组播路由表,对数据报文进行转发. 组播路由协议的主要作用: 确定朝向组播源的接口,这个接口被称为上游接口, ...

  2. 组播源不一定属于组播组,它向组播组发送数据,自己不一定是接收者。可以同时有多个...

    组播方式传输信息 综上所述,单播方式适合用户稀少的网络,而广播方式适合用户稠密的网络,当网络中需要某信息的用户量不确定时,单播和广播方式效率很低. IP组播技术的出现及时解决了这个问题.当网络中的某些 ...

  3. IP组播----组播基础 组播服务模型、组播地址

    一.简介 IPv4传输方式有三种:单播.组播.广播 单播:信息源为每个需要信息的主机都发送一份独立的报文 组播:信息源将保温发送到一个特定的组播IP地址,只有加入了这个组的主机才能接收 广播:信息源将 ...

  4. asio指定网卡加入组播组

    背景: rtsp客户端接收组播流,PC上存在多个网卡情况下,加入组播组时会选择默认的网卡加入,导致无法接收组播消息,即使抓包已经抓到组播的数据也接收不到. 上图中的IGMPv3消息如果不是由接收组播流 ...

  5. 验证组播的TTL阀值

    实验目的:验证组播的TTL阀值. 实验拓扑: 实验步骤: PC1,PC2均加入224.1.1.1这个组播组. R1: ip multicast-routing interface Serial1/1 ...

  6. 技术方案——可控组播

    --IPTV业务的承载风帆 一. 前言 进几年来,随着网络带宽和接入用户的迅猛增加,宽带业务运营商已经将关注的焦点逐渐由提高宽带用户数向提高户均营收(ARPU)值的目标转移.IPTV业务作为消除宽带用 ...

  7. 组播技术中IP地址到MAC地址的映射

    以太网组播MAC地址 以太网传输单播IP报文的时候,目的MAC地址使用的是接收者的MAC地址.但是在传输组播数据包时,其目的地不再是一个具体的接收者,而是一个成员不确定的组,所以要使用组播MAC地址. ...

  8. 组播路由协议基础——组播分发树

    背景 组播报文是由组播源产生并且发向一组接收者的,组播报文一旦进入组播网络后,组播网络设备(例如组播路由器等)负责拷贝及转发这些报文,直至报文到达组播接收者. 组播流量在组播网络中传递时,应该要考虑如 ...

  9. 07-IP组播配置指导

    1 组播概述 1.1  组播简介 作为一种与单播(Unicast)和广播(Broadcast)并列的通信方式,组播(Multicast)技术能够有效地解决单点发送.多点接收的问题,从而实现了网络中点到 ...

最新文章

  1. 北京发自动驾驶车辆考试大纲 难度堪比普通人考驾照
  2. 【正则表达式】之Possessive Quantifiers
  3. 语音跟踪:信号分解、锁相、鸡尾酒会效应、基于PR的信号分离
  4. 复古海报设计灵感配色,“一见即知”的感觉
  5. Windows2008之文件服务器资源管理器
  6. python内置函数每个执行一次
  7. 剑指Offer_16_合并两个排序的链表
  8. 使用Draw免费在线作图
  9. Python 珍藏函数超详解:随机抽样,分层抽样,系统抽样方法汇总
  10. 只有单号,用这个方法自动识别快递公司,快速查询出物流
  11. 洛谷-P1425-小鱼的游泳时间
  12. 每天坚持收小钱,能改命!
  13. proxmox PVE 安装 黑群晖
  14. Java反射机制的原理及作用
  15. html5 ins标签,HTML ins标签常用的用法有哪些?HTML ins常用属性的介绍
  16. 如何面试Java中级开发(16k)试题讲解和Java学习
  17. 谈今天的头条 台湾地震影响海底光纤
  18. 计算机专业英语博士就业情况,计算机专业博士期刊文章参考文献 计算机专业博士英语参考文献哪里找...
  19. 计算机语言指令数据用0,汇编语言-中国大学mooc-题库零氪
  20. 蛋白质对中可能相互作用域的数目计算

热门文章

  1. NSA组网下2G/3G/4G/5G系统协同策略的研究
  2. 数字电子技术课程设计——盲人报时钟
  3. LinuxTina动态调压调频配置与实现
  4. 如何理解奇偶校验位?
  5. 矩阵分析理论在实际工程中的应用_电气工程师谈电气火灾监控系统技术要点及其在地铁中的应用分析...
  6. 程序员跳槽的原因总结
  7. ooqzxdJeroboam v7.30 Bilingual 1CD瑰就
  8. RSS Can:使用 Golang Rod 解析浏览器中动态渲染的内容:(四)
  9. idea悄悄记录一下pojie
  10. 地表最全:60种数据可视化图表使用场景大全 !