socket之UDP组播(多播)

组播也可以称之为多播这也是 UDP 的特性之一。组播是主机间一对多的通讯模式,是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将一份报文发送到特定的组播地址,组播地址不同于单播地址,它并不属于特定某个主机,而是属于一组主机。一个组播地址表示一个群组,需要接收组播报文的接收者都加入这个群组。

广播只能在局域网访问内使用,组播既可以在局域网中使用,也可以用于广域网
在发送广播消息的时候,连接到局域网的客户端不管想不想都会接收到广播数据,组播可以控制发送端的消息能够被哪些接收端接收,更灵活和人性化。
广播使用的是广播地址,组播需要使用组播地址。
广播和组播属性默认都是关闭的,如果使用需要通过 setsockopt () 函数进行设置。
组播需要使用组播地址,在 IPv4 中它的范围从 224.0.0.0 到 239.255.255.255,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类

224.0.0.0 ~ 224.0.0.255: 局部链接多播地址:是为路由协议和其它用途保留的地址,
只能用于局域网中,路由器是不会转发的地址 224.0.0.0 不能用,是保留地址
224.0.1.0 ~ 224.0.1.255: 为用户可用的组播地址(临时组地址),可以用于 Internet 上的。
224.0.2.0 ~ 238.255.255.255: 用户可用的组播地址(临时组地址),全网范围内有效
239.0.0.0 ~ 239.255.255.255: 为本地管理组播地址,仅在特定的本地范围内有效

组播地址不属于任何服务器或个人,它有点类似一个微信群号,任何成员(组播源)往微信群(组播 IP)发送消息(组播数据),这个群里的成员(组播接收者)都会接收到此消息。

组播应用

点对多点应用
点对多点应用是指一个发送者,多个接收者的应用形式,这是最常见的多播应用形式。典型的应用包括:媒体广播、媒体推送、信息缓存、事件通知和状态监视等。

多点对点应用
多点对点应用是指多个发送者,一个接收者的应用形式。通常是双向请求响应应用,任何一端(多点或点)都有可能发起请求。典型应用包括:资源查找、数据收集、网络竞拍、信息询问等。

多点对多点应用
多点对多点应用是指多个发送者和多个接收者的

应用形式。通常,每个接收者可以接收多个发送者发送的数据,同时,每个发送者可以把数据发送给多个接收者。典型应用包括:多点会议、资源同步、并行处理、协同处理、远程学习、讨论组、分布式交互模拟(DIS)、多人游戏等

设置组播属性
如果使用组播进行数据的传输,不管是消息发送端还是接收端,都需要进行相关的属性设置,设置函数使用的是同一个,即:setsockopt(),函数原型如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

发送端
发送组播消息的一端需要设置组播属性,具体的设置方式如下:

/*
struct in_addr
{
    in_addr_t s_addr;	// unsigned int
};
*/

/*
函数:setsockopt
描述:配置发送端UDP组播属性
参数:
	sockfd:	用于 UDP 通信的套接字
	level:		套接字级别,设置组播属性需要将该参数指定为:IPPTOTO_IP
	optname: 	套接字选项名,设置组播属性需要将该参数指定为:IP_MULTICAST_IF
	optval:	设置组播属性,这个指针需要指向一个 struct in_addr{} 类型的结构体地址,这个结构体地址用于存储组播地址,并且组播 IP 地址的存储方式是大端的。
	optlen:optval 指针指向的内存大小,即:sizeof(struct in_addr)
	
返回值:函数调用成功返回 0,调用失败返回 - 1	
*/

   struct in_addr opt;
   // 将组播地址初始化到这个结构体成员中即可
   inet_pton(AF_INET, GROUP_IP, &opt.s_addr);
   setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));

接收端
因为一个组播地址表示一个群组,所以需要接收组播报文的接收者都加入这个群组,和想要接收群消息就必须要先入群是一个道理。加入到这个组播群组的方式如下:

/*
struct in_addr
{
    in_addr_t s_addr;	// unsigned int
};

struct ip_mreqn
{
    struct in_addr imr_multiaddr;   // 组播地址/多播地址
    struct in_addr imr_address;     // 本地地址
    int   imr_ifindex;              // 网卡的编号, 每个网卡都有一个编号
};
// 必须通过网卡名字才能得到网卡的编号: 可以通过 ifconfig 命令查看网卡名字
#include <net/if.h>
// 将网卡名转换为网卡的编号, 参数是网卡的名字, 比如: "ens33"
// 返回值就是网卡的编号
unsigned int if_nametoindex(const char *ifname);
*/

