Linux网络编程系列之UDP组播
一、什么是UDP组播
UDP组播是指使用用户数据报协议(UDP)实现的组播方式。组播是一种数据传输方式,允许单一数据包同时传输到多个接收者。在UDP组播中,一个数据包可以被多个接收者同时接收,这样可以降低网络传输的负载和提高数据传输效率。
二、特性
1、支持单向的多对多通信:UDP组播可以同时将一个数据包传输给多个接收者,使多个接收者能够同时获取到相同的数据。
2、不可靠性:跟普通的UDP一样,UDP组播只提供不可靠的数据传输服务。如果某个接收者没有接收到数据包,发送者不会得到任何提示或反馈信息。
3、可扩展性:UDP组播支持动态加入和退出组播组,能够自适应地处理组播成员的加入和离开(聊天群里的进群和退群操作)。
4、低延迟:UDP组播传输的数据包不需要在接收方重新组装,可以直接进行处理,因此具有很低的传输延迟。
5、高效:UDP组播传输的数据包只需要经过一次发送操作,就可以同时传输到多个接收者,可以有效地降低网络传输的负载。
6、简单易用:UDP组播不需要复杂的配置和管理,使用简单,能够快速搭建起基于组播的多媒体通信系统。
三、使用场景
1、多媒体流媒体:UDP组播可以在局域网或广域网上传输音视频流,能够快速地向多个接收者发送相同的视频和音频数据,避免了建立多个点对点的连接。
2、 分布式应用的数据分发:UDP组播可以实现高效的数据分发,例如在大型集群环境下,可以将某些服务的状态信息广播给所有节点,使得所有节点都能够及时了解到最新的信息。
3、网络游戏:UDP组播可以用于多人联机游戏,使得多个玩家能够同时收到相同的游戏状态和动作,提高游戏体验。
4、 网络广播:UDP组播可以用于向多个设备广播事件和消息,例如路由器可以向所有连接的设备发送网络配置信息、DHCP服务器可以向所有设备广播IP地址信息等。
5、实时数据更新:UDP组播可以用于实时的数据更新,例如在金融行业,可以订阅某些财经数据的实时更新,以便及时响应市场变化。
可以把UDP组播简单理解为群聊。
四、UDP组播通信流程
1、发送方
(1)、建立套接字。使用socket()
(2)、设置端口复用。使用setsockopt()(可选,推荐)
(3)、绑定自己的IP地址和端口号。使用bind()(可以省略)
(4)、发送数据,接收方IP地址要填写为组播地址。使用sendto()
(5)、关闭套接字。使用close()
2、接收方
(1)、建立套接字。使用socket()
(2)、定义并初始化一个组播结构体。使用struct ip_mreq;
(3)、给套接字加入组播属性。使用setsockopt()
(4)、绑定自己的IP地址和端口号。使用bind(),不可以省略
(5)、接收数据。使用recvfrom()
(6)、关闭套接字。使用close()
五、相关函数API
1、建立套接字
// 建立套接字 int socket(int domain, int type, int protocol);// 接口说明返回值:成功返回一个套接字文件描述符,失败返回-1参数domain:用来指定使用何种地址类型,有很多,具体看别的资源(1)PF_INET 或者 AF_INET 使用IPV4网络协议(2)其他很多的,看别的资源参数type:通信状态类型选择,有很多,具体看别的资源(1)SOCK_STREAM 提供双向连续且可信赖的数据流,即TCP(2)SOCK_DGRAM 使用不连续不可信赖的数据包连接,即UDP参数protocol:用来指定socket所使用的传输协议编号,通常不用管,一般设为0
2、设置端口状态
// 设置端口的状态 int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);// 接口说明返回值:成功返回0,失败返回-1参数sockfd:待设置的套接字参数level: 待设置的网络层,一般设成为SOL_SOCKET以存取socket层参数optname:待设置的选项,有很多种,具体看别的资源,这里讲常用的(1)、SO_REUSEADDR 允许在bind()过程中本地地址可复用,即端口复用(2)、SO_BROADCAST 使用广播的方式发送,通常用于UDP广播(3)、SO_SNDBUF 设置发送的暂存区大小(4)、SO_RCVBUF 设置接收的暂存区大小(5)、IP_ADD_MEMBERSHIP 设置为组播参数optval:待设置的值参数optlen:参数optval的大小,即sizeof(optval)// 组播结构体 struct ip_mreq {struct in_addr imr_multiaddr; // 多播组的IP地址,就是组播的IP地址struct in_addr imr_interface; // 需要加入到组的IP地址,就是自己的IP地址 };
3、绑定IP地址和端口号
// 绑定自己的IP地址和端口号int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);// 接口说明返回值:参数sockfd:待绑定的套接字参数addrlen:参数addr的大小,即sizeof(addr)参数addr:IP地址和端口的结构体,通用的结构体,根据sockfd的类型有不同的定义当sockfd的domain参数指定为IPV4时,结构体定义为struct sockaddr_in{unsigned short int sin_family; // 需与sockfd的domain参数一致uint16_t sin_port; // 端口号struct in_addr sin_addr; // IP地址 unsigned char sin_zero[8]; // 保留的,未使用};struct in_addr{uin32_t s_addr;} // 注意:网络通信时,采用大端字节序,所以端口号和IP地址需要调用专门的函数转换成网络字节序
4、字节序转换接口
// 第一组接口 // 主机转网络IP地址,输入主机IP地址 uint32_t htonl(uint32_t hostlong);// 主机转网络端口,输入主机端口号 uint16_t htons(uint16_t hostshort); // 常用// 网络转主机IP,输入网络IP地址 uint32_t ntohl(uint32_t netlong);// 网络转主机端口,输入网络端口 uint16_t ntohs(uint16_t netshort);// 第二组接口,只能用于IPV4转换,IP地址 // 主机转网络 int inet_aton(const char *cp, struct in_addr *inp);// 主机转网络 in_addr_t inet_addr(const char *cp); // 常用// 网络转主机 int_addr_t inet_network(const char *cp);// 网络转主机 char *inet_ntoa(struct in_addr in); // 常用// 将本地IP地址转为网络IP地址 int inet_pton(int af, const char *src, void *dst); // 参数说明:参数af:选择是哪一种协议族,IPV4还是IPV6参数src:本地IP地址参数dst:将本地IP地址转为网络IP地址存储到这里
5、发送数据
// UDP协议发送数据 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);// 接口说明返回值:成功返回成功发送的字节数,失败返回-1参数sockfd:发送者的套接字参数buf:发送的数据缓冲区参数len:发送的长度参数flags:一般设置为0,还有其他数值,具体查询别的资源参数dest_addr:接收者的网络地址参数addrlen:接收者的网络地址大小,即sizeof(dest_addr)
6、接收数据
// UDP协议接收数据 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);// 接口说明:返回值:成功返回成功接收的字节数,失败返回-1参数sockfd:接收者的套接字参数buf:接收数据缓的冲区参数len:接收的最大长度参数flags:一般设置为0,还有其他数值,具体查询别的资源参数src_addr:发送者的网络地址,可以设置为NULL参数addrlen: 发送者的网络地址大小,即sizeof(src_addr)
7、关闭套接字
// 关闭套接字 int close(int fd);// 接口说明返回值:成功返回0,失败返回-1参数fd:套接字文件描述符
六、案例
实现UDP组播的演示
发送端GroupSend.c
// UDP组播发送方的案例#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h>#define SEND_IP "192.168.64.128" // 记得改为自己IP #define SEND_PORT 10000 // 不能超过65535,也不要低于1000,防止端口误用int main(int argc, char *argv[]) {// 1、建立套接字,使用IPV4网络地址,UDP协议int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd == -1){perror("socket fail");return -1;}// 2、设置端口复用(推荐)int optval = 1; // 这里设置为端口复用,所以随便写一个值int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));if(ret == -1){perror("setsockopt fail");close(sockfd);return -1;}// 3、绑定自己的IP地址和端口号(可以省略)struct sockaddr_in send_addr = {0};socklen_t addr_len = sizeof(struct sockaddr);send_addr.sin_family = AF_INET; // 指定协议为IPV4地址协议send_addr.sin_port = htons(SEND_PORT); // 端口号send_addr.sin_addr.s_addr = inet_addr(SEND_IP); // IP地址ret = bind(sockfd, (struct sockaddr*)&send_addr, addr_len);if(ret == -1){perror("bind fail");close(sockfd);return -1;}// 4、发送数据,往组播地址uint16_t port = 0; // 端口号char ip[20] = {0}; // IP地址struct sockaddr_in recv_addr = {0};char msg[128] = {0}; // 数据缓冲区// 注意输入组播地址,范围是D类网络地址,224.0.0.1~239.255.255.254printf("please input receiver IP and port\n");scanf("%s %hd", ip, &port);printf("IP = %s, port = %hd\n", ip, port);recv_addr.sin_family = AF_INET; // 指定用IPV4地址recv_addr.sin_port = htons(port); // 接收者的端口号recv_addr.sin_addr.s_addr = inet_addr(ip); // 接收者的IP地址while(getchar() != '\n'); // 清空多余的换行符while(1){printf("please input data:\n");fgets(msg, sizeof(msg)/sizeof(msg[0]), stdin);// 发送数据,注意要填写接收者的地址ret = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&recv_addr, addr_len);if(ret > 0){printf("success: send %d bytes\n", ret);}}// 5、关闭套接字close(sockfd);return 0; }
接收端GroupRecv.c
// UDP组播接收方的案例#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h>#define RECV_IP "192.168.64.128" // 记得改为自己的地址#define GROUP_IP "224.0.0.10" // 组播地址 #define GROUP_PORT 20000 // 不能超过65535,也不要低于1000,防止端口误用int main(int argc, char *argv[]) {// 1、建立套接字,使用IPV4网络地址,UDP协议int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd == -1){perror("socket fail");return -1;}// 2、定义并初始化一个组播结构体,设置组播IPstruct ip_mreq vmreq;inet_pton(AF_INET, GROUP_IP, &vmreq.imr_multiaddr); // 初始化组播地址inet_pton(AF_INET, RECV_IP, &vmreq.imr_interface); // 把自己的地址加入到组中// 3、给套接字加入组播属性int ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &vmreq, sizeof(vmreq));if(ret == -1){perror("setsockopt fail");close(sockfd);return -1;}// 4、绑定自己的IP地址和端口号(不可以省略)struct sockaddr_in recv_addr = {0};socklen_t addr_len = sizeof(struct sockaddr);recv_addr.sin_family = AF_INET; // 指定协议为IPV4地址协议recv_addr.sin_port = htons(GROUP_PORT); // 端口号,注意绑定为组播的端口号// recv_addr.sin_addr.s_addr = inet_addr(RECV_IP); // IP地址. 写下面的更好recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机内所有的IP地址ret = bind(sockfd, (struct sockaddr*)&recv_addr, addr_len);if(ret == -1){perror("bind fail");close(sockfd);return -1;}// 4、接收数据uint16_t port = 0; // 端口号char ip[20] = {0}; // IP地址struct sockaddr_in send_addr = {0};char msg[128] = {0}; // 数据缓冲区while(1){// 接收数据,注意使用发送者的地址来接收ret = recvfrom(sockfd, msg, sizeof(msg)/sizeof(msg[0]), 0, (struct sockaddr*)&send_addr, &addr_len);if(ret > 0){memset(ip, 0, sizeof(ip)); // 先清空IPstrcpy(ip, inet_ntoa(send_addr.sin_addr)); // 网络IP转主机IPport = ntohs(send_addr.sin_port); // 网络端口号转主机端口号printf("[%s:%d] send data: %s\n", ip, port, msg);memset(msg, 0, sizeof(msg)); // 清空数据区}}// 5、关闭套接字close(sockfd);return 0; }
通信演示
注:第一幅图只有一台主机,不好演示;第二幅图有两台主机,一台本机,另外一台用ssh连接,实现了组播。
七、总结
组播是一种数据传输方式,允许单一数据包同时传输到多个接收者。在UDP组播中,一个数据包可以被多个接收者同时接收,这样可以降低网络传输的负载和提高数据传输效率。组播主要应用以群聊的场景。UDP组播的通信流程,跟UDP的广播的通信流程大致相同,但是要注意组播接收方要定义一个组播结构体,然后把自己的IP地址加入到组播中。可以结合案例加深对组播的理解。
相关文章:

