首页 > 其他分享 >UDP组播接收端解析

UDP组播接收端解析

时间:2023-09-20 10:33:43浏览次数:34  
标签:UDP 组播 struct mc 接收端 ip mreq socket

https://blog.51cto.com/u_4042309/3602677

网络中的一台主机如果希望能够接收到来自网络中其它主机发往某一个组播组的数据报,那么这么主机必须先加入该组播组,然后就可以从组地址接收数据包。在广域网中,还涉及到路由器支持组播路由等,但本文希望以一个最为简单的例子解释清楚协议栈关于组播的一个最为简单明了的工作过程,甚至,我们不希望涉及到 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。
    这个操作是在网络层上的一个选项,所以级别是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已经可以接收组播数据了,关于组播数据如何接收,下回分解。

       

标签:UDP,组播,struct,mc,接收端,ip,mreq,socket
From: https://www.cnblogs.com/tomato-haha/p/17716671.html

相关文章

  • cloudpickle pickle 扩展包
    pickle是python的序列化包,但是默认pickle不能进行lambda的处理,cloudpickle对于pickle进行了一些扩展,可以更好的支持集群节点之间的共享以及计算,同时apachespark的pyspark也集成了此功能,只是是自己fork的完整代码参考使用dump.py importcloudpickle,pic......
  • 1.OSI、TCP、UDP
    1.OSI和TCP/IP网络分层模型(1)OSI七层模型是什么?每一层的作用是什么?(2)TCP/IP四层模型是什么?每一层的作用是什么?网络接口层、网络层(为分组交换网上的不同主机提供通信服务)、传输层(提供应用进程之间的端到端逻辑通信)、应用层(为用户提供应用程序)(3)为什么网络要分层?解......
  • UDP编程
    UDP编程1.字节序1.1字节序概述字节序概念:是指多字节数据的存储顺序分类:小端格式:将低位字节数据存储在低地址大端格式:将高位字节数据存储在低地址大端:高字节数据存放低地址小端:低字节数据存放低地址1.2确认主机的字节序编写一个共用体,内存大小为2个字节。为short赋......
  • 在springboot中处理UDP流
    配置: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId></dependency><dependency><groupId>org.springframework.integration</gr......
  • udp通信中变长结构体的存在意义?
    在UDP通信中,使用变长结构体(又称为灵活数组成员)的存在意义是为了在不固定大小的数据报中传输可变长度的数据。在你的示例中,结构体定义了一个变长结构体,其中包含一个整型变量`size`和一个大小为0的字符数组`data`。通过将字符数组大小设置为0,你可以根据实际数据的长度在运行时分......
  • 关于TCP 和 UDP 的 Socket 调用
    在网络层,Socket函数需要指定到底是IPv4还是IPv6,分别对应设置为AF_INET和AF_INET6。另外,还要指定到底是TCP还是UDP。TCP协议是基于数据流的,所以设置为SOCK_STREAM,而UDP是基于数据报的,因而设置为SOCK_DGRAM。TCP的服务端要先监听一个端口,一般是先调用bind函数,给这......
  • Java网路编程____UDP协议Socket客户端服务器聊天室列子
    1.UPD服务端定义数据Socket和注册外放的端口一直做true循环读取数据包Packet里的数据datagramPacket.getData()转换为String字符串读取 packagecom.frame.base.UDP;importjava.net.DatagramPacket;importjava.net.DatagramSocket;/***@authorAdministrator*UDP_So......
  • 24UDP协议/操作系统发展
    作业#作业内容:实现上传和下载电影写了一个,另外一个反过来就可以(代码参考day24代码)#思考1.上传的电影如何判断是否重复小白思想:校验电影名称是否存在正确思想:校验电影的md5值(核心是内容不是名称)2.上传的电影如何判断是否有毒提前对电影内容加......
  • 10 UDP 聊天实现
    packageInternet;importjava.io.BufferedReader;importjava.io.InputStreamReader;importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetSocketAddress;//UDP实现聊天,两边都是发信方,也是收信方publicclassTest10_UDP_Me{pub......
  • 9 UDP 消息发送
    没有客户端和服务端这一说法packageInternet;importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetAddress;importjava.net.SocketException;//UDP:类似发短信//发送端publicclassTest09_UDP_User1{publicstaticvoidma......