python实现MC协议(SLMP 3E帧)的TCP服务端(篇二)
python实现MC协议(SLMP 3E帧)的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样,可以使用现成的pymodbus模块去实现。但是,我们可以根据协议帧进行组包,自己去实现帧的格式,而这一切可以基于socket模块。本文为第二篇。
二、读写保持寄存器的完整交互包
# 客户端发送(读) -》
50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 00 00 00 A8 05 00
# 《- 服务端应答
D0 00 00 FF FF 03 00 0C 00 00 00 73 00 00 00 00 00 00 00 00 00
# 客户端发送(写) -》
50 00 00 FF FF 03 00 16 00 10 00 01 14 00 00 0A 00 00 A8 05 00 4E 47 00 00 00 00 00 00 00 00
# 《- 服务端应答
D0 00 00 FF FF 03 00 02 00 00 00
1、分析交互包
基于上述交互包,我们查阅官方文档发现交互包使用的是二进制代码。那么,二进制代码与ASCII代码有什么区别呢?
SLMP(Seamless Message Protocol)3E帧有两种表示方式:二进制格式和ASCII格式。它们的区别在于数据的传输方式和呈现形式。
(1)二进制格式:
在二进制格式中,SLMP 3E帧中的各个字段(如帧头、副帧头、命令码、数据等)以二进制形式直接编码和传输。数据在网络中以原始的二进制位模式传输,这种方式效率较高,适用于网络传输。二进制格式通常用于实际的网络通信中,数据以二进制流的形式在网络上传输。
(2)ASCII格式:
在ASCII格式中,SLMP 3E帧中的各个字段被转换成ASCII字符表示。数据以ASCII码的文本形式进行传输,每个字节被转换为两个ASCII字符(通常是十六进制表示)。ASCII格式通常用于调试和人机界面中,方便人们查看和理解数据。
总的来说,二进制格式适用于机器之间的网络通信,而ASCII格式适用于人机交互和调试过程中的数据显示。选择哪种格式取决于具体的应用场景和需求。
因此,本文实现的是二进制格式,如果你会实现二进制格式,那么你也能实现ASCII格式。
2、读写保持寄存器的请求处理
(1)表头
客户端的两个请求,相同部分都为50 00 00 FF FF 03 00,我们姑且称之为表头。
(2)读/写长度(协议帧的长度)
0C 00是固定长度(读的时候报文都是这么长)与16 00 根据实际长度变化,表示后面数据的长度,例如前者,应该以00 0C来看长度,表示后面有12个00那样的长度。
(3)固定值
10 00
(4)读/写指令
01 04 / 01 14
(5)读/写寄存器地址
00 00 00 00 00 A8 05 00 / 00 00 0A 00 00 A8 05 00,其中写的0A 00代表从第10个保持寄存器,05表示读写5个寄存器
3、读写保持寄存器的响应处理
(1)表头
客户端的两个请求,相同部分都为D0 00 00 FF FF 03 00,我们姑且称之为表头。
(2)长度(协议帧的长度)
读:0C 00根据实际长度变化,写:02 00 可以不变化。
(3)固定值
00 00
(4)读/写响应
响应实际读到的数据 / 无
4、程序设计
根据上述内容,实现了一个定制MC服务器,能够处理保持寄存器的读写请求,给出正确的响应。
import socket
import struct# 创建一个TCP/IP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定套接字到特定地址和端口
server_address = ('192.168.1.188', 12345) # 服务器地址和端口
server_socket.bind(server_address)# 监听连接
server_socket.listen(1)print('等待客户端连接...')
connection, client_address = server_socket.accept()print('客户端已连接:', client_address)def request_verdict(req_bytes_frame): # req_bytes_frame是字节数据b'\x02\x00\x08\x00\x00\x00\x00\x00\x10\x00\x01\x01\x02\x03\x04\x03'command = req_bytes_frame.hex()[22:26] # 转成16进制字符串好数据处理if command in ["0104", "0401"]: # 判断读写return False # 读elif command in ["0114", "1401"]:return True # 写else:raise ValueError("读写指令错误!")def write_response_frame(req_bytes_frame):response = "D00000FFFF030002000000" # 写成功则返回这一串数据content = req_bytes_frame.hex()[42:] # 看一下客户端想写的内容print("客户端想要写入的内容:", bytes.fromhex(content).decode())return bytes().fromhex(response)def read_response_frame(req_bytes_frame, res_data):header = "D00000FFFF03000C000000" # 读的响应头nums = req_bytes_frame.hex()[38:42] # 获取客户端想要读的寄存器个数act_nums_hex = nums[2:] + nums[:2] # 涉及大端序和小端序,需要转一下act_nums = int(act_nums_hex, 16) # 得到实际数量res_data_hex = ''.join([hex(ord(c))[2:].zfill(2) for c in res_data]) # 将要返回的数据转成16进制字符串response = header + res_data_hex + '0'*(act_nums*2*2-len(res_data_hex)) # 根据请求数量返回对应的内容return bytes().fromhex(response)try:while True:# 接收客户端请求request = connection.recv(1024)print("001:", request)if request:flag = request_verdict(request)if flag: # 响应写response = write_response_frame(request)print("002:",response)else: # 响应读response = read_response_frame(request, "start")print("003:",response)connection.sendall(response)
finally:# 清理连接connection.close()
相关文章:

