基于WebSocket的modbus通信(一)- 服务器

ModbusTcp协议是基于tcp的,但不是说一定要通过tcp协议才能传输,只要能传输二进制的地方都可以。比如WebSocket协议。
但由于目前我只有tcp上面的modbus服务器实现,所以我必须先用tcp连接借助已有工具来验证我的服务器是否写正确。

效果

基于WebSocket的modbus通信(一)- 服务器

基于WebSocket的modbus通信(一)- 服务器

ModBusTCP协议报文

ModBusTCP协议报文比较复杂,主要是区分了3组类型

  • 请求和响应
  • 读和写
  • 多个数据和单个数据

理论上将报文格式的种类从1种变成了8种。
但是在响应读数据时,没有区分数据量,而是到请求中去查找,听说是为了减少传输量。因此实际格式种类是3组6种。

  1. 请求读数据报文

    事务标识符 协议标识符 报文长度 单元标识符 功能码 起始地址 读取数量
    字段长度 2 byte 2 byte 2 byte 1 byte 1 byte 2 byte 2 byte
    数据类型 ushort ushort ushort byte byte ushort ushort
    自增 一般为0 6,从单元标识符开始算 一般为0 链接
  2. 响应读数据报文

    事务标识符 协议标识符 报文长度 单元标识符 功能码 数据字节数 数据
    2 byte 2 byte 2 byte 1 byte 1 byte 1 byte n byte

    值得注意的是,响应报文中没有包含请求的数据数量,只能从请求中获得。比如是7个线圈还是8个线圈是看不出来的。
    这其实就限制了程序结构,应该在同一个上下文中声明请求和响应变量。

  3. 请求写单个数据报文

    事务标识符 协议标识符 报文长度 单元标识符 功能码 起始地址 数据
    2 byte 2 byte 2 byte 1 byte 1 byte 2 byte 2 byte
  4. 响应写单个数据报文(不需要响应)

    事务标识符 协议标识符 报文长度 单元标识符 功能码 起始地址 数据
    2 byte 2 byte 2 byte 1 byte 1 byte 2 byte 2 byte

    请求写单个数据的响应实际上是原样返回请求。
    写1位线圈和写16位寄存器都传输了2个字节,这很奇怪。
    原来是ModBusTCP定义写单个线圈用0xff00表示1,0x0000表示0。

  5. 请求写多个数据报文

    事务标识符 协议标识符 报文长度 单元标识符 功能码 起始地址 数量 字节数 数据
    2 byte 2 byte 2 byte 1 byte 1 byte 2 byte 2 byte 1 byte n byte

    请求写多个数据时,如果是线圈,现在又变成了一位代表一个数据,而不是原来的2字节代表单个数据
    还需要注意的是modbus采用大端传输,意味着一个字节中如果写了四个线圈,要从右向左数。

  6. 响应写多个数据报文(不需要响应)

    事务标识符 协议标识符 报文长度 单元标识符 功能码 起始地址 数量
    2 byte 2 byte 2 byte 1 byte 1 byte 2 byte 2 byte

报文类定义

从功能码字段往后,字段不确定,所以统一用Data表示。
在各个响应方法里根据不同功能码对数据进行处理。

//WebModbus.cs  // Modbus报文 public class ADUMessage {     // 事务标识符     public ushort Transaction { get; set; }     // 协议标识符     public ushort Protocol { get; set; }     // 报文长度     public ushort Length { get; set; }     // 单元标识符     public byte Unit { get; set; }     // 功能码     public byte FunctionCode { get; set; }     // 数据     public byte[] Data { get; set; }      public static ADUMessage Deserialize(byte[] buffer){}      public static byte[] Serialze(ADUMessage message){} } 

数据模型

modbusTCP协议规定了服务器实现线圈、寄存器2种数据模型,每种又分为只读、可读可写。于是服务器一共要提供4个存储栈。
因为报文起始地址字段有2个字节,所以寻址范围是0-65535

栈地址 线圈栈 离散量输入栈 只读寄存器栈 读写寄存器栈
65535 0 0 0xFF C1 0x00 00
65534 0 1 0x00 00 0x00 00
... 1 0 0x00 00 0x00 00
1 0 0 0x00 00 0x08 00
0 0 0 0x00 00 0x00 1A

