网络编程 | UDP组播通信

1、什么是组播

在上一篇博客中,对UDP的广播通信进行了由浅入深的总结梳理,本文继续对UDP的知识体系进行探讨,旨在将UDP的组播通信由浅入深的讲解清楚。

组播是介于单播与广播之间,在一个局域网内,将某些主机添加到组中,并设置一个组地址。将数据发送到组播地址时,加入到该组的所有主机都能接收到数据。

组播、单播和广播都是报文传输的一种方式。

单播是主机间一对一的通信模式,设备只会将数据发送到唯一指定的接收者。

广播是主机间一对所有的通信模式,设备会将数据发送到网络中的所有可能的接收者。设备简单地将它收到的任何广播报文都复制并转发到除该报文到达的接口外的每个接口。广播处理流程简单,不用选择路径。

组播是主机间一对多的通信模式, 组播是一种允许一个或多个组播源发送同一报文到多个接收者的技术。类似于生活中较为常见的群聊功能,在群内的所有群员,都可以在群内发送消息给群友,也可以接收到来自任意群友的消息。

为了帮助读者更进一步的理解UDP的单播、组播和广播功能,绘制了如下所示的总结图。

2、IP地址分类

因为组播通信需要设置IP地址,且必须是D类IP地址,但是考虑到每位博客读者的基础不同,知识贮备有较大差距,所以在这一小部分,简单讲解一下IP地址的分类及用途,在后面程序设计中需要用到这个E类IP地址。

IP地址 = 网络号 + 主机号

**网络号:**指的是不同的网络

**主机号:**指的是同一个网段下用来识别不同的主机。那也就是说,主机号所占的位数越多,在该网段下的主机数越多。

A类地址:保留给政府机构使用

A类IP地址就由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是"0"

A类地址范围 1.0.0.1 - 126.255.255.254

B类地址:分配给中等规模的公司

B类IP地址就由2字节的网络地址和2字节主机地址组成,网络地址的最高位必须是"10"。

B类地址范围 128.0.0.1 - 191.255.255.254

C类地址:分配给任何需要的人

C类IP地址就由3字节的网络地址和1字节主机地址组成,网络地址的最高位必须是"110"。

C类地址范围 192.0.0.1 - 223.255.255.254

D类地址:用于组播

组播地址不同于单播地址,它并不属于特定某个主机,而是属于一组主机。一个组播地址表示一个群组,需要接收组播报文的接收者都加入这个群组。

D类地址范围 224.0.0.1 - 239.255.255.254

E类地址:用于实验

E类地址范围 240.0.0.1 - 255.255.255.254

特殊地址:每一个字节都为0的地址("0.0.0.0")对应于当前主机; INADDR_ANY --->代表当前主机所有的地址

127.0.0.1 回环地址 ---> 在当前主机内部自动形成闭环的网络 ---> 主要用于主机内部不同的应用程序间通信

如果已经确定当前客户端 和 服务器 都是在同一台主机上运行,那么可以使用本地回环地址

3、组播的特点及应用

(1)、特点

  • **效率高:**组播传输的数据包只需要经过一次发送操作,就可以同时传输到多个接收者,可以有效地降低网络传输的负载。
  • **可扩展性:**组播支持动态加入和退出组播组,能够自适应地处理组播成员的加入和离开。
  • **IP组播地址:**在IPv4中,组播使用D类地址(224.0.0.0至239.255.255.255),而在IPv6中,组播地址以ff00::/8开头。这些特殊地址用于标识不同的组播组,使路由器能够识别并正确转发组播报文。
  • **路由协议的支持:**为有效管理组播流量,互联网工程任务组(IETF)定义了几种专门的组播路由协议,如PIM(Protocol Independent Multicast)、DVMRP(Distance Vector Multicast Routing Protocol)等,它们帮助确定最有效的路径来分发组播数据。
  • **不可靠性:**与普通的UDP一样,组播只提供不可靠的数据传输服务。如果某个接收者没有接收到数据包,发送者不会得到任何提示或反馈信息。

(2)、应用

  • **多媒体流媒体:**在局域网或广域网上传输音视频流,快速向多个接收者发送相同的视频和音频数据。
  • **分布式应用的数据分发:**实现高效的数据分发,例如在大型集群环境下广播服务的状态信息。
  • **网络游戏:**用于多人联机游戏,使多个玩家能够同时收到相同的游戏状态和动作。
  • **网络广播:**向多个设备广播事件和消息,例如路由器向所有连接的设备发送网络配置信息。
  • 实时数据更新:用于实时的数据更新,例如在金融行业订阅财经数据的实时更新。

4、组播的通信流程

组播通信的发送端程序与普通UDP发送端创建流程几乎一致,区别在于,其目标IP地址需要换成D类IP地址。

组播通信的接收端则是需要在普通UDP接收端程序的基础上,增加一步"加群"的操作,即将当前的IP地址设置到组播地址中。

5、组播通信的程序

(1)、组播发送端步骤

①、创建UDP套接字

cpp 复制代码
int socketfd = socket(AF_INET,SOCK_DGRAM,0);

②、发送数据,往组播地址(224.0.0.10 )里面发送数据

cpp 复制代码
struct sockaddr_in sendAddr;//IPV4地址结构体变量 
sendAddr.sin_family = AF_INET; 
sendAddr.sin_port = htons(GROUPPORT); 
sendAddr.sin_addr.s_addr = inet_addr(GROUPADDR);//一定是组播地址 
sendto(socketfd, buf, strlen(buf), 0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));

