基于WebSocket的modbus通信(三)- websocket和串口

WebSocket传递ModbusTCP数据包

  • 错误纠正
    上一篇还有个错误,就是客户端写数据时服务端不需要响应,但我的服务端响应了的。我选择改客户端,把写数据时接收到的响应丢弃。
PrintBytes(ADUMessage.Serialze(request), "请求"); if (Client != null) {     await Client.Client.SendAsync(new Memory<byte>(ADUMessage.Serialze(request)));     //丢弃可能的响应     await WebSocket.ReceiveAsync(new ArraySegment<byte>(new byte[1024*4]), CancellationToken.None); } 

现在我们同时有了服务器和客户端,就可以在tcp连接上面覆盖一个websocket连接,然后用WebSocket传递Modbus数据帧。

效果

这是基于WebSocket连接的modbus通信,读写都没问题

  • 服务器
    基于WebSocket的modbus通信(三)- websocket和串口
  • 客户端
    基于WebSocket的modbus通信(三)- websocket和串口

主程序改造

我们的服务器和客户端可以自由选择使用TCP或者WebSocket通信,所以

  • 需要提供对应的命令行参数
  • 根据参数选择TCP或者WebSocket通信

首先是命令行参数
参数设计如下

  • 服务器: tcp|websocket 服务器端口
  • 客户端: tcp|websocket 客户端端口 服务器端口
static void Main(string[] args) {     webModbusServer = new WebModbusServer();     //服务器     if (args.Length == 2)     {         if (args[0]=="tcp")         {             StartTcpServer(args[1]);         }         else if(args[0] == "websocket")         {             StartWebSocketServer(args[1]);         }     }     //客户端     else     {         if (args[0] == "tcp")         {             Task.Run(async () =>             {                 await StartClient(args);             }).Wait();         }         else if (args[0] == "websocket")         {             Task.Run(async () =>             {                 await StartWebsocketClient(args);             }).Wait();         }     } } 

然后就是实现StartTcpServer,StartWebSocketServer,StartClient,StartWebsocketClient这四个方法。
具体实现比较繁琐,我就放到最后的完整代码里面了。

串口传递ModbusTCP数据包

不同于网络通信的7层协议或者TCP/IP协议族为我们所熟悉。串口通信是如何进行的?也是分层的吗?串口通信与网络通信能融合吗?这个我们比较陌生。
串口通信和网络通信(例如通过以太网进行的网络通信)在其基本原理和工作方式上有一些显著的区别。

串口通信与网络通信比较

  1. 物理介质:

    • 串口通信:通常通过物理导线(例如串口线)直接连接两个设备进行通信,例如 RS-232、RS-485、USB 等串口标准。
      串口通信通常通过导线进行,但也可以通过其他媒介进行,如光纤或无线电波。就是插一个转换器到串口接口上,比如串口到光纤转换器。串口到无线电波转换器。我们平时经常用到的就有USB转WIFI、USB转4G。
    • 网络通信:通过各种不同的物理介质进行,如以太网使用的双绞线、光纤、无线电波等。
  2. 协议栈和分层:

    • 串口通信:虽然串口通信也可以分层,但通常它的分层结构较简单,主要包含物理层和数据链路层。常见的串口通信协议如
      物理层 传输介质 接口 数据链路层
      RS-232 串口电缆 9 针 D-Sub Modbus、CAN,自定义协议 传输距离相对较短,通常为数米
      RS-422 两对绝缘的双绞线 9 针 D-Sub Modbus、CAN,自定义协议 通常可达几百米的距离
      RS-485 双绞线 9 针或者 15 针 D-Sub Modbus、CAN,自定义协议 传输距离可达数百至数千米
    • 网络通信:基于 TCP/IP 协议族的网络通信通常遵循 OSI 模型的七层协议结构,包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。每一层都有特定的功能和协议,如物理层负责传输比特流,网络层负责数据包的路由等。
      相应的网络通信的标准:
      物理层 传输介质 数据链路层
      10BASE-T 双绞线 IEEE 802.3 以太网
      10BASE2 同轴电缆 IEEE 802.3 以太网
      802.11a系列 无线电 IEEE 802.11 MAC WIFI
      LTE 无线电 LTE MAC 4G
      1000BASE-SX 光纤 IEEE 802.3 以太网
    • 串口通信只是物理层和数据链路层的标准,同样可以上层架设TCP/IP协议栈,实现远程监控、远程控制等功能。
  3. 速率和带宽:

    • 串口通信:通常速率较低,受限于物理介质和串口协议的限制,不适合大量数据的高速传输。
    • 网络通信:具有较高的传输速率和带宽,能够支持大规模数据的传输。

在程序中使用串口

就像网络通讯我们不用管TCP及以下的协议层一样,串口通信物理层不用管,操作系统已经帮我们搞定了。
我们只需要写数据链路层就行了。
使用System.IO.Ports.SerialPort这个类就行了。
但有个问题是串口只负责发送接收比特流组织成字节放在缓冲区,至于里面是什么意思,我们一概不知。

在串口上覆盖ModbusTCP协议作为数据链路层实现复杂通信

为了减少代码,这里就由我手搓的ModbusTcp服务器和客户端来测试,但是串口的物理层只提供了数据编码、每个字符的数据校验的能力,不提供封装成帧的能力。
而ModbusTcp原本设计是在tcp上面使用,tcp及其下面的层已经提供了这些功能,所以ModbusTcp本身不提供封装成帧。
我们会面临看到数据来了就跑去接收,结果数据才接收了一半这些问题。
我们有两种选择。

  • 写一个封装成帧、透明传输的协议作为数据链路层在物理层之上,然后把ModbusTcp作为第三层的协议
  • 使用ModbusRTU协议作为数据链路层。

因为我们要使用ModbusTCP,所以就选第一种。

串口数据链路层

现成的是没有的,我们只好自己再来搓一个数据链路层。把一个ModbusTcp数据帧接收完了再交给上层处理。

  • 这个协议应该有一个数据帧栈。
  • 这一层不断读串口缓冲区,完整读出来一个帧后,就添加到数据帧栈中。
  • 读下一个帧。

帧格式

帧开始符 (modbustcp)数据 帧结束符
SOH(0x01) bytes EOT(0x04)
  • 不透明的帧
    帧开始符 (modbustcp)数据 帧结束符
    SOH(0x01) ESC bytes SOH bytes EOT bytes EOT(0x04)
  • 透明的帧
    用ESC来进行字节填充解决透明传输
    帧开始符 (modbustcp)数据 帧结束符
    SOH(0x01) ESC ESC bytes ESC SOH bytes ESC EOT bytes EOT(0x04)

数据链路层实现

数据链路层的实现较为复杂,主要实现了透明传输、封装成帧。
其中有一个难点是在没有获取到数据帧时等待,但有数据帧到来后又要完成这个等待任务。
这就用到了TaskCompletionSource对象

//没有计算完成时等待 await dataReceived.Task; //触发完成计算 dataReceived.TrySetResult(true); 

使用时直接传入串口号创建一个数据链路层对象,然后阻塞调用他的发送数据和接收数据方法

//创建数据链路层对象 SerialCommunication serialComm = new SerialCommunication("COM1", 9600); //开启modbustcp服务器 StartCommModbus(serialComm);  ... //在开启服务器里面 public static async Task StartCommModbus(SerialCommunication serialComm) {     while (serialComm.isOpen)     {         // 接收数据         byte[] buffer = await serialComm.ReceiveDataAsync();         if (buffer.Length > 0)         {             PrintBytes(buffer, "请求 ");             ADUMessage response = webModbusServer.HandleRequest(buffer);             // 发送数据             await serialComm.SendDataAsync(ADUMessage.Serialze(response));             PrintBytes(ADUMessage.Serialze(response), "响应 ");         }     } } 

之后还要在主程序中添加一个使用串口的分支,以便我们指定使用那种方式传输数据。

效果

  • 客户端
    基于WebSocket的modbus通信(三)- websocket和串口

  • 服务端
    基于WebSocket的modbus通信(三)- websocket和串口

完整代码

SerialCommunication.cs
public class SerialCommunication {     private const byte SOH = 0x01; // 起始标志     private const byte EOT = 0x04;   // 结束标志     private const byte ESC = 0x1B;   // 透明填充      private SerialPort serialPort;      public bool isOpen;      private Stack<byte[]> frames = new Stack<byte[]>();     private object lockObject = new object();     private TaskCompletionSource<bool> dataReceived = new TaskCompletionSource<bool>();      public SerialCommunication(string portName, int baudRate)     {         isOpen = false;         frames = new Stack<byte[]>();         serialPort = new SerialPort(portName, baudRate);         serialPort.Open();         isOpen = true;         readData();     }      private void readData()     {         Task.Run(() =>         {             byte[] frame = new byte[100]; // 假设最大帧长度为100字节             int index = -1;             while (true)             {                 int rs= serialPort.BaseStream.ReadByte();                 if (rs == -1)                 {                     index = -1;                     continue;                 }                 byte b = (byte)rs;                 if (b == SOH) // 如果读到起始标志                 {                     if (index>0 && frame[index-1]== ESC)                     {                         index--;                         frame[index] = SOH;                         index++;                     }                     else                     {                         index = 0;                         frame[index] = b;                         index++;                     }                 }                 else if (b==ESC)                 {                     if (index==-1)                     {                         //丢弃byte                         continue;                     }                     else if (index > 0 && frame[index - 1] == ESC)                     {                         continue;                     }                     else                     {                         frame[index] = b;                         index++;                     }                 }                 else if (b == EOT) // 如果读到结束标志                 {                     if (index == -1)                     {                         //丢弃byte                         continue;                     }                     else if (index>0 && frame[index - 1] == ESC)                     {                         index--;                         frame[index] = EOT;                         index++;                     }                     else                     {                         frame[index] = EOT;                         index++;                         byte[] data = ParseFrame(frame, index);                         if (data != null)                         {                             lock (lockObject)                             {                                 frames.Push(data);                                 dataReceived.TrySetResult(true);                             }                             index = -1;                         }                     }                 }                 else                 {                     if (index==-1)                     {                         //丢弃byte                         continue;                     }                     else                     {                         frame[index] = b;                         index++;                     }                 }             }         });     }      // 发送数据     public async Task SendDataAsync(byte[] data)     {         byte[] frame = EncapsulateFrame(data);         await serialPort.BaseStream.WriteAsync(frame, 0, frame.Length);     }      // 接收数据     public async Task<byte[]> ReceiveDataAsync()     {         byte[] frame;         lock (lockObject)         {             if (frames.Count > 0)             {                 frame = frames.Pop();                 return frame;             }         }         // 没有数据时等待         bool rs = await dataReceived.Task;         frame = frames.Pop();         lock (lockObject)         {             dataReceived = new TaskCompletionSource<bool>();         }         return frame;     }      // 封装数据帧     private byte[] EncapsulateFrame(byte[] data)     {         byte[] frame = new byte[data.Length + 3];         frame[0] = SOH;                // 添加起始标志         Array.Copy(data, 0, frame, 1, data.Length); // 添加数据内容         byte checksum = CalculateChecksum(data); // 计算校验字段         frame[data.Length + 1] = checksum;       // 添加校验字段         frame[data.Length + 2] = EOT;   // 添加结束标志         //透明传输处理         using (MemoryStream ms=new MemoryStream())         {             ms.Write(frame, 0, 1);             for (global::System.Int32 i = 1; i < frame.Length-1; i++)             {                 if (frame[i]==SOH || frame[i] == ESC || frame[i] == EOT)                 {                     ms.Write(new byte[2] { ESC, frame[i] });                 }                 else                 {                     ms.Write(new byte[1] { frame[i] });                 }             }             ms.Write(frame, (int)frame.Length-1, 1);             byte[] bytes = ms.ToArray();             //PrintBytes(bytes, "透明传输");             return bytes;         }     }      // 解析数据帧     private byte[] ParseFrame(byte[] frame, int length)     {         byte checksum = frame[length - 2];         byte[] data = new byte[length - 3];         Array.Copy(frame, 1, data, 0, length - 3);         if (CalculateChecksum(data) == checksum)         {             return data;         }         return null;     }      // 计算校验字段(简单求和校验)     private byte CalculateChecksum(byte[] data)     {         int sum = 0;         foreach (byte b in data)         {             sum += b;         }         return (byte)(sum % 256);     }      // 关闭串口     public void Close()     {         isOpen = false;         serialPort.Close();     }      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();     } } 
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();         }     } }  /// <summary> /// Modbus服务器 /// </summary> 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;     } }  /// <summary> /// Modbus客户端 /// </summary> public class WebModbusClient {     public ushort Transaction { get; set; }     public TcpClient Client { get; }     public WebSocket WebSocket { get; set; }     public SerialCommunication SerialComm { get; }     public ADUMessage request { get; set; }     public ADUMessage response { get; set; }      public WebModbusClient(TcpClient client)     {         Transaction = 0x00;         Client = client;     }      public WebModbusClient(WebSocket webSocket)     {         Transaction = 0x00;         WebSocket = webSocket;     }      public WebModbusClient(SerialCommunication serialComm)     {         Transaction = 0x00;         SerialComm = serialComm;     }      private ADUMessage CreateMsg()     {         ADUMessage message = new ADUMessage();         message.Transaction = Transaction;         Transaction++;         message.Protocol = 0x00;         message.Unit = 0x00;         this.request = message;         return message;     }     public 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();     }     public bool[] BytesToBools(byte[] bytes,ushort dataNumber)     {         int index = 0;         bool[] bools = new bool[dataNumber];         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)                     {                         bools[index] = true;                     }                     else                     {                         bools[index] = false;                     }                     //0100 0000                     rr <<= 1;                     index++;                 }                 else                 {                     break;                 }             }         }         return bools;     }      private async Task<ADUMessage> SendWithResponse(ADUMessage request)     {         PrintBytes(ADUMessage.Serialze(request), "请求");         if (Client != null)         {             await Client.Client.SendAsync(new Memory<byte>(ADUMessage.Serialze(request)));             byte[] bytes = new byte[1024];             int msgLength = await Client.Client.ReceiveAsync(new ArraySegment<byte>(bytes));             this.response = ADUMessage.Deserialize(bytes.Take(msgLength).ToArray());             PrintBytes(bytes.Take(msgLength).ToArray(), "响应");             return response;         }         else if(WebSocket != null)         {             await WebSocket.SendAsync(new ArraySegment<byte>(ADUMessage.Serialze(request)),WebSocketMessageType.Binary,true,CancellationToken.None);             byte[] bytes = new byte[1024];             var result = await WebSocket.ReceiveAsync(new ArraySegment<byte>(bytes),CancellationToken.None);             this.response = ADUMessage.Deserialize(bytes.Take(result.Count).ToArray());             PrintBytes(bytes.Take(result.Count).ToArray(), "响应");             return response;         }         else if (SerialComm!=null)         {             await SerialComm.SendDataAsync(ADUMessage.Serialze(request));             byte[] bytes = await SerialComm.ReceiveDataAsync();             this.response = ADUMessage.Deserialize(bytes);             PrintBytes(bytes, "响应");             return response;         }         else         {             throw new Exception("没有传入连接");         }     }     private async Task SendNoResponse(ADUMessage request)     {         PrintBytes(ADUMessage.Serialze(request), "请求");         if (Client != null)         {             await Client.Client.SendAsync(new Memory<byte>(ADUMessage.Serialze(request)));             //丢弃可能的响应             await WebSocket.ReceiveAsync(new ArraySegment<byte>(new byte[1024*4]), CancellationToken.None);         }         else if (WebSocket != null)         {             await WebSocket.SendAsync(new ArraySegment<byte>(ADUMessage.Serialze(request)), WebSocketMessageType.Binary, true, CancellationToken.None);             //丢弃可能的响应             await WebSocket.ReceiveAsync(new ArraySegment<byte>(new byte[1024 * 4]), CancellationToken.None);         }         else if (SerialComm != null)         {             await SerialComm.SendDataAsync(ADUMessage.Serialze(request));             byte[] bytes = await SerialComm.ReceiveDataAsync();             this.response = ADUMessage.Deserialize(bytes);             PrintBytes(bytes, "响应");         }         else         {             throw new Exception("没有传入连接");         }     }      public byte[] BoolToBytes(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="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public async Task<bool[]> Request_01(ushort startIndex, ushort length)     {         var request = CreateMsg();         request.Length = 0x06;         request.FunctionCode= 0x01;         request.Data = new byte[4];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write(startIndex);         writer.Write(length);         var response = await SendWithResponse(request);         BinaryReader reader = new BinaryReader(new MemoryStream(response.Data));         byte byteLength=reader.ReadByte();         byte[] bytes = reader.ReadBytes(byteLength);         bool[] bools= BytesToBools(bytes,length);         return bools;     }      /// <summary>     /// 读 只读线圈     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public async Task<bool[]> Request_02(ushort startIndex, ushort length)     {         var request = CreateMsg();         request.Length = 0x06;         request.FunctionCode = 0x02;         request.Data = new byte[4];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write(startIndex);         writer.Write(length);         var response = await SendWithResponse(request);         BinaryReader reader = new BinaryReader(new MemoryStream(response.Data));         byte byteLength = reader.ReadByte();         byte[] bytes = reader.ReadBytes(byteLength);         bool[] bools = BytesToBools(bytes, length);         return bools;     }      /// <summary>     /// 读 读写寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public async Task<ushort[]> Request_03(ushort startIndex, ushort length)     {         var request = CreateMsg();         request.Length = 0x06;         request.FunctionCode = 0x03;         request.Data = new byte[4];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write(startIndex);         writer.Write(length);         var response = await SendWithResponse(request);         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(response.Data));         byte byteLength = reader.ReadByte();         ushort[] registers = new ushort[length];         for (int i = 0; i < length; i++)         {             registers[i] = reader.ReadUInt16();         }         return registers;     }      /// <summary>     /// 读 只读寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="length"></param>     /// <returns></returns>     public async Task<ushort[]> Request_04(ushort startIndex, ushort length)     {         var request = CreateMsg();         request.Length = 0x06;         request.FunctionCode = 0x04;         request.Data = new byte[4];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write(startIndex);         writer.Write(length);         var response = await SendWithResponse(request);         BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(response.Data));         byte byteLength = reader.ReadByte();         ushort[] registers = new ushort[length];         for (int i = 0; i < registers.Length; i++)         {             registers[i] = reader.ReadUInt16();         }         return registers;     }      /// <summary>     /// 写 读写一个线圈     /// </summary>     /// <param name="startIndex"></param>     /// <param name="coil"></param>     /// <returns></returns>     public async Task<ADUMessage> Request_05(ushort startIndex, bool coil)     {         var request = CreateMsg();         request.Length = 0x06;         request.FunctionCode = 0x05;         request.Data = new byte[4];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write(startIndex);         if (coil)         {             writer.Write((ushort)0xff00);         }         else         {             writer.Write((ushort)0x0000);         }         await SendNoResponse(request);         return request;     }      /// <summary>     /// 写 读写一个寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="register"></param>     /// <returns></returns>     public async Task<ADUMessage> Request_06(ushort startIndex, ushort register)     {         var request = CreateMsg();         request.Length = 0x06;         request.FunctionCode = 0x06;         request.Data = new byte[4];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write(startIndex);         writer.Write(register);         await SendNoResponse(request);         return request;     }      /// <summary>     /// 写 读写多个线圈     /// </summary>     /// <param name="startIndex"></param>     /// <param name="coils"></param>     /// <returns></returns>     public async Task<ADUMessage> Request_0f(ushort startIndex, bool[] coils)     {         var request = CreateMsg();         request.FunctionCode = 0x0f;         request.Data = new byte[4+1+(coils.Length+7)/8];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write((ushort)startIndex);         var coilBytes = BoolToBytes(coils);         request.Length = (ushort)(7 + coilBytes.Length);         writer.Write((ushort)coils.Length);         writer.Write((byte)coilBytes.Length);         writer.Write(coilBytes);         await SendNoResponse(request);         return request;     }      /// <summary>     /// 写 读写多个寄存器     /// </summary>     /// <param name="startIndex"></param>     /// <param name="registers"></param>     /// <returns></returns>     public async Task<ADUMessage> Request_10(ushort startIndex, ushort[] registers)     {         var request = CreateMsg();         request.Length = (ushort)(7+ registers.Length * 2);         request.FunctionCode = 0x10;         request.Data = new byte[4+1+registers.Length*2];         BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));         writer.Write((ushort)startIndex);         writer.Write((ushort)registers.Length);         writer.Write((byte)(registers.Length * 2));         for (int i = 0; i < registers.Length; i++)         {             writer.Write(registers[i]);         }         await SendNoResponse(request);         return request;     } } 
Program.cs
    internal class Program     {         static WebModbusServer webModbusServer;         static void Main(string[] args)         {             webModbusServer = new WebModbusServer();             //服务器             if (args.Length == 2)             {                 if (args[0]=="tcp")                 {                     StartTcpServer(args[1]);                 }                 else if(args[0] == "websocket")                 {                     StartWebSocketServer(args[1]);                 }                 else if (args[0] == "comm")                 {                     StartCommServer(args[1]);                 }             }             //客户端             else             {                 if (args[0] == "tcp")                 {                     Task.Run(async () =>                     {                         await StartClient(args);                     }).Wait();                 }                 else if (args[0] == "websocket")                 {                     Task.Run(async () =>                     {                         await StartWebsocketClient(args);                     }).Wait();                 }                 else if (args[0] == "comm" && args[2]=="client")                 {                     Task.Run(async () =>                     {                         await StartCommClient(args);                     }).Wait();                 }             }         }          private static void StartTcpServer(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}");                     //给这个客户端开一个聊天线程                     //操作系统将会根据游客端口对应表将控制权交给对应游客线程                     //StartChat(client);                     StartModbus(client);                 }             }).Wait();         }                  private static void StartWebSocketServer(string args)         {              int serverPort = Convert.ToInt32(args);             WebsocketLisener websocketLisener = new WebsocketLisener(IPAddress.Parse("127.0.0.1"), serverPort);             Console.WriteLine($"Websocket服务器  127.0.0.1:{serverPort}");             Task.Run(async () =>             {                 while (true)                 {                     WebSocket websocketServer = await websocketLisener.AcceptWebsocketConnectionAsync();                     StartWebsocketModbus(websocketServer);                 }             }).Wait();         }          private static void StartCommServer(string args)         {             SerialCommunication serialComm = new SerialCommunication(args, 9600);             Console.WriteLine($"串口服务器  {args}");             Task.Run(async () =>             {                 await StartCommModbus(serialComm);             }).Wait();         }          private static async Task StartClient(string[] args)         {             int clientPort = Convert.ToInt32(args[1]);             int serverPort = Convert.ToInt32(args[2]);             var client = new TcpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), clientPort));             Console.WriteLine($"TCP客户端  127.0.0.1:{clientPort}");             await client.ConnectAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), serverPort));             Console.WriteLine($"连接到 127.0.0.1:{serverPort}");             WebModbusClient webModbusClient = new WebModbusClient(client);             Console.WriteLine("【功能码】 【地址】 【数量|数据】");             while (true)             {                 Console.WriteLine("请输入指令");                 string? msg = Console.ReadLine();                 while (msg == null)                 {                     //功能码 数据                     msg = Console.ReadLine();                 }                 try                 {                     string[] data = msg.Split(' ');                     ushort funCode = ushort.Parse(data[0],NumberStyles.HexNumber);                     ushort startIndex;                     ushort length;                     switch (funCode)                     {                         //读 读写线圈                         case 0x01:                             startIndex = ushort.Parse(data[1]);                             length= ushort.Parse(data[2]);                             var rs_01 = await webModbusClient.Request_01(startIndex, length);                             PrintBools(rs_01);                             break;                         //读 只读线圈                         case 0x02:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_02 = await webModbusClient.Request_02(startIndex, length);                             PrintBools(rs_02);                             break;                         //读 读写寄存器                         case 0x03:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_03 = await webModbusClient.Request_03(startIndex, length);                             for (global::System.Int32 i = 0; i < length; i++)                             {                                 Console.Write(rs_03[i]+" ");                             }                             Console.WriteLine();                             break;                         //读 只读寄存器                         case 0x04:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_04 = await webModbusClient.Request_04(startIndex, length);                             for (global::System.Int32 i = 0; i < length; i++)                             {                                 Console.Write(rs_04[i] + " ");                             }                             Console.WriteLine();                             break;                         //写 读写一个线圈                         case 0x05:                             startIndex = ushort.Parse(data[1]);                             var coil = bool.Parse(data[2]);                             var rs_05 = await webModbusClient.Request_05(startIndex, coil);                             break;                         //写 读写一个寄存器                         case 0x06:                             startIndex = ushort.Parse(data[1]);                             var register = ushort.Parse(data[2]);                             var rs_06 = await webModbusClient.Request_06(startIndex, register);                             break;                         //写 读写多个线圈                         case 0x0f:                             startIndex = ushort.Parse(data[1]);                             bool[] coils = new bool[data.Length - 2];                             for (global::System.Int32 i = 2; i < data.Length; i++)                             {                                 coils[i - 2] = bool.Parse(data[i]);                             }                             var rs_0f = await webModbusClient.Request_0f(startIndex, coils);                             break;                         //写 读写多个寄存器                         case 0x10:                             startIndex = ushort.Parse(data[1]);                             ushort[] registers = new ushort[data.Length - 2];                             for (global::System.Int32 i = 2; i < data.Length; i++)                             {                                 registers[i - 2] = ushort.Parse(data[i]);                             }                             var rs_10 = await webModbusClient.Request_10(startIndex, registers);                             break;                         default:                             //return Response_01(request);                             break;                     }                 }                 catch (Exception e)                 {                  }             }         }          private static async Task StartWebsocketClient(string[] args)         {             int clientPort = Convert.ToInt32(args[1]);             int serverPort = Convert.ToInt32(args[2]);             Uri uri = new($"ws://127.0.0.1:{serverPort}");             ClientWebSocket ws = new();             Console.WriteLine($"Websocket客户端");             await ws.ConnectAsync(uri, default);             Console.WriteLine($"连接到 127.0.0.1:{serverPort}");             WebModbusClient webModbusClient = new WebModbusClient(ws);             Console.WriteLine("【功能码】 【地址】 【数量|数据】");             while (true)             {                 Console.WriteLine("请输入指令");                 string? msg = Console.ReadLine();                 while (msg == null)                 {                     //功能码 数据                     msg = Console.ReadLine();                 }                 try                 {                     string[] data = msg.Split(' ');                     ushort funCode = ushort.Parse(data[0], NumberStyles.HexNumber);                     ushort startIndex;                     ushort length;                     switch (funCode)                     {                         //读 读写线圈                         case 0x01:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_01 = await webModbusClient.Request_01(startIndex, length);                             PrintBools(rs_01);                             break;                         //读 只读线圈                         case 0x02:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_02 = await webModbusClient.Request_02(startIndex, length);                             PrintBools(rs_02);                             break;                         //读 读写寄存器                         case 0x03:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_03 = await webModbusClient.Request_03(startIndex, length);                             for (global::System.Int32 i = 0; i < length; i++)                             {                                 Console.Write(rs_03[i] + " ");                             }                             Console.WriteLine();                             break;                         //读 只读寄存器                         case 0x04:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_04 = await webModbusClient.Request_04(startIndex, length);                             for (global::System.Int32 i = 0; i < length; i++)                             {                                 Console.Write(rs_04[i] + " ");                             }                             Console.WriteLine();                             break;                         //写 读写一个线圈                         case 0x05:                             startIndex = ushort.Parse(data[1]);                             var coil = bool.Parse(data[2]);                             var rs_05 = await webModbusClient.Request_05(startIndex, coil);                             break;                         //写 读写一个寄存器                         case 0x06:                             startIndex = ushort.Parse(data[1]);                             var register = ushort.Parse(data[2]);                             var rs_06 = await webModbusClient.Request_06(startIndex, register);                             break;                         //写 读写多个线圈                         case 0x0f:                             startIndex = ushort.Parse(data[1]);                             bool[] coils = new bool[data.Length - 2];                             for (global::System.Int32 i = 2; i < data.Length; i++)                             {                                 coils[i - 2] = bool.Parse(data[i]);                             }                             var rs_0f = await webModbusClient.Request_0f(startIndex, coils);                             break;                         //写 读写多个寄存器                         case 0x10:                             startIndex = ushort.Parse(data[1]);                             ushort[] registers = new ushort[data.Length - 2];                             for (global::System.Int32 i = 2; i < data.Length; i++)                             {                                 registers[i - 2] = ushort.Parse(data[i]);                             }                             var rs_10 = await webModbusClient.Request_10(startIndex, registers);                             break;                         default:                             //return Response_01(request);                             break;                     }                 }                 catch (Exception e)                 {                  }             }         }          private static async Task StartCommClient(string[] args)         {             string clientPort = args[1];             SerialCommunication serialComm = new SerialCommunication(clientPort, 9600);             Console.WriteLine($"串口客户端  :{clientPort}");             WebModbusClient webModbusClient = new WebModbusClient(serialComm);             Console.WriteLine("【功能码】 【地址】 【数量|数据】");             while (true)             {                 Console.WriteLine("请输入指令");                 string? msg = Console.ReadLine();                 while (msg == null)                 {                     //功能码 数据                     msg = Console.ReadLine();                 }                 try                 {                     string[] data = msg.Split(' ');                     ushort funCode = ushort.Parse(data[0], NumberStyles.HexNumber);                     ushort startIndex;                     ushort length;                     switch (funCode)                     {                         //读 读写线圈                         case 0x01:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_01 = await webModbusClient.Request_01(startIndex, length);                             PrintBools(rs_01);                             break;                         //读 只读线圈                         case 0x02:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_02 = await webModbusClient.Request_02(startIndex, length);                             PrintBools(rs_02);                             break;                         //读 读写寄存器                         case 0x03:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_03 = await webModbusClient.Request_03(startIndex, length);                             for (global::System.Int32 i = 0; i < length; i++)                             {                                 Console.Write(rs_03[i] + " ");                             }                             Console.WriteLine();                             break;                         //读 只读寄存器                         case 0x04:                             startIndex = ushort.Parse(data[1]);                             length = ushort.Parse(data[2]);                             var rs_04 = await webModbusClient.Request_04(startIndex, length);                             for (global::System.Int32 i = 0; i < length; i++)                             {                                 Console.Write(rs_04[i] + " ");                             }                             Console.WriteLine();                             break;                         //写 读写一个线圈                         case 0x05:                             startIndex = ushort.Parse(data[1]);                             var coil = bool.Parse(data[2]);                             var rs_05 = await webModbusClient.Request_05(startIndex, coil);                             break;                         //写 读写一个寄存器                         case 0x06:                             startIndex = ushort.Parse(data[1]);                             var register = ushort.Parse(data[2]);                             var rs_06 = await webModbusClient.Request_06(startIndex, register);                             break;                         //写 读写多个线圈                         case 0x0f:                             startIndex = ushort.Parse(data[1]);                             bool[] coils = new bool[data.Length - 2];                             for (global::System.Int32 i = 2; i < data.Length; i++)                             {                                 coils[i - 2] = bool.Parse(data[i]);                             }                             var rs_0f = await webModbusClient.Request_0f(startIndex, coils);                             break;                         //写 读写多个寄存器                         case 0x10:                             startIndex = ushort.Parse(data[1]);                             ushort[] registers = new ushort[data.Length - 2];                             for (global::System.Int32 i = 2; i < data.Length; i++)                             {                                 registers[i - 2] = ushort.Parse(data[i]);                             }                             var rs_10 = await webModbusClient.Request_10(startIndex, registers);                             break;                         default:                             //return Response_01(request);                             break;                     }                 }                 catch (Exception e)                 {                  }             }         }          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 async Task StartWebsocketModbus(WebSocket websocketServer)         {             var buffer = new byte[1024 * 4];             while (!websocketServer.CloseStatus.HasValue)             {                 var result = await websocketServer.ReceiveAsync(new ArraySegment<byte>(buffer),CancellationToken.None);                 if (result.Count > 0)                 {                     PrintBytes(buffer.Take(result.Count).ToArray(), "请求 ");                     ADUMessage response = webModbusServer.HandleRequest(buffer.Take(result.Count).ToArray());                     await websocketServer.SendAsync(ADUMessage.Serialze(response),WebSocketMessageType.Binary,true,CancellationToken.None);                     PrintBytes(ADUMessage.Serialze(response), "响应 ");                 }             }         }          public static async Task StartCommModbus(SerialCommunication serialComm)         {             while (serialComm.isOpen)             {                 byte[] buffer = await serialComm.ReceiveDataAsync();                 if (buffer.Length > 0)                 {                     PrintBytes(buffer, "请求 ");                     ADUMessage response = webModbusServer.HandleRequest(buffer);                     await serialComm.SendDataAsync(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();         }         public static void PrintBools(bool[] bools)         {             for (int i = 0; i < bools.Length; i++)             {                 Console.Write(bools[i] + " ");             }             Console.WriteLine();         }     }      public class HttpRequet     {         /// <summary>         /// 解析HTTP消息         /// </summary>         public HttpRequet(string str)         {             Str = str;             //开始行             var startLine = str.Split("rn")[0];             var lines = startLine.Split("rn");             httpMethod = lines[0].Split(' ')[0];             path = lines[0].Split(' ')[1];             //头部             var headerslines = str.Split("rnrn")[0].Split("rn");             headers = new Dictionary<string, string>();             for (int i = 1; i < headerslines.Length; i++)             {                 var header = headerslines[i].Split(": ");                 headers.Add(header[0], header[1]);             }         }          /// <summary>         /// 请求原始消息         /// </summary>         public string Str { get; }         /// <summary>         /// 请求方法         /// </summary>         public string httpMethod { get; internal set; }         /// <summary>         /// 请求路径         /// </summary>         public string path { get; set; }         /// <summary>         /// 头部字段         /// </summary>         public Dictionary<string, string> headers { get; set; }          /// <summary>         /// 判断是否是转协议的请求         /// </summary>         /// <returns></returns>         public bool IsWebsocket()         {             if (this.headers.ContainsKey("Connection") && this.headers["Connection"] == "Upgrade" && this.headers.ContainsKey("Upgrade") && this.headers["Upgrade"] == "websocket")                 return true;             else                 return false;         }          /// <summary>         /// 响应转协议请求并未用当前连接创建一个WebSocket对象         /// </summary>         /// <param name="client"></param>         /// <returns></returns>         public async Task<WebSocket> AcceptWebsocket(TcpClient client, string Sec_WebSocket_Key)         {             using (MemoryStream memoryStream = new MemoryStream())             {                 string header = @$"HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: {GenerateResponseKey(Sec_WebSocket_Key)}  ";                 memoryStream.Write(new ArraySegment<byte>(ASCIIEncoding.ASCII.GetBytes(header)));                 await client.Client.SendAsync(new ArraySegment<byte>(memoryStream.ToArray()));                 Console.WriteLine(header);                  return WebSocket.CreateFromStream(client.GetStream(), true, null, TimeSpan.FromSeconds(10));             }         }          public static string GenerateResponseKey(string requestKey)         {             const string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";             string concatenated = requestKey + guid;             byte[] hashed = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(concatenated));             return Convert.ToBase64String(hashed);         }     }      public class WebsocketLisener     {         public TcpListener server { get; set; }         public int cnt;         public WebsocketLisener(IPAddress address,int port)         {             cnt = 0;             server = new TcpListener(address, port);             server.Start();         }         public async Task<WebSocket> AcceptWebsocketConnectionAsync()         {             TcpClient client = await server.AcceptTcpClientAsync();             var buffer = new byte[1024 * 4];             int msgLength = await client.Client.ReceiveAsync(new ArraySegment<byte>(buffer));             string str = UTF8Encoding.UTF8.GetString(buffer, 0, msgLength);             HttpRequet request = new HttpRequet(str);             if (request.IsWebsocket())             {                 cnt++;                 WebSocket webSocket = await request.AcceptWebsocket(client, request.headers["Sec-WebSocket-Key"]);                 var ep = client.Client.RemoteEndPoint as IPEndPoint;                 Console.WriteLine($"Websocket客户端_{cnt}  {ep.Address}:{ep.Port}");                 return webSocket;             }             throw new Exception("不是WebSocket连接");         }     } 
发表评论

评论已关闭。

相关文章