/*
函数:setsockopt
描述:配置接收端UDP组播属性
参数:
	sockfd:基于 udp 的通信的套接字
	level:套接字级别,加入到多播组该参数需要指定为:IPPTOTO_IP
	optname:套接字选项名,加入到多播组该参数需要指定为:IP_ADD_MEMBERSHIP
	optval:加入到多播组,这个指针应该指向一个 struct ip_mreqn{} 类型的结构体地址
	optlen:optval 指向的内存大小,即:sizeof(struct ip_mreqn)
*/
    struct ip_mreqn opt;
    // 要加入到哪个多播组, 通过组播地址来区分
    inet_pton(AF_INET, GROUP_IP, &opt.imr_multiaddr.s_addr);
    opt.imr_address.s_addr = htonl(INADDR_ANY);
    opt.imr_ifindex = if_nametoindex("ens33");
    setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));

组播通信流程
发送组播消息的一端需要将数据发送到组播地址和固定的端口上,想要接收组播消息的终端需要绑定对应的固定端口然后加入到组播的群组,最终就可以实现数据的共享。

发送端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define GROUP_IP "224.0.1.0"
//#define GROUP_IP "239.0.1.10"

int main()
{
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }

    // 2. 设置组播属性 (经测试可以不设置发送端组播属性也能正常发送)
    struct in_addr opt;
    // 将组播地址初始化到这个结构体成员中即可
    inet_pton(AF_INET, GROUP_IP, &opt.s_addr);
    setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));

    char buf[1024];
	char sendaddrbuf[64];
	
	socklen_t len = sizeof(struct sockaddr_in);
	struct sockaddr_in sendaddr;
	
    struct sockaddr_in cliaddr;
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999); // 接收端需要绑定9999端口
    // 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
    inet_pton(AF_INET, GROUP_IP, &cliaddr.sin_addr.s_addr);
						
    // 3. 通信
    int num = 0;
    while(1)
    {
		memset(buf, 0, sizeof(buf));
        sprintf(buf, "hello, client...%d\n", num++);
        // 数据广播
        sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
        printf("发送的组播的数据: %s\n", buf);
		memset(buf, 0, sizeof(buf));
		recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sendaddr, &len);
		printf("sendaddr:%s, prot:%d\n", inet_ntop(AF_INET, &sendaddr.sin_addr.s_addr,  sendaddrbuf, sizeof(sendaddrbuf)),  sendaddr.sin_port);
		printf("接收到的组播消息: %s\n", buf);
    }
    close(fd);
    return 0;
}

接收端代码

#define GROUP_IP "224.0.1.0"
//#define GROUP_IP "239.0.1.10"


int main()
{
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }

    // 2. 通信的套接字和本地的IP与端口绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);    // 大端
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 0.0.0.0
    int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(0);
    }

    // 3. 加入到多播组
	#if 0 //使用struct ip_mreqn或者 struct ip_mreq 设置接收端组播属性都可以正常接收
    struct ip_mreqn opt;
    // 要加入到哪个多播组, 通过组播地址来区分
    inet_pton(AF_INET, GROUP_IP, &opt.imr_multiaddr.s_addr);
    opt.imr_address.s_addr = htonl(INADDR_ANY);
    opt.imr_ifindex = if_nametoindex("ens33");
    setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
	#else
	struct ip_mreq mreq; // 多播地址结构体
	mreq.imr_multiaddr.s_addr=inet_addr(GROUP_IP);
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);	
	ret=setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
	#endif
	
    char buf[1024];
	char sendaddrbuf[64];
	socklen_t len = sizeof(struct sockaddr_in);
	struct sockaddr_in sendaddr;
	
    // 3. 通信
    while(1)
    {
        // 接收广播消息
        memset(buf, 0, sizeof(buf));
        // 阻塞等待数据达到
        recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sendaddr, &len);
		printf("sendaddr:%s, prot:%d\n", inet_ntop(AF_INET, &sendaddr.sin_addr.s_addr,  sendaddrbuf, sizeof(sendaddrbuf)),  sendaddr.sin_port);
        printf("接收到的组播消息: %s\n", buf);
		sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr *)&sendaddr, len);
    }
    close(fd);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/526139.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Enzo Life Sciences--17β-Estradiol high sensitivity ELISA kit

