网络高级(学习)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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[项目实战]EOS多节点部署

文章总览&#xff1a;YuanDaiMa2048博客文章总览 EOS多节点部署 &#xff08;一&#xff09;环境设计&#xff08;二&#xff09;节点配置&#xff08;三&#xff09;区块信息同步&#xff08;四&#xff09;启动节点并验证同步EOS单节点的环境如何配置 &#xff08;一&#xf…

第十一周:机器学习

目录 摘要 Abstract 一、字符级的RNN进行名字分类 1、准备数据 2、构造神经网络 3、训练 4、评价结果 5、预测 二、字符级的RNN生成名字 1、准备数据 2、构造神经网络 3、训练 4、网络采样&#xff08;预测&#xff09; 三、batch normalization 1、 feature n…

Bootstrap 警告信息(Alerts)使用介绍

本章将讲解警告&#xff08;Alerts&#xff09;以及 Bootstrap 所提供的用于警告的 class。警告&#xff08;Alerts&#xff09;向用户提供了一种定义消息样式的方式。它们为典型的用户操作提供了上下文信息反馈。 您可以为警告框添加一个可选的关闭按钮。为了创建一个内联的可…

【工具箱】NAND NOR FLASH闪存

随着国内集成电路的发展&#xff0c;特别是存储芯片方面&#xff0c;关于NOR Flash&#xff0c;NAND Flash&#xff0c;SD NAND, eMMC, Raw NAND的资料越来越多了。这里我专门写了这篇文章&#xff1a;1&#xff0c;把常用的存储产品做了分类; 2&#xff0c;把这些产品的特点做…

[Postman]接口自动化测试入门

文章大多用作个人学习分享&#xff0c;如果大家觉得有不足或错漏的地方欢迎评论指出或补充 此文章将完整的走一遍一个web页面的接口测试流程 大致路径为&#xff1a; 创建集合->调用接口登录获取token->保存token->带着token去完成其他接口的自动化测试->断言-&g…

Kafka下载与安装教程(国产化生产环境无联网服务器部署实操)

请放心观看&#xff0c;已在正式环境部署验证&#xff0c;流程无问题&#xff01; 所用系统为国产化麒麟银河 aarch64系统&#xff0c;部署时间2024年9月份&#xff01; [rootecs-xxxxxx-xxx ~]# cat /etc/os-release NAME"Kylin Linux Advanced Server" VERSION&q…

MySQL 查询数据库的数据总量

需求&#xff1a;查看MySQL数据库的数据总量&#xff0c;以MB为单位展示数据库占用的磁盘空间 实践&#xff1a; 登录到MySQL数据库服务器。 选择你想要查看数据总量的数据库&#xff1a; USE shield;运行查询以获取数据库的总大小&#xff1a; SELECT table_schema AS Datab…

go语言后端开发学习(七)——如何在gin框架中集成限流中间件

一.什么是限流 限流又称为流量控制&#xff08;流控&#xff09;&#xff0c;通常是指限制到达系统的并发请求数。 我们生活中也会经常遇到限流的场景&#xff0c;比如&#xff1a;某景区限制每日进入景区的游客数量为8万人&#xff1b;沙河地铁站早高峰通过站外排队逐一放行的…

JAVA毕业设计170—基于Java+Springboot+vue3+小程序的房屋租赁小程序系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootvue3小程序的房屋租赁小程序系统(源代码数据库)170 一、系统介绍 本项目前后端分离(可以改为ssm版本)&#xff0c;分为用户、房东、管理员三种角色 1、用户&am…

[000-002-01].第03节:Linux系统下Oracle的安装与使用

2.1.Docker安装Oracle 在CentOS7中使用Docker安装Oracle&#xff1a; 1.安装Docker,详细请参考&#xff1a;https://blog.csdn.net/weixin_43783284/article/details/1211403682.拉取镜像&#xff1a; docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g3.下载…

删除有序数组中的重复项(同向指针(快慢指针))

题目&#xff1a; 算法分析&#xff1a; 快慢指针从0出发若快慢指针不相同&#xff0c;快指针替换慢指针&#xff08;即慢指针后一位&#xff09;快指针每次都会增加题目求不重复的元素个数&#xff08;slow 为对应元素索引&#xff0c;故个数为slow1&#xff09; 算法图解…

如何通过编程工具提升工作效率

目录 常用编程工具介绍 工具效率对比 未来发展趋势 结论 在当今软件开发的高效环境中&#xff0c;工具的选择至关重要。无论是编写代码、调试&#xff0c;还是协作开发&#xff0c;合适的编程工具不仅能够简化开发流程&#xff0c;还可以有效地提高生产力。开发者在日常工作…

声波的波数,通常用k表示

声波的波数&#xff08;通常用 k 表示&#xff09;是描述声波空间变化的一个参数&#xff0c;它与声波的频率和介质中的传播速度有关。波数 k 是一个具体值&#xff0c;并且在均匀介质中它是固定的&#xff0c;计算公式如下&#xff1a; 均匀介质中的波数是一个具体值&#xff…

ESP8266+httpServer+GET+POST实现网页验证密码

1. 代码 #include "esp_http_server.h" #include "esp_log.h" #include "web_server.h"// 辅助宏&#xff0c;用于计算两个数中的较小值 #define MIN(a, b) ((a) < (b) ? (a) : (b))static const char *TAG "wifi web_server";c…

安卓BLE蓝牙通讯

蓝牙测试demo 简介   Android手机间通过蓝牙方式进行通信&#xff0c;有两种常见的方式&#xff0c;一种是socket方式&#xff08;传统蓝牙&#xff09;&#xff0c;另一种是通过GATT&#xff08;BLE蓝牙&#xff09;。与传统蓝牙相比&#xff0c;BLE 旨在大幅降低功耗。这样…

华为OD机试 - 推荐多样性(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

气压测试实验(用IIC)

I2C: 如果没有I2c这类总线&#xff0c;连接方法可能会如下图&#xff1a; 单片机所有的通讯协议&#xff0c;无非是建立在引脚&#xff08;高低电平的变换高低电平持续的时间&#xff09;这二者的组合上&#xff0c;i2c 多了一个clock线&#xff0c;负责为数据传输打节拍。 (i2…

同时拥有独显和核显,怎么让应用程序选择使用哪个GPU?

看你现在使用的是核显还是独显 勾选上GPU引擎选项&#xff0c;后面便会标识你所使用的是哪种显卡&#xff0c;如果是独立显卡&#xff0c;就可以免去后续的操作&#xff1b;如果不是&#xff0c;那么请继续接下来的操作。 将你需要使用独显的程序换成gpu1&#xff08;独显&am…

佰朔资本:未来钢铁行业产业格局有望稳中趋好

组织指出&#xff0c;未来钢铁作业工业格式有望稳中趋好&#xff0c;叠加当时部分公司已经处于价值小看区域&#xff0c;现阶段仍具结构性出资机会&#xff0c;尤其是拥有较高毛利率水平的优特钢企业和本钱管控力度强、具有规划效应的龙头钢企&#xff0c;未来存在估值修改的机…

JavaEE:文件操作

文章目录 文件操作和IO文件系统操作File介绍属性构造方法方法 代码演示前四个listmkdirrenameTo 文件操作和IO 文件系统操作 创建文件,删除文件,创建目录,重命名… Java中有一个类,可以帮我们完成上述操作. 这个类叫做File类. File介绍 属性 这个表格描述了文件路径的分隔符…