Linux网络编程系列之UDP组播
一、什么是UDP组播 UDP组播是指使用用户数据报协议(UDP)实现的组播方式。组播是一种数据传输方式,允许单一数据包同时传输到多个接收者。在UDP组播中,一个数据包可以被多个接收者同时接收,这样可以降低网络传输的负载和…...
设计模式~状态模式(state)-23
目录 (1)优点: (2)缺点: (3)使用场景: (4)注意事项: (5)应用实例: 代码 在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状…...

linux环境下使用lighthouse与selenium
一、安装谷歌浏览器、谷歌浏览器驱动、lighthouse shell脚本 apt update && apt -y upgrade apt install -y curl curl -fsSL https://deb.nodesource.com/setup_18.x | bash apt install -y nodejs apt install -y npm npm install -g lighthouse apt-get install -y …...

NeuroImage | 右侧颞上回在语义规则学习中的作用:来自强化学习模型的证据
在现实生活中,许多规则的获取通常需要使用语言作为桥梁,特别是语义在信息传递中起着至关重要的作用。另外,个体使用的语言往往具有明显的奖励和惩罚元素,如赞扬和批评。一种常见的规则是寻求更多的赞扬,同时避免批评。…...

uni-app编程checkbox-group获取选中的每个checkbox的value值
uni-app编程checkbox-group获取选中的每个checkbox的value值_uniappcheckboxvalue-CSDN博客...
数组——螺旋矩阵II
文章目录 一、题目二、题解 题目顺序:代码随想录算法公开课,b站上有相应视频讲解 一、题目 59. Spiral Matrix II Given a positive integer n, generate an n x n matrix filled with elements from 1 to n2 in spiral order. Example 1: Input: n …...
反范式化设计
反范式化设计与范式化设计相对立。范式化设计是将数据组织成多个表,以最小化数据的冗余和提高数据一致性。相反,反范式化设计是故意增加冗余,以提高查询性能和降低复杂性。反范式化设计通常用于需要高度优化的读取密集型应用程序,…...