高灵敏ELISA试剂盒&#xff0c;可检测到低至14 pg/ml的17β-雌二醇 雌二醇(estradiol) 是由卵巢内卵泡的颗粒细胞分泌的类固醇激素&#xff0c;是主要的雌激素&#xff0c;负责调节女性特征、附属性器官的成熟和月经-排卵周期&#xff0c;促进乳腺导管系统的产生&#xff0c;有…

鸿蒙南向开发:【智能烟感】

样例简介 智能烟感系统通过实时监测环境中烟雾浓度&#xff0c;当烟雾浓度超标时&#xff0c;及时向用户发出警报。在连接网络后&#xff0c;配合数字管家应用&#xff0c;用户可以远程配置智能烟感系统的报警阈值&#xff0c;远程接收智能烟感系统报警信息。实现对危险及时报…

【大功率汽车大灯升压方案】LED恒流驱动芯片FP7208升压车灯调光应用,PWM内部转模拟,调光深度1%,无频闪顾虑,低亮无抖动

宝马X5前中排座椅宽大舒适&#xff0c;车厢内储物空间丰富。操控性能极佳&#xff0c;底盘稳扎精良。原车为氙气灯&#xff0c;其实宝马的氙气大灯配的比其他车型要好&#xff0c;照明效果是没得说的。但是不管什么灯久了都会出现光衰的情况。下面这辆宝马X5车灯已老化严重。 宝…

学习记录15-运算放大器例题1

一、例题1 图中自己加的一些声明&#xff0c;方便待会讲解&#xff08;请忽略丑。。。&#xff09; 根据虚短原则&#xff1a;U U- U 3V*(R4/(R3R4)) 3V*&#xff08;20 / (1020)) 2V U- U- -1V*(R2/(R1R2))Uo*(R1/(R1R2)) -1V*(20/30)Uo*(10/30) -2/3VUo*1/3 …

如何通过专业二手手机店ERP工具提升手机零售业绩?

在竞争激烈的手机零售市场中&#xff0c;二手手机店如何脱颖而出&#xff0c;实现业绩的稳步增长&#xff1f;途渡科技的超机系列工具提供了一个专业而全面的解决方案&#xff0c;专注于通过二手手机店ERP工具提升商家的运营效率和销售业绩。 1、数字化革新&#xff0c;开启智能…

面试经典150题——删除链表的倒数第 N 个结点

1. 题目描述 2. 题目分析与解析 这个题目整体来讲还是比较简单的&#xff0c;因此直接给出解题思路&#xff1a; 遍历链表计数 计算要删除的节点的位置 创建虚拟头节点 创建指针指向虚拟头节点 移动指针到要删除的节点的前一个节点 删除节点 返回头节点 3. 代码实现 …

四川易点慧电子商务抖音小店安全运营全攻略

在互联网时代&#xff0c;电子商务已成为人们日常生活不可或缺的一部分。抖音小店作为抖音平台上的一个重要功能&#xff0c;为众多商家提供了一个全新的销售渠道。四川易点慧电子商务有限公司&#xff08;以下简称“易点慧”&#xff09;作为业内知名的电商服务提供商&#xf…

废品回收小程序推动回收行业的发展趋势

回收在全球都是一个重要行业&#xff0c;它为全球的环保作出了重要贡献。 随着科技的不断发展创新&#xff0c;废品回收的方式也逐渐多样&#xff0c;全新的线上回收小程序也逐渐出现在大众的生活中&#xff0c;在当下的手机时代&#xff0c;线上回收也为大众提供了更加便利的…

【AIGC】通义千问生成问答数据集

好久没有更新跟实际应用相关的内容了&#xff08;主要是因为公司知识产权问题未能立即公开&#xff0c;目前只能挑选一些脱敏内容与各位分享&#xff09;&#xff0c;如标题所示本期将跟大家讲一下如何通过通义千问生成问答数据集的。 在之前使用 Autokeras 的 RNN 训练时提到…