其中线圈栈和离散量输入栈是bool类型的,实际实现的时候可以是数字0、1,也可以是布尔true,false,还可以是字符串"on","off"
如何存储,存储的数据类型是什么?这不重要。重要的是传输的时候采用规定格式就行。

  • 传输格式
    • 采用第3种报文写单线圈时,如果要传输true,要使用0xFF 00。传输false,要使用0x00 00

    • 其它时候都用一个bit位表示true和false
      比如线圈栈,从地址0开始读9个数据。就是小端字节序0x0f 00。

      0 1 2 3 4 5 6 7 8 ...
      1 1 1 1 0 0 0 0 0 0

      当然,要从请求报文中知道截取取响应数据的哪几位。

  • 模型类定义
//WebModbus.cs  // 数据仓库,144KB public class DataStore {     // 读写16位寄存器,64KB     public ushort[] HoldingRegisters;     // 只读16位寄存器,64KB     public ushort[] InputRegisters;     // 读写1位线圈,8KB     public bool[] CoilDiscretes;     // 只读1位线圈,8KB     public bool[] CoilInputs;      public DataStore()     {         HoldingRegisters = new ushort[65536];         InputRegisters = new ushort[65536];         CoilDiscretes = new bool[65536];         CoilInputs = new bool[65536];     }      // 读 读写16位寄存器     public ushort[] ReadHoldingRegisters(ushort startIndex, ushort length){}     // 读 只读16位寄存器     public ushort[] ReadInputRegisters(ushort startIndex, ushort length){}     // 读 读写1位线圈     public bool[] ReadCoilDiscretes(ushort startIndex, ushort length){}     // 读 只读1位线圈     public bool[] ReadCoilInputs(ushort startIndex, ushort length){}     // 写 读写16位寄存器     public void WriteHoldingRegisters(ushort startIndex, ushort[] data){}     //写 读写1位线圈     public void WriteCoilDiscretes(ushort startIndex, bool[] data){} } 

功能码

功能码主要用来操作这4个栈,因为栈分读写和只读,写入还分为多个数据和单个数据。所以主要有8种功能码

  • 0x01 读线圈栈
  • 0x02 读离散量输入栈
  • 0x03 读只读寄存器栈
  • 0x04 读读写寄存器栈
  • 0x05 写一个数据到线圈栈
  • 0x06 写一个数据到读写寄存器栈
  • 0x0F 写多个数据到线圈栈
  • 0x10 写多个数据到读写寄存器栈
  • 当然还有其它的,但不影响主要功能,这里就不说明了

相应的

字节序大小端

字节序这个东西还是当初上操作系统课时老师讲过,以后再没接触。不过要自己处理字节流时又面临这个问题了。
modbusTcp主要采用大端字节序方式传输数据。
为什么说主要呢,因为唯独线圈数据是采用小端的方式传输的,其它数据都是大端传输。这要特别注意。

  • C#实现时可以使用BinaryReaderBinaryWriter来处理接收到的和要传输的字节流。
    但这两个类默认都是按照小端字节序来处理数据。比如reader.ReadUInt16(),如果我们传的字节流是0x00 01,它会以为是ushort 256,而不是ushort 1。
    所以需要我们重载override这个方法,翻转字节数组,按照大端方式读取。
//BigEndianBinaryReader.cs  public override short ReadInt16() {     var data = base.ReadBytes(2);     Array.Reverse(data);     return BitConverter.ToInt16(data, 0); } 

服务器处理流程

modbus服务器要做的就是

  • 接收消息,反序列化为消息对象

  • 然后判断怎么响应

  • 读取数据栈,构造消息

  • 序列化后响应

  • 核心方法

//WebModbus.cs  public ADUMessage HandleRequest(byte[] buffer) {     ADUMessage request = ADUMessage.Deserialize(buffer);     switch (request.FunctionCode)     {         //读 读写线圈         case 0x01:             return Response_01(request);         //读 只读线圈         case 0x02:             return Response_02(request);         //读 读写寄存器         case 0x03:             return Response_03(request);         //读 只读寄存器         case 0x04:             return Response_04(request);         //写 读写一个线圈         case 0x05:             return Response_05(request);         //写 读写一个寄存器         case 0x06:             return Response_06(request);         //写 读写多个线圈         case 0x0f:             return Response_0f(request);         //写 读写多个寄存器         case 0x10:             return Response_10(request);         default:             return Response_01(request);     } } 

