
本文旨在解决go语言在实现modbus tcp客户端时常见的连接重置和接收到空响应的问题。核心在于理解modbus tcp协议的请求格式与串行modbus的区别,并强调应使用go标准库net.conn提供的低级别write和read方法进行精确的字节流控制,避免使用可能导致数据格式化错误或不当读取行为的高级别i/o函数,确保请求正确发送和响应准确接收。
在Go语言中开发网络应用程序时,特别是在处理如MODBUS TCP这类基于字节流的工业协议时,开发者常会遇到诸如“connection reset by peer”错误或接收到空响应的问题。这些问题往往源于对协议帧格式的误解以及对Go语言网络I/O函数的不当使用。本文将深入探讨这些问题,并提供一个健壮的MODBUS TCP客户端实现方案。
MODBUS TCP与传统的MODBUS RTU(串行通信)在协议帧结构上存在显著差异。MODBUS TCP在MODBUS PDU(协议数据单元)前添加了一个MBAP(MODBUS Application Protocol)头,而MODBUS RTU则使用CRC校验码。忽略这一区别是导致通信失败的常见原因。
一个典型的MODBUS TCP请求帧(如读取单个保持寄存器)通常由以下部分组成:
例如,读取从站地址0x01的第0x0001个寄存器(数量为0x0001)的请求字节序列为: 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01 其中,0x00, 0x06表示后续有6个字节。
在处理原始字节流协议时,fmt.Fprintf和ioutil.ReadAll等高级别I/O函数可能不是最佳选择,甚至可能引入问题。
Yaara
使用AI生成一流的文案广告,电子邮件,网站,列表,博客,故事和更多…
95
查看详情
为了确保发送和接收的数据与协议规范严格一致,推荐使用net.Conn接口提供的低级别Write和Read方法。
以下是一个使用Go语言实现MODBUS TCP客户端的示例,它演示了如何正确构建请求、发送数据以及接收响应。
package main
import (
"fmt"
"net"
"time" // 引入time包用于设置超时
)
// TCP MODBUS客户端示例
func main() {
// 目标MODBUS TCP服务器地址和端口
serverAddr := "192.168.98.114:502" // 替换为你的设备IP和端口
// 1. 建立TCP连接
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
fmt.Printf("Error dialing %s: %v\n", serverAddr, err)
return
}
defer conn.Close() // 确保连接在使用完毕后关闭
// 可以设置读写超时,避免长时间阻塞
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
// 2. 构造MODBUS TCP请求帧
// 示例:读取从站地址0x01的第1个保持寄存器(数量1)
// [Transaction ID] [Protocol ID] [Length] [Unit ID] [Function Code] [Starting Address] [Quantity]
// 0x00, 0x00 0x00, 0x00 0x00, 0x06 0
x01 0x03 0x00, 0x01 0x00, 0x01
request := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01}
// 3. 发送请求
n, err := conn.Write(request)
if err != nil {
fmt.Printf("Error writing request: %v\n", err)
return
}
fmt.Printf("Sent %d bytes request: %X\n", n, request)
// 4. 计算预期响应长度
// MODBUS TCP响应帧结构:
// MBAP头 (6字节) + Unit ID (1字节) + Function Code (1字节) + Byte Count (1字节) + Data (N字节)
// 对于读取1个16位寄存器 (Function Code 0x03),数据部分是2字节。
// 所以总长度 = 6 (MBAP) + 1 (Unit ID) + 1 (Function Code) + 1 (Byte Count) + 2 (Data) = 11字节
numRegs := 1 // 请求读取的寄存器数量
expectedResponseLen := 9 + 2 * numRegs // 9字节固定部分 + 2字节/寄存器
// 5. 创建缓冲区并接收响应
response := make([]byte, expectedResponseLen)
n, err = conn.Read(response)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
return
}
// 6. 处理接收到的响应
fmt.Printf("Received %d bytes response: %X\n", n, response[:n])
// 进一步解析响应数据
if n >= 9 { // 确保至少有MBAP头、Unit ID、Function Code和Byte Count
// 检查功能码是否是错误响应(功能码+0x80)
if response[7] == (request[7] | 0x80) {
fmt.Printf("MODBUS Error Response: Exception Code %02X\n", response[8])
} else if response[7] == request[7] { // 检查功能码是否匹配
byteCount := int(response[8])
if n == 9 + byteCount {
fmt.Printf("Data (raw): %X\n", response[9:9+byteCount])
// 假设读取的是16位整数
for i := 0; i < byteCount; i += 2 {
if i+1 < byteCount {
value := uint16(response[9+i])<<8 | uint16(response[9+i+1])
fmt.Printf("Register %d Value: %d (0x%X)\n", i/2+1, value, value)
}
}
} else {
fmt.Printf("Incomplete or malformed response data. Expected %d bytes, got %d.\n", expectedResponseLen, n)
}
} else {
fmt.Printf("Unexpected function code in response: %02X\n", response[7])
}
} else {
fmt.Printf("Response too short for MODBUS TCP: %d bytes\n", n)
}
}在Go语言中实现MODBUS TCP客户端,关键在于深刻理解MODBUS TCP协议的字节帧结构,并采用低级别的net.Conn.Write和net.Conn.Read方法进行精确的字节流控制。避免使用可能引入不必要格式化或不当读取行为的高级别I/O函数。通过正确的请求构建、严格的错误处理、合理的连接管理和超时设置,可以有效避免“connection reset by peer”和空响应等常见问题,从而构建出稳定可靠的MODBUS TCP通信应用程序。
以上就是Go语言实现MODBUS TCP客户端:避免连接重置与空响应的实践指南的详细内容,更多请关注其它相关文章!
相关文章:
神庙逃亡小游戏在线玩 神庙逃亡小游戏入口
AO3最新官网入口公告_2025AO3镜像站实时查询方法
composer的"require-dev"部分是用来做什么的?
MAC怎么让Dock栏只显示当前运行的应用_MAC终端命令实现极简Dock栏
Tabulator表格日期时间排序问题及自定义解决方案
steam官方入口大全 steam账号注册及操作指南
新手怎么开始学化妆 零基础化妆入门教程
Animex动漫社网入口地址 Animex动漫社网正版在线入口
抖音极速版最新版本 抖音极速版官方下载地址
Lar*el Excel导入时生成自定义递增ID的策略与实践
必由学官方平台入口 必由学在线课堂登录地址
Selenium Python中处理点击后新窗口加载冻结问题的策略与实践
夸克浏览器桌面版同步不了书签怎么处理 夸克浏览器跨设备同步异常解决方案
钉钉视频会议声音异常如何处理 钉钉会议音频修复技巧
三星ZFold5多任务卡顿_Samsung ZFold5流畅度提升
QQ邮箱网页版入口登录 QQ邮箱在线邮箱官方通道
Win11怎么隐藏桌面图标 Win11一键隐藏所有桌面元素及恢复显示
CKEditor 5 自定义构建在React应用中渲染失败的调试与解决
电脑屏幕颜色不舒服怎么办_Windows夜间模式与色彩校准教程【护眼技巧】
msn官网入口地址手机版 msn官方网站手机最新链接
实现全屏滚动与导航点:专业教程
SteamMachine定价或为699美元 大家想入手吗?
PyTorch模型训练准确率不提升:诊断与修复常见指标计算错误
汽水音乐车机版8.9下载 汽水音乐车机版8.9版本安装入口
LINUX的perf命令入门_LINUX官方性能分析工具的使用与解读
LocoySpider如何部署到云服务器_LocoySpider云部署的远程配置
在Blazor WebAssembly应用中动态注入客户端特定指标代码的策略
支付宝如何设置安全保护_支付宝安全设置的全面教程
PySpark中从现有列右侧提取可变长度字符创建新列的教程
处理Kafka消息时会话超时与实现幂等性消费者
汽水音乐在线解析 汽水音乐在线解析入口
163邮箱注册官网 免费申请163个人邮箱
Composer的 "licenses" 命令如何帮助你遵守开源协议_检查项目依赖的许可证合规性
PHP表单数据传递:如何通过隐藏输入字段获取动态ID
PySpark中高效提取字符串右侧可变长度数字:使用regexp_extract
在Typer应用中优雅地处理和重组任意命令行参数
不同用户不同价格! 索尼开启账户个性化定价测试
提升Kafka消费者健壮性:会话超时处理与消息处理语义
Shopware订单对象中获取产品自定义字段的正确方法
小猿搜题在线学习页面在哪_小猿搜题在线学习中心入口
企业名称高精度匹配:N-gram方法在结构相似性分析中的应用
C#中解析不规范的HTML为XML 常见的坑与解决办法
age动漫网站入口 age动漫官网直接访问入口
J*aScript中如何高效提取对象指定属性
J*a初级项目如何接入API数据_第三方接口请求与响应解析
PS5 Pro有点优势但不多! 《燕云十六声》PS5平台与PC性能画面对比
PHP中SSG-WSG API的AES加密实践:正确使用初始化向量
QQ官网正版登录链接 QQ在线登录入口最新
怎么搭建一个php网站源码_搭php网站源码搭建教程
Win11怎么开启省电模式_Win11电池节电模式自动开启