③、关闭套接字

cpp 复制代码
close(socketfd);

(2)、组播接收端步骤

①、创建UDP套接字

cpp 复制代码
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);

②、设置组播ip(初始化 组播结构体)

cpp 复制代码
函数原型:
    #include <arpa/inet.h>
    int inet_pton(int af, const char *src, void *dst);//函数原型
函数参数:
    af: 你要选择哪一种协议族  IPV4 --》AF_INET  还是 IPV6--》AF_INET6
    src: 本地IP地址
    dst:将本地IP地址转为网络IP地址存储到这里
函数功能:
    将本地IP地址转为网络IP地址
函数返回值:
    成功返回0, 失败返回-1

//配置方法
struct ip_mreq vmreq;
inet_pton(AF_INET, "224.0.0.10", &vmreq.imr_multiaddr); // 组播地址
inet_pton(AF_INET, "192.168.63.2", &vmreq.imr_interface); // 需要添加到组的ip

③、加入组播属性(设置套接字 可以接收组播信息)

cpp 复制代码
setsockopt(socketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &vmreq, sizeof(vmreq));

④、绑定地址

cpp 复制代码
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(INADDR_ANY)  代表 主机所有的地址
bind(socketfd, (struct sockaddr *)&saddr, sizeof(saddr));

⑤、接收数据

cpp 复制代码
recvfrom(......)

(3)、组播实现程序

①、组播发送端

cpp 复制代码
#include<stdio.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define GROUPADDR   "224.0.0.10" //组播地址
#define GROUPPORT   10000

int main()
{
    printf("组播发送端.....\n");
        
    //1、创建UDP数据报套接字
    int socketfd = socket(AF_INET,SOCK_DGRAM,0);
    if(socketfd == -1)
    {
        perror("socket error");
        return -1;
    }
    
    //2、发送数据,往组播地址(224.0.0.10 )里面发送数据
    struct sockaddr_in sendAddr;//IPV4地址结构体变量
    sendAddr.sin_family = AF_INET;
    sendAddr.sin_port = htons(GROUPPORT);
    sendAddr.sin_addr.s_addr = inet_addr(GROUPADDR);//一定是组播地址
    
    while(1)
    {
        char buf[1024]={0};
        printf("data:");
        scanf("%s",buf); 
        sendto(socketfd,buf,strlen(buf),0,( struct sockaddr *)&sendAddr,sizeof(sendAddr));
    }
    
    //3、关闭 
    close(socketfd);
    
    return 0;
}

②、组播接收端

cpp 复制代码
#include<stdio.h>
#include <sys/types.h>     
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define OWNADDR "192.168.112.109" //接收端的IP地址   当前ubuntu的IP地址
#define GROUPADDR   "224.0.0.10"  //组播地址
#define GROUPPORT   10000

int main()
{
    printf("组播接收端.....\n");
    //1、创建UDP套接字
    int socketfd = socket(AF_INET,SOCK_DGRAM,0);
    if(socketfd == -1)
    {
        perror("socket error");
        return -1;
    }
    
    //2、定义组播结构体
    struct ip_mreq vmreq;
    
    //3、设置组播ip(初始化 组播结构体)
    inet_pton(AF_INET,GROUPADDR,&vmreq.imr_multiaddr); // 组播地址
    inet_pton(AF_INET,OWNADDR,&vmreq.imr_interface); // 需要添加到组的ip
    
    //4)加入组播属性(也就是设置这个套接字 可以接收组播信息)
    setsockopt(socketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&vmreq,sizeof(vmreq));
    
    //5)绑定地址
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(GROUPPORT);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(INADDR_ANY)  代表 主机所有的地址
    bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
    
    //6)接收数据
    struct sockaddr_in otherAddr;
    int len = sizeof(struct sockaddr_in);
    
    while(1)
    {
        char buf[1024]={0};
        recvfrom(socketfd,buf,sizeof(buf),0, (struct sockaddr *)&otherAddr,&len);
        printf("来自 %s:%u  recv:%s\n",inet_ntoa(otherAddr.sin_addr),ntohs(otherAddr.sin_port),buf);
    }
    
    //关闭 
    close(socketfd);
    
    return 0;
}
相关推荐
此般纯净11 分钟前
Ubuntu、Windows系统网络设置(ping通内外网)
linux·开发语言
汤姆和杰瑞在瑞士吃糯米粑粑32 分钟前
【C++学习篇】滑动窗口--结合例题讲解思路
开发语言·数据结构·c++·算法·散列表
小王努力学编程37 分钟前
【C++篇】红黑树封装 实现map和set
java·开发语言·c++
我们的五年1 小时前
【Linux课程学习】:锁封装(Mutex)线程封装(Thread),this指针
linux·服务器·c语言·c++·学习
字节高级特工1 小时前
【优选算法】3----快乐数
c++·算法
奇树谦1 小时前
C++|开源日志库log4cpp和glog
开发语言·c++·开源
王子良.1 小时前
Python 的 WebSocket 实现详解
网络·websocket·网络协议
GIS小小研究僧2 小时前
数据库基础知识:理论、E-R图、事务、原则
数据库·ubuntu·postgresql
刘争Stanley2 小时前
Android系统开发(六):从Linux到Android:模块化开发,GKI内核的硬核科普
android·linux·运维·内核·镜像·gki·kmi
dot to one2 小时前
C++入门 详细版
c语言·开发语言·汇编·c++