python实现MC协议(SLMP 3E帧)的TCP服务端(篇二)
python实现MC协议(SLMP 3E帧)的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样,可以使用现成的pymodbus模块去实现。但是,我们可以根据协议帧进行组包,自己去实现帧的格式,而这一切可以基于socket模…...

nodejs express uniapp 图书借阅管理系统源码
开发环境及工具: nodejs,mysql5.7,HBuilder X,vscode(webstorm) 技术说明: nodejs express vue elementui uniapp 功能介绍: 用户端: 登录注册 首页显示轮播图&am…...

从零开始的目标检测和关键点检测(一):用labelme标注数据集
从零开始的目标检测和关键点检测(一):用labelme标注数据集 1、可视化标注结果2、划分数据集3、Lableme2COCO,将json文件转换为MS COCO格式 前言:前段时间用到了mmlab的mmdetction和mmpose,因此以一个小的数…...

【JVM经典面试题(五十二道)】
文章目录 JVM经典面试题(五十二道)引言1.什么是JVM 内存管理2.能说一下JVM的内存区域吗?3.说一下JDK1.6、1.7、1.8内存区域的变化?4.为什么使用元空间替代永久代作为方法区的实现?5.对象创建的过程了解吗?6…...

高效管理:在文件夹名称左边添加关键字,实现批量重命名
在高效的文件管理中,对文件夹进行合理命名和重命名是十分关键的。有时候,我们可能需要在一批文件夹的名称左边添加特定的关键字,以便更好地组织和管理这些文件夹。为了实现这个目标,我们可以使用云炫文件管理器一些简单的步骤来实…...

Leetcode1122. 数组的相对排序
Every day a Leetcode 题目来源:1122. 数组的相对排序 解法1:哈希 用集合 set 存储 arr2 中的元素。 遍历数组 arr1 ,设当前元素为 num: 如果 num 在 set 中出现,用哈希表 hash 记录 num 和它出现的次数。否则&a…...

CN考研真题知识点二轮归纳(5)
本轮的最后一贴,真题中涉及计网的部分彻底总结完!后期的3轮总结可能会上一些大题,比如路由转发、子网划分什么的,以及重点的背诵内容~ 上期目录: CN考研真题知识点二轮归纳(4)https://jslhyh32…...

windows系统 生成RSA密钥对
在Windows系统上生成密钥对,可以使用多种方法,这里将介绍两种常用的方法: 方法1: 使用PuTTYgen PuTTYgen是PuTTY套件的一部分,是在Windows上生成SSH密钥对的一个流行工具。如果你的目的是SSH密钥对,你可以这样操作&a…...

大文件分片上传并发
我这边使用的是boostrap-fileimput 初始化文件上传框 $(document).ready(function () {$("#file-upload_import").fileinput({uploadUrl: "#",language: "zh", //设置语言showPreview: true,autoReplace: true,// uploadUrl: "/uact/uploa…...

数据结构——基于顺序表实现通讯录
一、. 基于动态顺序表实现通讯录 1.1 功能要求 1)⾄少能够存储100个⼈的通讯信息 2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等 3)增加联系⼈信息 4)删除指定联系⼈ 5)查找制定联系⼈ 6&…...

行业追踪,2023-11-03
自动复盘 2023-11-03 凡所有相,皆是虚妄。若见诸相非相,即见如来。 k 线图是最好的老师,每天持续发布板块的rps排名,追踪板块,板块来开仓,板块去清仓,丢弃自以为是的想法,板块去留让…...

JSPv2之El
(一)EL的基本语法 1优点 1 jsp的java太长了,el自己的语言${ 开始 }结束 2el直接返回空字符转,而java直接报错 3使用“lt”代替“<”运算符,如果运算符后面是数字,在运算符 *EL取值时,没有数组的下标越界,没有…...

出现 gpg: cancelled by user时的处理方法
今天在使用git commit -S -m "comment" check in 代码的时候, 莫名其妙出现了以下错误: gpg: cancelled by user经过在网上查询资料, 本质原因是GnuPG没有$(tty)的读写权限,有以下两种解决方法是靠谱的: c…...

MySQL中表的增删改查
目录 一、CRUD 二、新增(Create) (1)语法 (2)单行数据全列插入 (3)多行数据指定列插入 三、查询(Retrieve) (1)语法 …...

web.py python服务器两种模板template使用方法
【版权声明】 本文为博主原创文章,未经博主允许严禁转载,我们会定期进行侵权检索。 更多python应用或算法总结请关注我的博客:https://blog.csdn.net/suiyingy,或”乐乐感知学堂“公众号。 web.py是Python Web框架之一,…...