完整代码

程序所需命令行参数的一种方式是在项目文件种指定,这在调试时比较方便

<PropertyGroup> 	<StartArguments>5234</StartArguments> </PropertyGroup> 
WebModbus.cs
/// <summary> /// 数据仓库,144KB /// </summary> public class DataStore {     /// <summary>     /// 读写16位寄存器,64KB     /// </summary>     public ushort[] HoldingRegisters;     /// <summary>     /// 只读16位寄存器,64KB     /// </summary>     public ushort[] InputRegisters;     /// <summary>     /// 读写1位线圈,8KB     /// </summary>     public bool[] CoilDiscretes;     /// <summary>     /// 只读1位线圈,8KB     /// </summary>     public bool[] CoilInputs;      public DataStore()     {         HoldingRegisters = new ushort[65536];         InputRegisters = new ushort[65536];         CoilDiscretes = new bool[65536];         CoilInputs = new bool[65536];     }        /// <summary>     /// 读 读写16位寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public ushort[] ReadHoldingRegisters(ushort startIndex, ushort length)     {         return HoldingRegisters.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();     }     /// <summary>     /// 读 只读16位寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public ushort[] ReadInputRegisters(ushort startIndex, ushort length)     {         return InputRegisters.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();     }     /// <summary>     /// 读 读写1位线圈     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public bool[] ReadCoilDiscretes(ushort startIndex, ushort length)     {         return CoilDiscretes.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();     }     /// <summary>     /// 读 只读1位线圈     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public bool[] ReadCoilInputs(ushort startIndex, ushort length)     {         return CoilInputs.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();     }     /// <summary>     /// 写 读写16位寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="data"></param>     public void WriteHoldingRegisters(ushort startIndex, ushort[] data)     {         for (int i = 0; i < data.Length; i++)         {             if (startIndex+i < 65536)             {                 HoldingRegisters[startIndex + i] = data[i];             }         }     }     /// <summary>     /// 写 读写1位线圈     /// </summary>     /// <param name="startIndex"></param>     /// <param name="data"></param>     public void WriteCoilDiscretes(ushort startIndex, bool[] data)     {         for (int i = 0; i < data.Length; i++)         {             if (startIndex + i < 65536)             {                 CoilDiscretes[startIndex + i] = data[i];             }         }     } }  /// <summary> /// Modbus报文 /// </summary> public class ADUMessage {     /// <summary>     /// 事务标识符     /// </summary>     public ushort Transaction { get; set; }     /// <summary>     /// 协议标识符     /// </summary>     public ushort Protocol { get; set; }     /// <summary>     /// 报文长度     /// </summary>     public ushort Length { get; set; }     /// <summary>     /// 单元标识符     /// </summary>     public byte Unit { get; set; }     /// <summary>     /// 功能码     /// </summary>     public byte FunctionCode { get; set; }     /// <summary>     /// 数据     /// </summary>     public byte[] Data { get; set; }      public static ADUMessage Deserialize(byte[] buffer)      {         //BinaryReader读取方式是小端(右边是高字节),而modbus是大端传输(左边是高字节)         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(buffer));         ADUMessage adu = new ADUMessage()         {             Transaction = reader.ReadUInt16(),             Protocol = reader.ReadUInt16(),             Length = reader.ReadUInt16(),             Unit = reader.ReadByte(),             FunctionCode = reader.ReadByte(),             Data = reader.ReadBytes(buffer.Length - 8)         };         return adu;     }      public static byte[] Serialze(ADUMessage message)     {         using (MemoryStream ms=new MemoryStream())         {             BinaryWriter writer = new BigEndianBinaryWriter(ms);             writer.Write(message.Transaction);             writer.Write(message.Protocol);             writer.Write(message.Length);             writer.Write(message.Unit);             writer.Write(message.FunctionCode);             writer.Write(message.Data);             return ms.ToArray();         }     } }  public class WebModbusServer {     public DataStore store = new DataStore();      public ADUMessage HandleRequest(byte[] buffer)     {         ADUMessage request = ADUMessage.Deserialize(buffer);         switch (request.FunctionCode)         {             //读 读写线圈             case 0x01:                 return Response_01(request);             //读 只读线圈             case 0x02:                 return Response_02(request);             //读 读写寄存器             case 0x03:                 return Response_03(request);             //读 只读寄存器             case 0x04:                 return Response_04(request);             //写 读写一个线圈             case 0x05:                 return Response_05(request);             //写 读写一个寄存器             case 0x06:                 return Response_06(request);             //写 读写多个线圈             case 0x0f:                 return Response_0f(request);             //写 读写多个寄存器             case 0x10:                 return Response_10(request);             default:                 return Response_01(request);         }     }      public byte[] CoilToBytes(bool[] bools)     {         int byteCount = (bools.Length + 7) / 8; // 计算所需的字节数         byte[] bytes = new byte[byteCount];          for (int i = 0; i < bools.Length; i++)         {             int byteIndex = i / 8; // 计算当前布尔值应该存储在哪个字节中             int bitIndex = i % 8; // 计算当前布尔值应该存储在字节的哪个位上              if (bools[i])             {                 // 设置对应位为 1                 bytes[byteIndex] |= (byte)(1 << bitIndex);             }             else             {                 // 对应位保持为 0,无需额外操作             }         }          return bytes;     }      /// <summary>     /// 读 读写线圈     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_01(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         BinaryWriter writer;         ushort StartAddress, DataNumber;         StartAddress = reader.ReadUInt16();         DataNumber = reader.ReadUInt16();         bool[] data = store.ReadCoilDiscretes(StartAddress, DataNumber);         byte[] coilBytes = CoilToBytes(data);         byte[] dataBytes = new byte[coilBytes.Length + 1];         writer = new BinaryWriter(new MemoryStream(dataBytes));         writer.Write((byte)coilBytes.Length);         writer.Write(coilBytes);         ADUMessage response = new ADUMessage()         {             Transaction = request.Transaction,             Protocol = request.Protocol,             Length = (ushort)(dataBytes.Length + 2),             Unit = request.Unit,             FunctionCode = request.FunctionCode,             Data = dataBytes,         };         return response;     }      /// <summary>     /// 读 只读线圈     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_02(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         BinaryWriter writer;         ushort StartAddress, DataNumber;         StartAddress = reader.ReadUInt16();         DataNumber = reader.ReadUInt16();         bool[] data = store.ReadCoilInputs(StartAddress, DataNumber);         byte[] coilBytes = CoilToBytes(data);         byte[] dataBytes = new byte[coilBytes.Length + 1];         writer = new BinaryWriter(new MemoryStream(dataBytes));         writer.Write((byte)coilBytes.Length);         writer.Write(coilBytes);         ADUMessage response = new ADUMessage()         {             Transaction = request.Transaction,             Protocol = request.Protocol,             Length = (ushort)(dataBytes.Length + 2),             Unit = request.Unit,             FunctionCode = request.FunctionCode,             Data = dataBytes,         };         return response;     }      /// <summary>     /// 读 读写寄存器     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_03(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         BinaryWriter writer;         ushort StartAddress, DataNumber;         StartAddress = reader.ReadUInt16();         DataNumber = reader.ReadUInt16();         ushort[] data = store.ReadHoldingRegisters(StartAddress, DataNumber);         byte[] dataBytes = new byte[data.Length * 2 + 1];         writer = new BigEndianBinaryWriter(new MemoryStream(dataBytes));         writer.Write((byte)(data.Length * 2));         foreach (ushort value in data)         {             writer.Write(value);         }         Array.Resize(ref dataBytes, dataBytes.Length + 1);         ADUMessage response = new ADUMessage()         {             Transaction = request.Transaction,             Protocol = request.Protocol,             Length = (ushort)(dataBytes.Length + 2),             Unit = request.Unit,             FunctionCode = request.FunctionCode,             Data = dataBytes,         };         return response;     }      /// <summary>     /// 读 只读寄存器     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_04(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         BinaryWriter writer;         ushort StartAddress, DataNumber;         StartAddress = reader.ReadUInt16();         DataNumber = reader.ReadUInt16();         ushort[] data = store.ReadInputRegisters(StartAddress, DataNumber);         byte[] dataBytes = new byte[data.Length * 2 + 1];         writer = new BigEndianBinaryWriter(new MemoryStream(dataBytes));         writer.Write((byte)(data.Length * 2));         foreach (ushort value in data)         {             writer.Write(value);         }         Array.Resize(ref dataBytes, dataBytes.Length + 1);         ADUMessage response = new ADUMessage()         {             Transaction = request.Transaction,             Protocol = request.Protocol,             Length = (ushort)(dataBytes.Length + 2),             Unit = request.Unit,             FunctionCode = request.FunctionCode,             Data = dataBytes,         };         return response;     }      /// <summary>     /// 写 读写一个线圈     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_05(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         ushort StartAddress, coli;         StartAddress = reader.ReadUInt16();         coli = reader.ReadUInt16();         store.WriteCoilDiscretes(StartAddress, new bool[] { coli ==0xff00?true:false});         return request;     }      /// <summary>     /// 写 读写一个寄存器     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_06(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         ushort StartAddress, register;         StartAddress = reader.ReadUInt16();         register = reader.ReadUInt16();         store.WriteHoldingRegisters(StartAddress, new ushort[] { register });         return request;     }      /// <summary>     /// 写 读写多个线圈     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_0f(ADUMessage request)     {         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         ushort StartAddress, DataNumber;         StartAddress = reader.ReadUInt16();         DataNumber = reader.ReadUInt16();         byte byteNumber = reader.ReadByte();         //线圈是小端传输         byte[] bytes = reader.ReadBytes(byteNumber);         bool[] data=new bool[DataNumber];         byte index = 0;         foreach (var item in bytes)         {             //1000 0000             byte rr = (byte)0x01;             for (int i = 0; i < 8; i++)             {                 if (index< DataNumber)                 {                     var result = rr & item;                     if (result > 0)                     {                         data[index] = true;                     }                     else                     {                         data[index] = false;                     }                     //0100 0000                     rr <<= 1;                     index++;                 }                 else                 {                     break;                 }             }         }         store.WriteCoilDiscretes(StartAddress, data);         return request;     }      /// <summary>     /// 写 读写多个寄存器     /// </summary>     /// <param name="request"></param>     /// <returns></returns>     private ADUMessage Response_10(ADUMessage request)     {         //寄存器是大端传输         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));         ushort StartAddress, DataNumber;         StartAddress = reader.ReadUInt16();         DataNumber = reader.ReadUInt16();         byte byteNumber = reader.ReadByte();         ushort[] data = new ushort[byteNumber / 2];         for (int i = 0; i < data.Length; i++)         {             data[i] = reader.ReadUInt16();         }         store.WriteHoldingRegisters(StartAddress, data);         return request;     } } 
BigEndianBinaryReader.cs
public class BigEndianBinaryReader : BinaryReader {     public BigEndianBinaryReader(Stream input) : base(input)     {     }      public override short ReadInt16()     {         var data = base.ReadBytes(2);         Array.Reverse(data);         return BitConverter.ToInt16(data, 0);     }      public override ushort ReadUInt16()     {         var data = base.ReadBytes(2);         Array.Reverse(data);         return BitConverter.ToUInt16(data, 0);     }      public override int ReadInt32()     {         var data = base.ReadBytes(4);         Array.Reverse(data);         return BitConverter.ToInt32(data, 0);     }      public override uint ReadUInt32()     {         var data = base.ReadBytes(4);         Array.Reverse(data);         return BitConverter.ToUInt32(data, 0);     }      public override long ReadInt64()     {         var data = base.ReadBytes(8);         Array.Reverse(data);         return BitConverter.ToInt64(data, 0);     }      public override float ReadSingle()     {         var data = base.ReadBytes(4);         Array.Reverse(data);         return BitConverter.ToSingle(data, 0);     }      public override double ReadDouble()     {         var data = base.ReadBytes(8);         Array.Reverse(data);         return BitConverter.ToDouble(data, 0);     }      // 可以继续添加其他方法来支持更多数据类型的大端读取 } public class BigEndianBinaryWriter : BinaryWriter {     public BigEndianBinaryWriter(Stream input) : base(input)     {     }      public override void Write(ushort value)     {         var bytes = BitConverter.GetBytes(value);         Array.Reverse(bytes);         base.Write(bytes);     }      public override void Write(short value)     {         var bytes = BitConverter.GetBytes(value);         Array.Reverse(bytes);         base.Write(bytes);     }      public override void Write(uint value)     {         var bytes = BitConverter.GetBytes(value);         Array.Reverse(bytes);         base.Write(bytes);     }      public override void Write(int value)     {         var bytes = BitConverter.GetBytes(value);         Array.Reverse(bytes);         base.Write(bytes);     }      public override void Write(ulong value)     {         var bytes = BitConverter.GetBytes(value);         Array.Reverse(bytes);         base.Write(bytes);     }      public override void Write(long value)     {         var bytes = BitConverter.GetBytes(value);         Array.Reverse(bytes);         base.Write(bytes);     }        // 可以继续添加其他方法来支持更多数据类型的大端写入 } 
Program.cs
    internal class Program     {         static WebModbusServer webModbusServer;         static void Main(string[] args)         {             webModbusServer = new WebModbusServer();             //服务器             if (args.Length == 1)             {                 //webModbusServer.store.WriteCoilDiscretes(0, new bool[] { true, true });                 //webModbusServer.store.CoilInputs[0] = true;                 //webModbusServer.store.CoilInputs[1] = true;                 StartServer(args[0]);             }         }          private static void StartServer(string args)         {              int serverPort = Convert.ToInt32(args);             var server = new TcpListener(IPAddress.Parse("127.0.0.1"), serverPort);             Console.WriteLine($"TCP服务器  127.0.0.1:{serverPort}");             server.Start();             int cnt = 0;             Task.Run(async () =>             {                 List<TcpClient> clients = new List<TcpClient>();                 while (true)                 {                     TcpClient client = await server.AcceptTcpClientAsync();                     clients.Add(client);                     cnt++;                     var ep = client.Client.RemoteEndPoint as IPEndPoint;                     Console.WriteLine($"TCP客户端_{cnt}  {ep.Address}:{ep.Port}");                     //给这个客户端开一个聊天线程                     //操作系统将会根据游客端口对应表将控制权交给对应游客线程                     StartModbus(client);                 }             }).Wait();         }          public static async Task StartModbus(TcpClient client)         {             var buffer = new byte[1024 * 4];             while (client.Connected)             {                 int msgLength = await client.Client.ReceiveAsync(new ArraySegment<byte>(buffer));                 //关闭连接时会接收到一次空消息,不知道为什么                 if (msgLength>0)                 {                     PrintBytes(buffer.Take(msgLength).ToArray(), "请求 ");                     ADUMessage response = webModbusServer.HandleRequest(buffer.Take(msgLength).ToArray());                     await client.Client.SendAsync(ADUMessage.Serialze(response));                     PrintBytes(ADUMessage.Serialze(response), "响应 ");                 }             }         }          public static void PrintBytes(byte[] bytes,string prefix="")         {             Console.Write(prefix);             for (int i = 0; i < bytes.Length; i++)             {                 if (i < 2)                 {                     Console.ForegroundColor = ConsoleColor.Red;                 }                 else if(i<4)                 {                     Console.ForegroundColor = ConsoleColor.Green;                 }                 else if(i<6)                 {                     Console.ForegroundColor= ConsoleColor.Blue;                 }                 else if (i < 7)                 {                     Console.ForegroundColor = ConsoleColor.Yellow;                 }                 else if (i<8)                 {                     Console.ForegroundColor = ConsoleColor.DarkCyan;                 }                 else                 {                     Console.ForegroundColor = ConsoleColor.White;                 }                 Console.Write(bytes[i].ToString("X2") + " ");             }             Console.WriteLine();         }     } 
发表评论

评论已关闭。

相关文章