【WCH以太网接口系列芯片】基于CH395的组播请求(IGMP)
在上一篇文章中,我们通过直连电脑测试了CH395在组播环境中进行数据的收发,但在实际的使用场景中更多的是将CH395接入局域网环境中。因此,我们需要使用到一个协议——IGMP(Internet Group Management Protocol)。
IGMP和ICMP一样,都是IP层的一部分。IGMP报文通过IP数据包进行传输,由20字节IP首部和8字节IGMP报文封装而成,其中IP首部的协议字段值为2来指明IGMP报文(重点,后续需要用上)。我们通过wireshark抓包可以发现电脑会不断的发送IGMP报文请求加入组播(图1示),而对比发现上一篇文章CH395的组播例程抓包中并没有发出该报文,所以CH395在路由环境中会不能正常收发组播数据。

图1 电脑端发送的IGMP报文
那如何使用CH395发送该报文呢?这里教大家一个简单的快速使用方法。上面我们讲过IGMP报是通过IP数据报进行传输的,那我们就可以通过CH395的IP-RAW或者MAC-RAW的方式来自己封装该报文。那IGMP报文的内容该填写什么呢?电脑会发出组播请求报文,那我们就根据该报文来填充IP-RAW模式下的报文内容即可。
用wireshark抓取一个电脑端发送的IGMP报文,分析后会发现如果使用IP-RAW模式,那前面的MAC地址和IP信息是不用我们填写了,该内容会在CH395的协议栈中自动填充(MAC-RAW就需要在程序里面填充全部内容了)。其中图2中示IGMP Verrsion:3表示IGMPv3协议;Type报文类型,取值为0x22(画重点,取值关系到发送出去的IGMP类型);Reserverd: 00 保留;Checksum:校验值(这个值可以根据算法算出来的,但本篇文章不涉及算法,所以后文会用最原始的方法填充);Num Group Records 1:已加入1个组;Group Record 224.0.0.251: 加入的组是224.0.0.251。

图2 IGMP报文内容
分析完报文类型后,根据IP数据包的格式,我们应该需要填充内容为报文类型+保留值+校验值+加入组数量+保留值+要加入的组地址。接下来通过编写程序来实现CH395发送该报文。
首先,根据官方手册图三示,配置socket为成IP-RAW模式,也就是协议类型为0。