flex:1是干嘛的

直接上图&#xff1a; flex:1实际代表的是三个属性的简写&#xff0c;如上图所示。 其中flex-grow是用来增大盒子的&#xff0c;比如&#xff0c;当子盒子的宽度小于父盒子的宽度&#xff0c;父盒子的剩余空间可以 利用flex-grow来设置子盒子增大的占比&#xff1b; flex-shri…

如何利用软文吸引用户下单?媒介盒子告诉你

数字技术的进步改变了用户的购物行为&#xff0c;他们期望能够在最短的时间内找到并购买自己需要的产品或服务。软文凭借对用户心理的深入洞察以及柔性的表现形式&#xff0c;成为企业吸引用户的关键因素&#xff0c;今天媒介盒子就来和大家聊聊&#xff1a;如何利用软文吸引用…

Unity2023使用sdkmanager命令行工具安装Android SDK

1&#xff0c;下载cmdline-tools&#xff0c;官网地址&#xff1a;https://developer.android.com/studio或者https://dl.google.com/android/repository/文件名 文件名对应版本名。例如文件名为commandlinetools-win-9862592_latest.zip 引用Android cmdline-tools 版本与其…

Linux:五种IO模型的基本认识

文章目录 IO的本质五种IO模型异步和同步 阻塞IO非阻塞IO信号驱动IO IO的本质 在之前的内容中已经结束了对于网络和操作系统的学习&#xff0c;那么回过来再继续看IO&#xff0c;什么是IO呢&#xff1f; 对于网络的学习当中&#xff0c;实际上也是一种IO&#xff0c;数据从计算…

Android Studio导入第三方so库和jar包——Android Studio

导入so库 方式一&#xff08;libs文件夹&#xff09; 将项目以【Project】的结构显示&#xff0c;将目标架构对应的so文件夹&#xff08;如下图中 的arm64-v8a&#xff09;复制粘贴到app文件下的lib文件夹中&#xff08;如下图的步骤1 2 3&#xff09; 在build.gradle&…

【Java EE】Spring请求如何传递参数详解

文章目录 &#x1f38d;传递单个参数&#x1f334;传递多个参数&#x1f340;传递对象&#x1f384;后端参数重命名&#xff08;后端参数映射&#xff09;&#x1f332;传递数组&#x1f38d;传递集合&#x1f334;传递JSON数据&#x1f338;JSON概念&#x1f338;JSON的语法&a…

Python学习,记录不熟悉知识点

目录 Set&#xff08;集合&#xff09; 集合内置方法完整列表 根据字符串的表达式计算结果 ​编辑 条件控制&#xff1a; if – elif – else match...case 循环语句&#xff1a; while循环 for循环 在同一行中有多个赋值操作&#xff08;先计算&#xff0c;再赋值&…

ThinkPhp8 框架使用 mysql find_in_set 函数

前言: 使用mysql 存储一些标签时 会使用逗号拼接的存储方法 比如 1,2,3,11 一般情况下 查询 1 可能会用到 like %1% 但这样查询的不够准确 因为11也会被查询到 如果每次都多一个逗号 1,2,3,11, 查询时 like %1,% 这样存储有点不太符合程序设计 解决方案 ----------- 官网…

Google Play上架:恶意软件、移动垃圾软件和行为透明度拒审解析(关于对SDK 要求,第三方插件需自查清理混淆)

近期发现很多开发者在上架过程中遇到了 《关于恶意软件、移动垃圾软件和行为透明度》相关的拒审邮件,今天来聊一下关于sdk的政策要求,希望能提供一个自查方向。如有疑问,可以关注博主后私聊进行讨论和咨询。 SDK要求要求如下用户数据政策“敏感信息访问权限和 API”政策“恶…

YOLOv8的多分类模型如何计算准确率(Accuracy)、精确率(Precision)、召回率(recall)和F1-Score模型评估参数

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

mysql 连接查询和子查询

学习了mysql基本查询&#xff0c; 接着学习连接查询和子查询。 4&#xff0c;连接查询 连接是关系数据库模型的主要特点。连接查询是关系数据库中最主要的查询&#xff0c;主要包括内连接、外连接等。通过连接运算符可以实现多个表查询。在关系数据库管理系统中&#xff0c;表建…