Flutter 01 目录结构入门
一、Flutter目录结构: 二、Flutter入口文件、入口方法: 三、Flutter Demo: demo1: import package:flutter/material.dart;//MaterialApp 和 Scaffold两个组件装饰App void main() {runApp(MaterialApp(home: Scaffold(appBar: A…...

Esxi安装OpenWrt
最近折腾下软路由主要就是实现局域网内的上网。 1.StarWind V2V Converter下载 先去下载个StarWind V2V Converter,觉得麻烦我在网上有找到一个博主的地址点击这里。 这是官网地址传送门,然后一阵乱输入点击下载 然后 双击之后无脑下一步即可。 2.Op…...

tuple 简易实现(C++ 模板元编程)
std::tuple 在标准库里面,tuple主要有下面四个类模板 or 函数模板 tupletuple_sizetuple_elementget 在后续有实现:tuple_size_v tuple_size::value和tuple_element_t tuple_element::type。 事例Example: auto tup std::tuple<in…...

Http代理与socks5代理有何区别?如何选择?(二)
上篇文章我们基本分别了解了http代理与socks5代理的定义与优缺点,接下来我们继续来了解http代理与socks5代理之间的比较与区别。 一、两者的比较 1、功能比较 HTTP代理专门用于Web流量,并在处理HTTP和HTTPS协议方面非常高效。它们可以修改正在传输的数…...

java中main方法和@Test注解的区别
Java的main方法和Test注解在用途和功能上有很大的区别。 main方法是Java应用程序的入口点。当你运行一个Java程序时,JVM会首先查找具有public static void main(String[] args)签名的类,并从这个方法开始执行程序。main方法通常用于控制程序的启动、执行…...

C++进阶语法——STL 标准模板库(下)(Standard Template Library)【学习笔记(七)】
文章目录 STL 代码示例1、迭代器2、算法3、array容器示例4、vector示例5、deque(double ended queue,双端数组)示例6、list(链表)容器7、set示例8、map示例9、stack 示例10、queue示例11、priority_queue (…...

力扣:求最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 示例1: 输入: strs ["flower", "flow" , "flight"] 输出: "fl" 示例2: 输入: strs ["dog","racecar","car&…...

Redis入门04-消息通知
目录 Redis中的消息通知 命令行操作 Redis中的管道 Redis中的消息通知 Redis可以用作消息队列的中间件,它提供了一种轻量级、高性能的消息传递机制,适用于实时通信、任务队列、事件处理等各种应用。以下是有关如何使用Redis作为消息队列的一些重要信…...

关于idea使用的一些操作设置
关于idea使用的一些操作设置 1. 常用的一下设置1.1 快捷键相关1.2 配置自动生成注释(类、方法等)1.3 maven项目相关1.4 常见其他的一些操作设置 2. IntelliJ IDEA 取消param注释中参数报错提示3. idea同时打开多个文件,导航栏不隐藏、自动换行…...

CLion 2023.2.2(C ++ IDE智能代码编辑器)
CLion 2023是一款跨平台C/C集成开发环境(IDE)。它为Mac用户提供了高效的编程体验,帮助程序员们在Mac平台上进行C/C开发。 CLion 2023支持多种编译器和调试器,并具有强大的代码分析和导航功能。它还为用户提供了许多便捷的工具和插…...

企业级API资产如何管理
在当今数字化时代,API已成为企业开发和创新的重要工具,如何高效地管理和调度这些资产成为了企业发展的重要课题。API资产管理的出现,为企业解决了这一难题,通过合理管理和利用API资产,企业可以更好地推动业务发展&…...

Git https方式拉的代码IDEA推送代码报错
报错信息 fatal: could not read Username for ‘https://codehub-cn-south-1.devcloud.huaweicloud.com’: No such file or directory 18:18:39.885: [recovery_pattern] git -c credential.helper -c core.quotepathfalse -c log.showSignaturefalse push --progress --porc…...

C++ capacity()用法总结
1. 容器的容量 容器的size是指它已经保存的元素的数目;而capacity则是在不分配新的内存空间的前提下它最多可以保存多少元素。capacity(),size(),reserve(),resize()这几个都是不太容易区分的函数 functionmeancapacity()获得容…...

TensorFlow2.0教程1-Eager
文章目录 张量极其操作1 张量自定义层一、网络层layer的常见操作二、实现自定义网络层三、网络层组合自动求导一、Gradient tapes二、记录控制流三、高阶梯度自定义训练(基础)一、Variables二、示例:拟合线性模型1、定义模型2.定义损失函数3.获取训练数据4.定义训练循环张…...

Redis学习系统(持续更新中)
RedisExample 课程介绍 目标是提供一个高效、可靠的学习和实践Redis的环境。我们将通过搭建Redis集群、实现缓存数据的持久化存储、制定缓存数据的淘汰策略以及同步缓存数据等步骤来深入了解和学习Redis的特性和功能。通过这个项目,你可以掌握Redis的核心概念和技…...