图三 IP-RAW配置
程序设定了socket3为IP-RAW模式来进行数据发送,目的地址是224.0.0.22(这是一个特殊地址空间,应用上有区别,下面会特别说明),配置程序如下:
const UINT8 Socket3DesIP[4] = {224,0,0,22}; /* Socket 3目的IP地址(PC) */
void InitSocketParam(void){
memset(&sockinf[0],0,sizeof(sockinf[0])); /* 将SockInf[0]全部清零*/
memcpy(&sockinf[0].IPAddr, BroadcastIP,sizeof(BroadcastIP)); /* 如果启用UDP SERVER功能,需将目的IP设为广播地址255.255.255.255 */
// sockinf[0].DesPort = Socket0DesPort; /* 目的端口 */
sockinf[0].SourPort= Socket0SourPort; /* 源端口 */
sockinf[0].ProtoType=PROTO_TYPE_UDP; /* UDP模式 */
memset(&sockinf[1],0,sizeof(sockinf[1])); /* 将SockInf[1]全部清零*/
memcpy(&sockinf[1].IPAddr, BroadcastIP,sizeof(BroadcastIP)); /* 如果启用UDP SERVER功能,需将目的IP设为广播地址255.255.255.255 */
// sockinf[1].DesPort = Socket1DesPort; /* 目的端口 */
sockinf[1].SourPort= Socket1SourPort; /* 源端口 */
sockinf[1].ProtoType=PROTO_TYPE_UDP;
memset(&sockinf[2],0,sizeof(sockinf[2])); /* 将SockInf[2]全部清零*/
memcpy(&sockinf[2].IPAddr, BroadcastIP,sizeof(BroadcastIP)); /* 如果启用UDP SERVER功能,需将目的IP设为广播地址255.255.255.255 */
// sockinf[1].DesPort = Socket1DesPort; /* 目的端口 */
sockinf[2].SourPort= Socket2SourPort; /* 源端口 */
sockinf[2].ProtoType=PROTO_TYPE_UDP;
memset(&sockinf[3],0,sizeof(sockinf[3])); /* 将SockInf[3]全部清零*/
memcpy(CH395Inf.IPAddr,Socket2DesIP,sizeof(Socket3DesIP)); /* 将目的IP地址写入 */
sockinf[3].ProtoType = PROTO_TYPE_IP_RAW; /* IP RAW模式 */
}
设置socket3时,IPRawProto的协议段为02,上面说过IGMP在IP报中的协议类型为2。
const UINT8 IPRawProto = 0x02;
void CH395SocketInitOpen(void)
{
UINT8 i; /* socket 0为UDP模式 */
CH395SetSocketDesIP(0,sockinf[0].IPAddr); /* 设置socket 0目标IP地址 */
CH395SetSocketProtType(0,sockinf[0].ProtoType); /* 设置socket 0协议类型 */
CH395SetSocketSourPort(0,sockinf[0].SourPort); /* 设置socket 0源端口 */
i = CH395OpenSocket(0); /* 打开socket 0 */
mStopIfError(i); CH395SetSocketDesIP(1,sockinf[1].IPAddr); /* 设置socket 1目标IP地址 */
CH395SetSocketProtType(1,sockinf[1].ProtoType); /* 设置socket 1协议类型 */
CH395SetSocketSourPort(1,sockinf[1].SourPort); /* 设置socket 1源端口 */
i = CH395OpenSocket(1); /* 打开socket 1 */
mStopIfError(i); CH395SetSocketDesIP(2,sockinf[2].IPAddr); /* 设置socket 2目标IP地址 */
CH395SetSocketProtType(2,sockinf[2].ProtoType); /* 设置socket 2协议类型 */
CH395SetSocketSourPort(2,sockinf[2].SourPort); /* 设置socket 2源端口 */
i = CH395OpenSocket(2); /* 打开socket 2 */
mStopIfError(i); /* socket 0为IP RAW模式 */
CH395SetSocketDesIP(3,CH395Inf.IPAddr); /* 设置socket 3目标地址 */
CH395SetSocketProtType(3,sockinf[3].ProtoType); /* 设置socket 3协议类型 */
CH395SetSocketIPRAWProto(3,IPRawProto); /* 设置协议字段,重点此时的IP报协议段为2 */
i = CH395OpenSocket(3); /* 打开socket 3 */
mStopIfError(i);
/* 检查是否成功 */
}
完成socket3的IP-RAW模式初始化后就可以封装IGMP报文了,根据电脑抓包内容,我们在程序里定义一个Buffer来填充报文。IGMP中Mac地址和目的Ip都是已经在IP-RAW模式下CH395的内部协议栈自动封装,IP报类型也已填入。因此,Buffer中中需要填入0x22,0x00(IGMP类型、保留值);校验值随机填入两位0x01,0x02(后面会根据抓包修改);保留值填3个0x00;入组数量填1;最后填入Group Record信息:类型 :04;Len:00;Num:00;组播地址:e0:00:00:fb。
UINT8 MyBuffer1[24] = {
0x22,0x00,
0x01,0x02,
0x00,0x00,0x00,0x01,
0x04,0x00,0x00,0x00,0xe0,0x00,0x00,0xfb,
};
MAC-RAW方式需要自己手动全部添加报文内容(Buffer最后一行为0是为了满足以太网的最小帧64字节):
UINT8 MyBuffer1[] =
{
0x54,0xbf,0x64,0x1c,0x2a,0x9c,0xb4,0x05,0x00,0x00,
0x00,0x00,0x08,0x00,0x45,0x00,0x00,0x2c,0x02,0x98,
0x00,0x00,0x01,0x02,0x13,0x16,0xc0,0xa8,0x01,0x0A,
0xe0,0x00,0x00,0x16,0x22,0x00,0xf9,0x02,0x00,0x00,
0x00,0x01,0x04,0x00,0x00,0x00,0xe0,0x00,0x00,0xfb,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
在初始化的时候,一定设置一个CH395的TTL值(重点):
InitSocketParam(); /* 初始化socket相关变量 */
CH395SocketInitOpen(); /*打开socket */
CH395SetTTLNum(3,1); /* 设置TTL值 */
为什么要设置这个TTL值呢?因为我们的目的IP地址是224.0.0.22,而在TCP\IP协议规定中,这个地址其为特殊地址空间不能超过1跳(可以在TCPIP详解1卷中了解),图三示该IP时设置TTL为1的原因。

图三 特殊地址空间的TTL值
设置完成后,可以在主函数循环调用CH395Senddata函数进行发送:CH395SendData(3,MyBuffer1,24)。提示:一般这个报文需要定时发送,因为有些路由或者交换机在一定时间内没有收到该设备的组播请求会踢出设备,所以应用时可以通过定时器来定时发送该报文。
CH395初始化完成并启动后,通过Wireshark抓包会发现CH395发出了IGMP报文,但是打开报文分析会出现如图四示红色报错。

图四 CH395发送的IGMP报文
报错内容为校验值不对,0x01、0x02应该是0xF9、0x02,那我们就把Buffer中的01、02改成0xF9、0x02再看抓包结果如图五示。

图五 CH395的IGMP请求加入报文
这时候wiresahrk的报文分析中没有出现红色报错了。但大家可能会发现电脑1.21发出的报文长度为54字节,而CH395的IP1.200发出的长度为60字节,是不是报文还是错的呢?不是,这是因为以太网帧最小为64字节,如果小于64字节就会有padding段填充,同时wireshark会不显示4字节的校验字段,减去该4字节就为60字长,图六示60字节的原因。

图六 CH395的60字长IGMP报文
到这里CH395的IGMP就没问题了,CH395的IP-RAW和MAC-RAW方式例程已放到下面自取,关于IGMP报文里的校验值其实可以通过算法进行填充,但这里只是教大家一个快速简单的应用方法,如果感兴趣的可以根据网上的算法例程自行添加。
例程链接:https://files.cnblogs.com/files/blogs/805237/Socket-UdpMulticast.rar?t=1700655709&download=true
【WCH以太网接口系列芯片】基于CH395的组播请求(IGMP)的更多相关文章
- 海思3516系列芯片SPI速率慢问题深入分析与优化(基于PL022 SPI 控制器)
海思3516系列芯片SPI速率慢问题深入分析与优化(基于PL022 SPI 控制器) 我在某个海思主控的项目中需要使用SPI接口来驱动一块液晶屏,液晶屏主控为 st7789,分辨率 240x240,图 ...
- nxp基于layerscape系列芯片的硬件型号解析
每一种layerscape系列芯片都有两种硬件型号: RDB 和QDS RDB: Refrence Design Board QDS: QorIQ Development system
- 以太网接口TCP/IP协议介绍,说的很容易懂了
以太网接口TCP/IP协议介绍,说的很容易懂了 TCP/IP协议,或称为TCP/IP协议栈,或互联网协议系列. TCP/IP协议栈(按TCP/IP参考模型划分) 应用层 FTP SMTP HTT ...
- [转帖]你不曾见过的国产CPU:可能是最全的龙芯系列芯片家谱(下)
你不曾见过的国产CPU:可能是最全的龙芯系列芯片家谱(下) https://www.ijiwei.com/html/news/newsdetail?source=pc&news_id=7177 ...
- Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
- Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
- Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401开发
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
- Nacos系列:基于Nacos的配置中心
前言 在看正文之前,我想请你回顾一下自己待过的公司都是怎么管理配置的,我想应该会有以下几种方式: 1.硬编码 没有什么配置不配置的,直接写在代码里面,比如使用常量类 优势:对开发友好,开发清楚地知道代 ...
- STM32系列芯片命名规范
1.STM32的基础知识 STM32是意法半导体公司,基于ARM Cortex®-M0,M0+,M3, M4和M7内核生产的系列通用MCU.截止当前时间为止(20190515),STM32有STM32 ...
- Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
随机推荐
- 关于 LLM 和图数据库、知识图谱的那些事
本文整理自 NebulaGraph 布道师 wey 在「夜谈 LLM」主题分享上的演讲,主要包括以下内容: 背景 LLM RAG Graph 知识抽取 Text2Cypher Graph RAG 未来 ...
- Docker数据持久化与数据共享
上篇文章的最后我们使用Docker部署了一个纯前端项目,但还有一个很重要的问题就是容器中产生的数据(比如log文件),容器一旦被删除,容器内的所有数据也就没有了,为了避免这个问题我们可以将数据存储到容 ...
- 代替forever下一个部署node的持久化工具---pm2
最近有个后端项目,用的是node,在持久化的时候会挂掉,详细了解到用的是nohup,然后先详细了解了一下nohup nohup是一个Linux命令,用于在系统后台不挂断地运行命令,退出终端不会影响程序 ...
- Python操作Redis大全
一.字符串 string Python操作Redis的redis模块对字符串(string)的主要操作函数包括:SET.GET.GETSET.SETEX.SETNX.MSET.MSETNX.INCR( ...
- 入门篇-其之一-第一个Java程序
️注意: 本文中包含实际操作,需要安装JDK.如果需要安装JDK,请按照这篇文章的步骤进行安装:点我查看JDK安装教程 小白可以多看几遍这篇文章,多敲几次代码 前面我们已经安装了JDK,接下来就是写一 ...
- [Python3] 初识py, 一个简单练手的小玩意. 快递查询
有图有真相 脚本代码 最近刚入门py, 准备写点小玩意练练手. 于是决定拿快递100开刀. 因为它的api很简单. # 快递100 API # 作者: 剑齿虎 # 邮箱: yuxiaobo64@gma ...
- HTML一键打包APK工具1.9.5更新,新增一机一码功能
HMTL网址打包APK,可以把本地HTML项目, Egret游戏,网页游戏,或者网站打包为一个安卓应用APK文件,无需编写任何代码,也无需配置安卓开发环境,支持在最新的安卓设备上安装运行. 打包软件会 ...
- 机器学习(6)K近邻算法
k-近邻,通过离你最近的来判断你的类别 例子: 定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近的样本中大多数属于某一类别),则该样本属于这个类别 K近邻需要做标准化处理 例如: imp ...
- Node练习 | 文件管理模块使用
功能 新建一个Project文件夹, 里面是三个新建的文件, 分别是app.js/app.css/index.html 实现步骤 fs模块中的同步和非同步 同步 等待运行完成后再运行下一步 本次练习为 ...
- filebeat新filestream类型是否支持tail_files类似功能探究
背景 试水搭建ELK,使用了ELK7.17.13版本,filebeat默认配置的input type已经是filestream而非旧版的log类型,开始了探索之旅. 信任ChatGPT导致的三次失败尝 ...