网络高级(学习)2024.9.10
目录
一、Modbus简介
1.起源
2.特点
3.应用场景
二、Modbus TCP协议
1.特点
2.协议格式
3.MBAP报文头
4.功能码
5.寄存器
(1)线圈寄存器,类比为开关量,每一个bit都对应一个信号的开关状态。
(2)离散输入寄存器,离散输入寄存器就相当于线圈寄存器的只读模式,每个bit表示一个开关量,而开关量只能读取输入的开关信号,不能写的。
(3)保持寄存器,这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。
(4)输入寄存器,这个和保持寄存器类似,但是也是只支持读而不能写。
三、工具软件
1.modbus软件
Modbus slave端
Modbus poll端
2.wireshark软件
过滤器选择
过滤条件
3.网络调试助手
四、代码编程
1.读取保持寄存器中的数值(功能号03),起始地址40001,寄存器个数1个
2.编程实现主机功能,写入单个线圈状态(功能号05)
一、Modbus简介
1.起源
Modbus由Modicon公司于1979年开发,是一种工业现场总线协议标准。
Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,其中最著名的是Modbus RTU、Modbus ASCII、Modbus TCP三种
2.特点
免费、简单、容易使用
3.应用场景
Modbus协议是现在国内工业领域应用最多的协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备。
二、Modbus TCP协议
1.特点
(1)Modbus TCP采用主从问答方式(master/slave)通信,有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点(可以多个),每一个slave设备都有一个唯一的地址
(2)Modbus TCP是基于TCP实现的应用层协议
(3)默认端口号为502
2.协议格式

3.MBAP报文头
Modbus TCP协议包含一个7字节报文头
事务处理标识符:2字节,报文的序列号
协议标识符:2字节,0000表示Modbus TCP协议
长度:2字节,字节长度
单元标识符:1字节,从机地址
4.功能码
根据四种不同的寄存器设置了8种功能码,根据实际需要设置不同的功能码
在协议中,功能码占1个字节
| 功能码 | 作用 | 寄存器PLC地址 | 位操作/字操作 | 操作数量 |
| 01 | 读线圈状态 | 00001-09999 | 位操作 | 单个或多个 |
| 02 | 读离散输入状态 | 10001-19999 | 位操作 | 单个或多个 |
| 03 | 读保持寄存器 | 40001-49999 | 字操作 | 单个或多个 |
| 04 | 读输入寄存器 | 30001-39999 | 字操作 | 单个或多个 |
| 05 | 写单个线圈 | 00001-09999 | 位操作 | 单个 |
| 06 | 写单个保持寄存器 | 40001-49999 | 字操作 | 单个 |
| 15 | 写多个线圈 | 00001-09999 | 位操作 | 多个 |
| 16 | 写多个保持寄存器 | 40001-49999 | 字操作 | 多个 |
5.寄存器
Modbus TCP通过寄存器的方式存储数据。
一共有四种类型的寄存器,分别是:离散量输入、线圈、输入寄存器、保持寄存器。
离散量和线圈其实就是位寄存器(每个寄存器数据占1个字节),工业上主要用于控制IO设备。输入和保持寄存器是字寄存器(每个寄存器数据占2个字节),工业上主要用于存储工业设备的值。
(1)线圈寄存器,类比为开关量,每一个bit都对应一个信号的开关状态。
所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。
功能码:0x01 0x05 0x0f
(2)离散输入寄存器,离散输入寄存器就相当于线圈寄存器的只读模式,每个bit表示一个开关量,而开关量只能读取输入的开关信号,不能写的。
比如读取外部按键是按下还是松开。
功能码:0x02
(3)保持寄存器,这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。
比如设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写
功能码:0x03 0x06 0x10
(4)输入寄存器,这个和保持寄存器类似,但是也是只支持读而不能写。
一个寄存器也是占据两个byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值
功能码:0x04
三、工具软件
1.modbus软件
Modbus slave端
此端是从机,相当于服务器,需要先运行
设置:setup->设置从机ID、指定寄存器、起始地址、个数
连接:connection->connect,设置ip和端口号,进行连接
Modbus poll端
此端是主机,相当于客户端
设置:setup->设置从机ID、功能码、起始地址、个数
连接:connection->connect,设置ip和端口号,进行连接
2.wireshark软件
过滤器选择
如果是在windows下本地测试选择Loopback adapter
如果数据经过路由器,选择WLAN
过滤条件
过滤ip:ip.addr == ip地址
过滤端口号:tcp.port == 端口号
过滤协议类型:协议类型名
注:每个条件通过&&连接,最后敲回车生效
3.网络调试助手

