c# mudbus TCP协议

一、引言

建议先大致了解c# Socket通讯,之后再学习modbusTCP协议
ModbusTCP 协议概念:1996年施耐德公司推出基于以太网TCP/IP的Modbus协议。 Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。报文格式是协议的重点。 TCP 协议 知道格式就完事了,主要是格式里面的数据。

二、ModbusTCP 报文格式–读取保持寄存器

1、ModbusTCP报文格式:报文头+功能码+数据

① 报文头:服务器与客户端之间的“握手”,当一台服务器连接n台客户端,报文头能够保证服务器精准地给其中一客户端发送数据(或者服务器接收到数据,能够明白是哪一台客户端发送的)。
② 功能码:服务器与客户端交流是读取数据 还是写数据
③ 数据:读取的数据内容 或者要写数据的内容(前面一对乱七八糟 就是为了得到这个玩意)

2、报文头

报文头字节长度作用
事务标志符2个字节Modbus 请求/响应事务(一般为16进制 00,00 )
协议标志符2个字节Modbus 协议(一般为00,00)
长度 标志符2个字节单元标志符+功能码+数据 总共的字节数量(知道字节数,可以知道数据是否丢失)
单元标志符1个字节总线的从站识别(相当于给每一个客户端一个号码/地址,知道你是谁)

一般格式为:

事务协议长度单元标志
0x00,0x000x00,0x000x00,0x060x01

解释:报文格式均为16进制
事务/协议:0x00000000
长度:0x06 — 6个字节–后面的报文的字节总数量 :单元1+功能码1+地址2+线圈数量2
单元标识:0x01 —客户端号码/地址为1

2、功能码

Modbus的操作对象有四种:线圈、离散输入、保持寄存器、输入寄存器。占用一个字节

功能码含义
0x01读线圈
0x05写单个线圈
0x0F写多个线圈
0x02读离散量输入
0x04读输入寄存器
0x03读保持寄存器
0x06写单个保持寄存器
0x10写多个保持寄存器

不同的操作对象,报文格式不同,这里下面的主要介绍0x03 读保持寄存器----就是可以读取数据。
明白其中一种操作对象报文格式,其它功能码内容类似。

3、数据(寄存器)

读取保持寄存器报文又分为发送报文返回报文两种
例如当你给服务器发送数据,这个数据格式是按照读保持寄存器报文格式发送的
服务器接收到你发的信息,就明白你是来读取数据的,之后就会回复给你信息,因为你发送很规范,服务器回复你的时候就会按照的你报文格式回复(返回报文格式

发送报文格式如下:
起始地址低位、起始地址高位、线圈数量高位、线圈数量低位均占一个字节
事务协议缩写 0x00000000,实际为:0x00,0x00,0x00,0x00
长度缩写0x0006,实际为:0x00,0x06

--------------报文头--------------功能------------------------数据--------------------------
事务协议长度单元标志功能码起始地址高位起始地址低位数量高位数量低位
0x000000000x00060x010x030x000x6B0x000x02

发送报文含义:
读取1号服务器保持寄存器(数据),数据起始地址0x6B=107,对应地址40108,读取寄存器数量0x02=2,即读取1号数据,地址从40108-40109,共两个寄存器数组(4个字节)。
为什么地址是从40000开始,因为读保持寄存器地址为0x03;前面有0x00、0x01、0x02。

返回报文格式如下:
事务协议缩写 0x00000000,实际为:0x00,0x00,0x00,0x00
长度缩写0x0006,实际为:0x00,0x06

--------------报文头--------------功能----------------------------------数据-----------------------------------
事务协议长度单元标志功能码字节数量1寄存器高位1寄存器低位2寄存器高位2寄存器低位
0x000000000x00070x010x030x040x000x010x000x02

返回报文含义:
返回服务器1号从站保持寄存器40108-40109,共两个寄存器的数值,返回的字节数为4个
40108对应数值为0x0001,40109对应数值为0x00109;转换为10进制分别为1和2;
即读取到数据为1 和 2

三、C# ModbusTCP编程

1、通讯连接

public bool Connect()
{
    //实例化
    tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    try
    {
         //连接
         tcpClient.Connect(System.Net.IPAddress.Parse(IPAddress), Port);
    }
    catch(Exception ex)
    {
         //日志处理
          return false;
    }
    return true;
}

2、断开通讯

public void DisConnect()
{
    if(tcpClient !=null)
    {
        tcpClient.Close();
    }
}

2、读取保持型寄存器

 public ushort[] ReadHoldingRegisters(ushort start,ushort length)
 {
            //五步走

            //1、拼接报文

            //集合放整个报文码
            List<byte> SendCommand = new List<byte>();

            //事务 协议
            SendCommand.AddRange(new byte[] { 0, 0, 0, 0 });

            //长度--两个字节  
            SendCommand.AddRange(new byte[] { 0, 6 });

            //单元标志符
            SendCommand.Add(SlaveId);

            //功能码
            SendCommand.Add(0x03);

            //数据 起始寄存器地址+寄存器数量 两个字节组成

            //起始寄存器地址
            SendCommand.Add((byte)(start / 256));  //获得高位  一个字节8位 1111 1111为255    寄存器地址由两个字节组成 例如:1234 5678 0000 0000  高位:1234 5678
            SendCommand.Add((byte)(start % 256));  //获得低位  1234 5678 0000 0000  低位:0000 0000

            //读寄存器数量
            SendCommand.Add((byte)(length / 256));
            SendCommand.Add((byte)(length% 256));

            //2、发送报文
            //SendCommand.ToArray() 集合变数值
            tcpClient.Send(SendCommand.ToArray());

            //3、接收报文
            byte[] buffer = new byte[512];
            //Receive(buffer) 返回字节的长度
            int count = tcpClient.Receive(buffer);

            //4、验证报文 读二个寄存器 返回长度7 读三个寄存器返回9
            if(count==2*length+9)
            {
                //获取真正的报文 字节数组的截取
                byte[] des = new byte[count];

                //将buffer复制给des 
                Array.Copy(buffer, 0, des, 0, count);

                //返回的报文 des[0]--des[5] 前6个字节为 事务2个字节 协议2个字节 长度2个字节
                //接下来是 单元标识1个字节  功能码1个字节 字节计数1个字节
                //二次验证 des[6]为单位识别码;des[5]为高位识别码  des[7]为功能码 des[8]为字节计数
                if (des[6] == SlaveId && des[7] == 0x03&&des[8]==2*length)
                {
                    //5、解析报文

                    //字节数组截取
                    byte[] res = new byte[count]; 
                    //将des复制给res  des[9] 第九位为信息 高位和低位
                    Array.Copy(des, 9, res, 0, 2 * length);

                    //解析  BitConverter.ToUInt16()将字节数组转换unint无符号的整数

                    List<ushort> result = new List<ushort>();
                    for (int i = 0; i < res.Length; i+=2)
                    {
                        result.Add(Convert.ToUInt16( res[i]*256 + res[i+1]));
                        //result.Add(BitConverter.ToUInt16(res, i));
                    }
                    return result.ToArray();           
                }
            }
            return null;
}
  • 9
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值