CCF CSP认证 历年题目自练Day31
题目一 试题编号: 202206-1 试题名称: 归一化处理 时间限制: 500ms 内存限制: 512.0MB 题目背景 在机器学习中,对数据进行归一化处理是一种常用的技术。 将数据从各种各样分布调整为平均值为 0、方差为 1的标准分布&a…...
PCL点云处理之从两片点云中获取具有匹配关系的同名点对 (二百一十八)
PCL点云处理之从两片点云中获取具有匹配关系的同名点对 (二百一十八) 一、算法介绍二、算法实现1.代码2.效果一、算法介绍 点云配准的前提是,我们知道或者预测了一些匹配对,我们认为这些匹配对就是两片点云中的同名点,同名点就是由于激光扫描存在误差的关系,导致同一地物…...

MySQL Row size too large (> 8126)
错误信息 ERROR 1118 (42000) at line 901: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMATDYNAMIC or ROW_FORMATCOMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. 错误原因 这个问题…...

HUAWEI(26)——防火墙双机热备
一、拓扑 二、需求 PC2 ping PC1 FW1与FW2双机热备,FW1为active,FW2为Standby,抢占延时1s VRRP 三、配置 1.IP地址,防火墙接口加入区域 防火墙用户名:admin 防火墙旧密码:Admin@123 防火墙新密码:admin@123 [FW1]interface GigabitEthernet 1/0/0 [FW1-GigabitEthe…...

【ArcGIS】NDVI估算植被覆盖度FVC
NDVI估算植被覆盖度FVC NDVI计算植被覆盖度FVC计算NDVI估算植被覆盖度FVC操作步骤Step1:调出栅格计算器工具Step2:查找NDVIStep3: 参考 NDVI计算 植被覆盖度FVC计算 NDVI估算植被覆盖度FVC操作步骤 Step1:调出栅格计算器工具 1、首先打开软件&#x…...

vscode用密钥文件连接ssh:如果一直要输密码怎么办
commandshiftP:打开ssh配置文件 加上这么一段,host就是你给主机起的名字 对IdentityFile进行更改,改成相应的密钥文件 然后commandshiftP链接到主机就可以了 但是有时候它会让输入密码 这是由于你给这个IdentityFile的权限太多了…...

【AI视野·今日Robot 机器人论文速览 第五十三期】Thu, 12 Oct 2023
AI视野今日CS.Robotics 机器人学论文速览 Thu, 12 Oct 2023 Totally 25 papers 👉上期速览✈更多精彩请移步主页 Daily Robotics Papers Pixel State Value Network for Combined Prediction and Planning in Interactive Environments Authors Sascha Rosbach, St…...
【LeetCode第115场双周赛】100029. 和带限制的子多重集合的数目 | 前缀和背包 | 中等
题目内容 原题链接 给定一个长度为 n n n 的数组 n u m s nums nums 和一个区间左右端点 [ l , r ] [l,r] [l,r] 。 返回 n u m s nums nums 中子多重集合的和在闭区间 [ l , r ] [l, r] [l,r] 之间的 子多重集合的数目 。 子多重集合 指的是从数组中选出一些元素构成的 …...

ArcGIS笔记5_生成栅格文件时保存报错怎么办
本文目录 前言Step 1 直接保存到指定文件夹会报错Step 2 先保存到默认位置再数据导出到指定文件夹 前言 有时生成栅格文件时,保存在自定义指定的文件夹内会提示出错,而保存到默认位置则没有问题。因此可以通过先保存到默认位置,再数据导出到…...

YOLO目标检测——跌倒摔倒数据集【含对应voc、coco和yolo三种格式标签】
实际项目应用:公共安全监控、智能家居、工业安全等活动区域无监管情况下的人员摔倒事故数据集说明:YOLO目标检测数据集,真实场景的高质量图片数据,数据场景丰富。使用lableimg标注软件标注,标注框质量高,含…...

uniapp小程序实现绘制内容,生成海报并保存截图(Painter和Canvas两种方式举例)
一、Painter方法 Painter插件传送门 1.下载资源包 2.将资源包的如下部分 3.使用页面引入组件 ui样式 <paintercustomStyle=margin-left: 40rpx; height: 1000rpx;palette="{{palette}}"bind:imgOK="onImgOK"/>data 中数据(绘制内容,替换区域) pai…...
HTTPS双向认证及密钥总结
公钥私钥: 1)公钥加密,私钥解密:加解密 为什么不能私钥加密公钥解密? 私钥加密后,公钥是公开的都能解密,没有意义。 2)私钥签名,公钥验签:属于身份验证,防串改&#x…...

Mybatis用Byte[]存图片,前端显示图片
前端页面 static下 也就是说byte[] 转成JSON字符串后,和用BASE64编码后是一摸一样的,那么SpringBoot会自动将实体类转JSON字符串,也就是说根本不需要Base64编码 注意:两个值并非一摸一样,一个多了个双引号 byte[]的值前后有个双引号 有一点点区别 一个有双引号,一个没有…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
LangChain【6】之输出解析器:结构化LLM响应的关键工具
文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器?1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...