四、代码编程
1.读取保持寄存器中的数值(功能号03),起始地址40001,寄存器个数1个
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("sockfd 失败");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(502);saddr.sin_addr.s_addr = inet_addr("192.168.50.121");socklen_t addrlen = sizeof(saddr);if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){perror("connect失败\n");return -1;}printf("connect 成功\n");#define N 32uint8_t buf[N] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01};uint8_t buf1[N];unsigned int n;send(sockfd, buf, N, 0);sleep(1);int ret = recv(sockfd, buf1, N, 0);if (ret < 0){perror("recv失败");return -1;}else if (ret == 0){printf("连接关闭\n");return 0;}else{for (int i = 0; i < ret; i++){printf("0x%X ", buf1[i]);}printf("\n");}close(sockfd);return 0;
}
封装成函数:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>#define N 32int sockfd;void read_hold_register(int socket, uint8_t addr, uint8_t fun, uint16_t addra, uint16_t count)
{uint8_t buf[N] = {0};buf[0] = 0x00; // 事务处理标识符(高位)buf[1] = 0x00; // 事务处理标识符(低位)buf[2] = 0x00; // 协议标识符(高位)buf[3] = 0x00; // 协议标识符(低位)buf[4] = 0x00;buf[5] = 0x06; // 字节长度buf[6] = addr; // 从机地址buf[7] = fun; // 功能码buf[8] = addra >> 8; // 寄存器起始地址(高位)buf[9] = addra & 0x00ff; // 寄存器起始地址(低位)buf[10] = count >> 8; // 寄存器数量(高位)buf[11] = count & 0x00ff; // 寄存器数量(低位)send(socket, buf, N, 0); // 发送请求memset(buf, 0, N); // 清空缓冲区int ret = recv(socket, buf, N, 0); // 接收响应if (ret < 0){perror("recv失败");return;}else if (ret == 0){printf("连接关闭\n");return;}else{for (int i = 0; i < ret; i++){printf("0x%X ", buf[i]);}printf("\n");}
}int main(int argc, char const *argv[])
{sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket 失败");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(502);saddr.sin_addr.s_addr = inet_addr("192.168.50.121");socklen_t addrlen = sizeof(saddr);if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){perror("connect 失败");close(sockfd);return -1;}printf("connect 成功\n");uint8_t fun, addr;uint16_t addra, count;printf("请输入功能码(格式0x01):");scanf(" %hhx", &fun);printf("请输入从机地址(格式0x01):");scanf(" %hhx", &addr);printf("请输入起始地址(格式0x0001):");scanf(" %hx", &addra);printf("请输入寄存器数量(格式0x0001):");scanf(" %hx", &count);read_hold_register(sockfd, addr, fun, addra, count);close(sockfd);return 0;
}
2.编程实现主机功能,写入单个线圈状态(功能号05)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("sockfd 失败");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(502);saddr.sin_addr.s_addr = inet_addr("192.168.50.121");socklen_t addrlen = sizeof(saddr);if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){perror("connect失败\n");return -1;}printf("connect 成功\n");#define N 32uint8_t buf[N] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00};uint8_t buf1[N];unsigned int n;send(sockfd, buf, N, 0);sleep(1);int ret = recv(sockfd, buf1, N, 0);if (ret < 0){perror("recv失败");return -1;}else if (ret == 0){printf("连接关闭\n");return 0;}else{for (int i = 0; i < ret; i++){printf("0x%X ", buf1[i]);}printf("\n");}close(sockfd);return 0;
}
封装成函数:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>#define N 32int sockfd;void read_hold_register(int socket, uint8_t addr, uint8_t fun, uint16_t addra, uint16_t count)
{uint8_t buf[N] = {0};buf[0] = 0x00; // 事务处理标识符(高位)buf[1] = 0x00; // 事务处理标识符(低位)buf[2] = 0x00; // 协议标识符(高位)buf[3] = 0x00; // 协议标识符(低位)buf[4] = 0x00;buf[5] = 0x06; // 字节长度buf[6] = addr; // 从机地址buf[7] = fun; // 功能码buf[8] = addra >> 8; // 线圈地址(高位)buf[9] = addra & 0x00ff; // 线圈地址(低位)buf[10] = count >> 8; // 断通标志(高位)buf[11] = count & 0x00ff; // 断通标志(低位)send(socket, buf, 12, 0); // 发送请求memset(buf, 0, N); // 清空缓冲区int ret = recv(socket, buf, N, 0); // 接收响应if (ret < 0){perror("recv失败");return;}else if (ret == 0){printf("连接关闭\n");return;}else{for (int i = 0; i < ret; i++){printf("0x%X ", buf[i]);}printf("\n");}
}int main(int argc, char const *argv[])
{sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket 失败");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(502);saddr.sin_addr.s_addr = inet_addr("192.168.50.121");socklen_t addrlen = sizeof(saddr);if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){perror("connect 失败");close(sockfd);return -1;}printf("connect 成功\n");uint8_t fun, addr;uint16_t addra, count;printf("请输入功能码(格式0x01):");scanf(" %hhx", &fun);printf("请输入从机地址(格式0x01):");scanf(" %hhx", &addr);printf("请输入线圈地址(格式0x0001):");scanf(" %hx", &addra);printf("请输入断通标志(格式0x0001):");scanf(" %hx", &count);read_hold_register(sockfd, addr, fun, addra, count);close(sockfd);return 0;
}
相关文章:
网络高级(学习)2024.9.10
目录 一、Modbus简介 1.起源 2.特点 3.应用场景 二、Modbus TCP协议 1.特点 2.协议格式 3.MBAP报文头 4.功能码 5.寄存器 (1)线圈寄存器,类比为开关量,每一个bit都对应一个信号的开关状态。 (2)…...
【软件全文档】项目概要设计说明书(2024实际项目Word原件)
一、 引言 (一) 编写目的 (二) 范围 (三) 文档约定 (四) 术语 二、 项目概要 (一) 建设背景 (二) 建设目标 (三࿰…...
震惊!国产数据库厂商减少了51家!
前面文章我提到国产数据库厂商实际上大部分都不赚钱,我估计国产目前国产数据库厂商利润为正的,目前不超过5家。 而经济寒冬,融资困难,那么对于很多厂商,尤其是全靠融资的数据库厂商来讲,这将变得极其困难。…...
[AI书籍分享]<AI时代,学什么,怎么学 - 和渊>
本文由Markdown语法编辑器编辑完成。 1, 背景: 本书是一位清华大学毕业的生物学博士,和渊老师,现就职于人大附中, 是一名一线的高中生物教师. 她之前已经写过几本关于教育类的书籍,而这本书,则是她针对当前, AI时代迅猛发展的背…...
鸿蒙HarmonyOS开发:一次开发,多端部署(界面级)断点和媒体查询
文章目录 概述引入与使用流程媒体查询条件语法规则媒体类型(media-type)媒体逻辑操作(media-logic-operations)媒体特征(media-feature) 场景示例1、监听设备屏幕的方向(竖屏,横屏&a…...
1 Linux SSH安全加固_linux system-auth
 RabbitMQ详解
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。 消息中间件最主要的作用是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理&…...
《华为 eNSP 模拟器安装教程》
1.电脑安装环境要求: 检查电脑是否安装过 eNSP 和依赖软件,如果有,请全部卸载。 安装软件列表: 2.软件安装: 安装 WinPcap: 打开安装包,单击【Next】 单击【I Agree】 单击【Install】 单击【…...
vector(二)vector模拟实现
vector成员变量是三个迭代器 vector的迭代器底层与string相同是使用 指针实现的 使用的是类模版T*指针 template<class T> class vector { public:typedef T* iterator;typedef const T* const_iterator; private:iterator _start nullptr;iterator _finish nullp…...
【Canvas与电脑桌面】用六角回旋镖铺满一个平面(1920*1080)
【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>六角回旋镖桌面1920x1080</title><style type"text/cs…...
创游系列开心娱乐完整组件
别人分享的一套东西,是个不错的娱乐源码,里面包含了很多小游戏。可以创建房间。 没测试自行研究吧,内含搭建教程。 代码免费下载:百度网盘...
高效驱动之选 ——KP85211ASGA 半桥栅极驱动器 内置互锁死区
KP85211A是一款 225V 耐压,具有 1A 拉电流和 1.5A 灌电流能力的半桥栅极驱动器,专用于驱动功率MOSFET或IGBT。采用高压器件工艺技术,具有良好的电流输出及出色的抗瞬态干扰能力。可保证开关节点 VS 瞬态 -7V 情况下系统正常工作。可支持开关节…...
建投数据获批安全生产许可证
9月1日,建投数据成功获批由北京市住房和城乡建设委员会核发的《安全生产许可证》。该资质的获得,是建投数据能力与实力的展现,更是对其企业规模、管理水平、项目业绩等的肯定。 《安全生产许可证》是国家为了加强安全生产监督管理,…...
MCU9.reg52.h的介绍
1.引用头文件的两种方式 #include <reg52.h> #include "reg52.h" 区别:优先搜索的位置不同! 在keil软件中 #include <reg52.h> 优先搜索软件安装的INC文件夹 #include "reg52.h" 优先搜索当前工程文件夹下的头文件,如果没有,则在软件安装的…...
Python知识点:如何使用Python进行二维码生成与识别
在Python中,生成和识别二维码可以使用不同的库来实现。最常用的库包括 qrcode 和 pyzbar。以下是如何使用这些库来生成和识别二维码的示例: 1. 生成二维码 你可以使用 qrcode 库来生成二维码。首先,你需要安装它: pip install …...
跨域问题(CORS)
文章目录 介绍解决一、添加跨域头,允许跨域1.后端配置CORS策略(4种方法)2.配置nginx 二、代理 介绍 跨域资源共享(CORS, Cross-Origin Resource Sharing)是浏览器的一个安全机制,用来防止来自一个域的网页对另一个域下的资源进行…...
评测AI写毕业论文软件排行榜前十名的网站
在当今信息爆炸的时代,AI智能写作工具已经成为我们写作过程中的得力助手。特别是对于学术论文的撰写,这些工具不仅能够提高写作效率,还能帮助用户生成高质量的文稿。以下是五款值得推荐的AI智能写论文软件,其中特别推荐千笔-AIPas…...
发邮件格式
邮件作为一种正式的沟通方式,其格式通常需要遵循一定的规范。 尊敬的xx:(无缩进) 您好!(缩进两个字符) 正文(缩进两个字符)xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xxx正文xx…...
解锁Web3.0——Scaffold-eth打造以太坊DApp的终极指南
🚀本系列文章为个人学习笔记,目的是巩固知识并记录我的学习过程及理解。文笔和排版可能拙劣,望见谅。 目录 前言 一、快速部署 1、前期准备: 2、安装项目: 二、配置部署运行环境 1、初始化本地链:…...
机器学习之监督学习(四)决策树和随机森林
机器学习之监督学习(四)决策树和随机森林 0. 文章传送1. 决策树 Decision Tree案例引入构建过程 2. 随机森林 Random Forest3. 决策树 vs 神经网络4. 代码实现手写版本sklearn版本 5. 案例Iris数据集介绍实验代码 0. 文章传送 机器学习之监督学习&#…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
C++--string的模拟实现
一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现,其目的是加强对string的底层了解,以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量,…...
echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式
pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图,如果边框加在dom上面,pdf-lib导出svg的时候并不会导出边框,所以只能在echarts图上面加边框 grid的边框是在图里…...
Axure零基础跟我学:展开与收回
亲爱的小伙伴,如有帮助请订阅专栏!跟着老师每课一练,系统学习Axure交互设计课程! Axure产品经理精品视频课https://edu.csdn.net/course/detail/40420 课程主题:Axure菜单展开与收回 课程视频:...
6.9本日总结
一、英语 复习默写list11list18,订正07年第3篇阅读 二、数学 学习线代第一讲,写15讲课后题 三、408 学习计组第二章,写计组习题 四、总结 明天结束线代第一章和计组第二章 五、明日计划 英语:复习l默写sit12list17&#…...
构建Docker镜像的Dockerfile文件详解
文章目录 前言Dockerfile 案例docker build1. 基本构建2. 指定 Dockerfile 路径3. 设置构建时变量4. 不使用缓存5. 删除中间容器6. 拉取最新基础镜像7. 静默输出完整示例 docker runDockerFile 入门syntax指定构造器FROM基础镜像RUN命令注释COPY复制ENV设置环境变量EXPOSE暴露端…...
