ModbusRTU\TCP消息帧解析(C#实现报文发送与解析)

目录

  • 知识点
  • 常用链接
  • 一、Modbus
    • 1.ModbusRTU消息帧解析
    • 2.主站poll、从站slave通讯仿真-modbusRTU
      • 1.功能码=01读线圈状态
      • 2.功能码=03读保持寄存器
        • 报文解析(寄存器存整型)
        • 报文解析(寄存器存float)
    • 3.C#模拟主站Poll(ModbusRTU协议-组报文)
    • 4.NModbus4模拟主站poll(ModbusRTU协议)
    • 5.C#模拟主站Poll(ModbusTCP协议-组报文)
    • 6.NModbus4模拟从站slave(ModbusTCP协议)
    • 7.NModbus4模拟从站slave(ModbusRTU协议)
    • 8.modbusRTU、modbusTCP报文不同之处
  • 二、明文TCP

知识点

  1. PLC寄存器中存储(整型和无符号整型:2字节。长整型:4字节。单精度浮点数:4字节。双精度浮点数:8字节),我们只要知道数据类型,是2个字节一截取,还是4个字节 ,对接收到的报文进行字节截取然后编码成str就行
  2. 向PLC中写入Float,float占4个字节=2个寄存器,所以要使用功能码“写多寄存器0x10”, 功能码0x06只能写一个寄存器”
  3. serialPort.write(bytes,0,bytes.Length); thread.sleep(300); serialPort.Read() 发完指令后,要等待从站响应300ms,然后再去读数据
  4. 主站请求从站有两种方式:主动(手动点击查询线圈状态的按钮)被动(通过委托方式,一件事情的发生触发另外事件。场景:称菜,菜一放上去,触发去查询的功能代码块)
  5. 一个F要用4个二进制表示,两个F用8个二进制表示,所以 0xFA :表示1个字节
  6. modbusTCP响应 Tx:00 00 00 00 00 03 01 83 02 【83=1000 0011 (功能码03 的高位为1,就是异常)02是错误码代号要查表】
    在这里插入图片描述
  7. send()/recv()和write()/read():发送数据和接收数据 参考链接
  8. socket原理
  9. 不同协议图,
    在这里插入图片描述
  10. 比如omoronsocekt, modbustcp,他们都是用socket进行数据交互,只是他们在应用层采用不同的协议约定,对报文进行不同方式的解析;明文协议就是直接编码不组包,其他协议都是组包发出去(如明文协议,将字符串编码后直接send
    modbustcp协议,要组装发送报文为(从站地址+功能码+等等+字符串数据))

常用链接

虚拟串口调试工具 V6.9 汉化版免费版
在这里插入图片描述
串口、Modbus通信协议

一、Modbus

课程
文章介绍
一篇博客

1.ModbusRTU消息帧解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.主站poll、从站slave通讯仿真-modbusRTU

从站slave用于模拟PLC中的功能区,一个tab页表示一个功能模块(下图建了两个功能块)
主站poll发送请求,获取PLC中数据。
poll、slave都要设置connection、setup两个区域,只有参数配对了才能正常收发数据
在这里插入图片描述

1.功能码=01读线圈状态

在这里插入图片描述
在这里插入图片描述

报文都是16进制表示
16进制 0X01=0000 0001,一位16进制需要4位二进制表达(F =1111),两个16进制数字表示1个字节
线圈中数据要么是0,要么是1
读取长度:00 0A表示读取10个寄存器
响应字节数(单位是字节):02 表示两个字节,从02往后数两个字节都是数据未位
输出状态:A2 02 这是两字节,解析:先颠倒高低位 02 A2= 0000 0010 1010 0010 再反向读取数据0100 0101 0100 0000
在这里插入图片描述

2.功能码=03读保持寄存器

寄存器中数据可以是整数,浮点型 (整型和无符号整型:2字节。长整型:4字节。单精度浮点数:4字节。双精度浮点数:8字节)
在这里插入图片描述

报文解析(寄存器存整型)

读取长度:00 0A表示读取10个寄存器,1个寄存器是16位=2个字节,所以返回20个字节,一个整型=2字节,所以返回的是10个数据
响应字节数(单位是字节):14 表示20个字节
输出状态:007B 00 00 00 00 00 00 00 00 00 00 这是20个字节,解析: 第一个数为123
在这里插入图片描述

报文解析(寄存器存float)

读取长度:00 0A表示读取10个寄存器,1个寄存器是16位=2个字节,所以返回20个字节,一个float 占4字节,所以返回的是5个数据
响应字节数(单位是字节):14 表示20个字节
输出状态:解析: 42 0A 00 00 通过IEEE转换标准->第一个数为34.5
在这里插入图片描述

3.C#模拟主站Poll(ModbusRTU协议-组报文)

说明
1.下面代码模拟的是主站,需要开启小程序mbslave作为从站PLC
2.主站发起的功能码请求有:读线圈,读保持寄存器,写多个寄存器
3.主站发送报文,然后对响应报文按消息帧进行解析

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Threading;namespace 通讯收发功能解析
{class Program{static void Main(string[] args){Console.WriteLine("Hello World!");//Test_0x01();Test_0x03();//Test_0x10();Console.ReadKey();}static void Test_0x01()/// 01功能码-读线圈状态测试,PLC中线圈全为0{ushort startAddr = 0;ushort readLen = 10;// 请求// byte[] 需要指定长度;不支持LinqList<byte> command = new List<byte>();command.Add(0x01);// 1号从站command.Add(0x01);// 功能码:读线圈状态// 起始地址command.Add(BitConverter.GetBytes(startAddr)[1]);command.Add(BitConverter.GetBytes(startAddr)[0]);// 读取数量command.Add(BitConverter.GetBytes(readLen)[1]);command.Add(BitConverter.GetBytes(readLen)[0]);// CRCcommand = CRC16(command);//command 为长度=8的字节{0x01 0x01 0x00 0x00 0x00 0x0A 0xBC 0x0D}// 以上报文组装完成// 发送-》SerialPortSerialPort serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);// 打开串口serialPort.Open();//将command的第0位->command.count所有数据都发出去serialPort.Write(command.ToArray(), 0, command.Count);//发送报文为01 01 00 00 00 0A BC 0D   请求10个线圈的状态,响应时1个字节8位接收不够,所以两字节Thread.Sleep(5);//加上延时等待PLC反应时间// 进行响应报文的接收和解析 byte[] respBytes = new byte[serialPort.BytesToRead];//缓冲区信息serialPort.Read(respBytes, 0, respBytes.Length);// respBytes -> 01 01 02 00 00 B9 FC// 检查一个校验位//对报文进行解析数据List<byte> respList = new List<byte>(respBytes);respList.RemoveRange(0, 3);//00 00 B9 FCrespList.RemoveRange(respList.Count - 2, 2);//00 00 数据报文// 1。高低位切换// 2。从后往前读respList.Reverse();var respStrList = respList.Select(r => Convert.ToString(r, 2)).ToList();var values = string.Join("", respStrList).ToList();values.Reverse();values.ForEach(c => Console.WriteLine(Convert.ToBoolean(int.Parse(c.ToString()))));//Convert.ToBoolean('1');}static void Test_0x03()//读保持寄存器  PLC中第一个寄存器为123,其他=0{ushort startAddr = 0;ushort readLen = 10;// 请求// byte[] 需要指定长度;不支持LinqList<byte> command = new List<byte>();command.Add(0x01);// 1号从站command.Add(0x03);// 功能码:读保持型寄存器// 起始地址command.Add(BitConverter.GetBytes(startAddr)[1]);command.Add(BitConverter.GetBytes(startAddr)[0]);// 读取数量command.Add(BitConverter.GetBytes(readLen)[1]);command.Add(BitConverter.GetBytes(readLen)[0]);// CRCcommand = CRC16(command);// 报文组装完成// 发送-》SerialPortSerialPort serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);// 打开串口serialPort.Open();serialPort.Write(command.ToArray(), 0, command.Count);// 进行响应报文的接收和解析byte[] respBytes = new byte[serialPort.BytesToRead];serialPort.Read(respBytes, 0, respBytes.Length);// respBytes -> 01 01 02 00 00 B9 FC// 检查一个校验位List<byte> respList = new List<byte>(respBytes);respList.RemoveRange(0, 3);respList.RemoveRange(respList.Count - 2, 2);// 拿到实际的数据部分,进行数据解析// 明确一点:读的是无符号单精度,占两个字节//byte[] data = new byte[2]; //for (int i = 0; i < readLen; i++)//{//    // 字节序问题    小端   大端//    data[0] = respList[i * 2 + 1];//    data[1] = respList[i * 2];//    // 根据此两个字节转换成想要的实际数字//    var value = BitConverter.ToUInt16(data, 0);//    Console.WriteLine(value);//}// 明确一点:读的是Float  占4个字节byte[] data = new byte[4];for (int i = 0; i < readLen / 2; i++){// 字节序问题    小端   大端data[0] = respList[i * 4 + 3];data[1] = respList[i * 4 + 2];data[2] = respList[i * 4 + 1];data[3] = respList[i * 4];// 根据此两个字节转换成想要的实际数字var value = BitConverter.ToSingle(data, 0);Console.WriteLine(value);}}//向PLC中写入Float,float占4个字节=2个寄存器,所以要使用功能码“写多寄存器0x10”, 功能码0x06只能写一个寄存器”, static void Test_0x10()//写多个寄存器功能码0x10{ushort startAddr = 2;ushort writeLen = 4;float[] values = new float[] { 123.45f, 14.3f };// 请求// byte[] 需要指定长度;不支持LinqList<byte> command = new List<byte>();command.Add(0x01);// 1号从站command.Add(0x10);// 功能码:写多个保持型寄存器// 写入地址command.Add(BitConverter.GetBytes(startAddr)[1]);command.Add(BitConverter.GetBytes(startAddr)[0]);// 写入数量command.Add(BitConverter.GetBytes(writeLen)[1]);command.Add(BitConverter.GetBytes(writeLen)[0]);// 获取数值的byte[]List<byte> valueBytes = new List<byte>();for (int i = 0; i < values.Length; i++){List<byte> temp = new List<byte>(BitConverter.GetBytes(values[i]));temp.Reverse();// 调整字节序valueBytes.AddRange(temp);}// 字节数command.Add((byte)valueBytes.Count);command.AddRange(valueBytes);// CRCcommand = CRC16(command);// 报文组装完成// 发送-》SerialPortSerialPort serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);// 打开串口serialPort.Open();serialPort.Write(command.ToArray(), 0, command.Count);}static List<byte> CRC16(List<byte> value, ushort poly = 0xA001, ushort crcInit = 0xFFFF){if (value == null || !value.Any())throw new ArgumentException("");//运算ushort crc = crcInit;for (int i = 0; i < value.Count; i++){crc = (ushort)(crc ^ (value[i]));for (int j = 0; j < 8; j++){crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);}}byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置byte lo = (byte)(crc & 0x00FF);         //低位置List<byte> buffer = new List<byte>();buffer.AddRange(value);buffer.Add(lo);buffer.Add(hi);return buffer;}}
}

4.NModbus4模拟主站poll(ModbusRTU协议)

在这里插入图片描述
ReadHoldingRegisters(1, 0, 1)# 参数:从站地址,起始地址,读取数量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.C#模拟主站Poll(ModbusTCP协议-组报文)

课程视频P17-P14

03读保持寄存器报文
在这里插入图片描述
在这里插入图片描述

说明
1.下面代码模拟的是modbusTCP主站,需要开启小程序mbslave作为从站PLC,要设置slave的connection、setup两个区域的TCP相关参数
2.主站发起的功能码请求有:ReadHoldingRegister,ReadInputRegister
3.主站发送报文,然后对响应报文按消息帧进行解析
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace 通讯收发功能解析
{public class ModbusTcp: ModbusBase{Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);public Result connect(string host, int port){Result result = new Result();try{socket.Connect(host, port);result.State = true;}catch (Exception ex){result.State = false;result.Exception = ex.Message;}return result;}// 读保持型寄存器03// ModbusRTU:0x01(从站地址) 03 (功能码)  0x00 0x0A(起始地址)  0x00 0x05(读取长度)   0xXX 0xXX(CRC16校验码)// ModbusTCP:请求报文:0x00 0x00(TransationID 最大65535)  0x00 0x00 (Modbus协议标识)   0x00 0x06(后续字节数)   =>  0x01 (单元标识) 0x03  0x0 0x0A 0x00 0x05// 响应报文:                   0x00 0x00(TransationID 最大65535)   0x00 0x00 (Modbus协议标识)   0x00 0x0D(后续字节数)   =>  0x01 (单元标识) 0x03(功能码)=>//                                       0x0A(返回10个字节数据) 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00/// <summary>////// </summary>/// <param name="unit_id">从站地址</param>/// <param name="start_addr">寄存器起始地址</param>/// <param name="count">读寄存器数量</param>public Result  ReadHoldingRegister(byte unit_id , ushort start_addr,ushort count){Result result = new Result();try {ushort tid = 0;//报文组装byte[] req_bytes = new byte[]{(byte)(tid/256),(byte)(tid%256),0x00, 0x00,0x00, 0x06,unit_id,0x03,(byte)(start_addr/256),(byte)(start_addr%256),//10进制转成16(byte)(count/256),(byte)(count%256),};tid++;tid %= 65535;//var req_bytes = this.ReadCommandBytes( unit_id,  0x03,start_addr,  count);//发送请求socket.Send(req_bytes);//接收响应byte[] resp_bytes = new byte[6];//由于plc返回的响应字数长度是不一样的,先取前6个字节socket.Receive(resp_bytes, 0 ,6 ,SocketFlags.None);var len_bytes = resp_bytes.ToList().GetRange(4, 2);ushort len = (ushort)(len_bytes[4] * 256 + len_bytes[5]);//解析报文中返回的有多少个字节数resp_bytes = new byte[len];//这个resp_bytes len 表明数据中有多少个字节数据是有用的数据报文socket.Receive(resp_bytes, 0, len, SocketFlags.None);//上面从缓存区拿走了6个字节,现在把剩余的都拿走//检查响应报文是否正常,功能码的高位为1,就是异常//0x83 1000 0011if (resp_bytes[1] > 0x80){//说明响应的是异常报文// 返回异常信息 根据resp_bytes[2]字节进行异常的关联throw new Exception("错误了");}//提取PLC中寄存器中的数据部分报文var data_bytes = resp_bytes.ToList().GetRange(3, resp_bytes[2]).ToArray();result.State = true;result.Datas = data_bytes;}catch(Exception ex){result.State = false;result.Exception = ex.Message;}return result;}public Result ReadInputRegister(byte unit_id, ushort start_addr, ushort count){Result result = new Result();try{ushort tid = 0;//报文组装byte[] req_bytes = new byte[]{(byte)(tid/256),(byte)(tid%256),0x00, 0x00,0x00, 0x06,unit_id,0x04,(byte)(start_addr/256),(byte)(start_addr%256),//10进制转成16(byte)(count/256),(byte)(count%256),};tid++;tid %= 65535;//var req_bytes = this.ReadCommandBytes(unit_id, 0x04, start_addr, count);//发送请求socket.Send(req_bytes);//接收响应byte[] resp_bytes = new byte[6];//由于plc返回的响应字数长度是不一样的,先取前6个字节socket.Receive(resp_bytes, 0, 6, SocketFlags.None);var len_bytes = resp_bytes.ToList().GetRange(4, 2);ushort len = (ushort)(len_bytes[4] * 256 + len_bytes[5]);//解析报文中返回的有多少个字节数resp_bytes = new byte[len];//这个resp_bytes len 表明数据中有多少个字节数据是有用的数据报文socket.Receive(resp_bytes, 0, len, SocketFlags.None);//上面从缓存区拿走了6个字节,现在把剩余的都拿走//检查响应报文是否正常,功能码的高位为1,就是异常//0x83 1000 0011if (resp_bytes[1] > 0x80){//说明响应的是异常报文// 返回异常信息 根据resp_bytes[2]字节进行异常的关联throw new Exception("错误了");}//解析PLC中寄存器中的数据var data_bytes = resp_bytes.ToList().GetRange(3, resp_bytes[2]).ToArray();result.State = true;result.Datas = data_bytes;}catch (Exception ex){result.State = false;result.Exception = ex.Message;}return result;}public void write(){}public T[] Getvalues<T>(byte[] data_bytes)//解析报文{var type_len = Marshal.SizeOf(typeof(T));//检查类型的长度 , int32是4字节,float是4字节for (var i =0; i<data_bytes.Length;i+=type_len){//根据数据类型将报文切割,获取一个类型的字节内容,并将字节转换成对应 的strvar temp_bytes = data_bytes.ToList().GetRange(i, type_len);temp_bytes.Reverse();//字序short v = BitConverter.ToInt16(temp_bytes.ToArray(), 0);ushort vv = BitConverter.ToUInt16(temp_bytes.ToArray(), 0);float vvv = BitConverter.ToSingle(temp_bytes.ToArray(),0);}return null;}}
}

6.NModbus4模拟从站slave(ModbusTCP协议)

用小工具poll连接自己建立的从站,可读取从站的值
文章

using System.Threading;
using System.Net.Sockets;
using System.Net;
using Modbus.Data;
using Modbus.Device;public class slave{/// <summary>/// 服务器提供的数据区/// </summary>public  DataStore Data=DataStoreFactory.CreateDefaultDataStore(); //初始化服务数据区;/// <summary>/// Modbus服务器/// </summary>public  ModbusSlave modbus_tcp_server;public  void modbustcpslave(){modbus_tcp_server = ModbusTcpSlave.CreateTcp(1, new TcpListener(IPAddress.Parse("127.0.0.1"), 502)); //创建ModbusTcp服务器modbus_tcp_server.DataStore = Data;//数据区赋值Thread th_0 = new Thread(() =>{modbus_tcp_server.Listen();//异步 非阻塞 启动服务}){IsBackground = true,};th_0.SetApartmentState(ApartmentState.STA);th_0.Start();Thread th_1 = new Thread(() =>{SetData(); //数据区数据赋值}){IsBackground = true,};th_1.SetApartmentState(ApartmentState.STA);th_1.Start();}/// <summary>/// 设置数据/// </summary>public  void SetData() //static修饰的函数或变量都是在类初始化的时候加载的,而非静态的变量都是在对象初始化的时候加载。{while (true){Data.InputRegisters[1] = (ushort)DateTime.Now.Year;         //年Data.InputRegisters[2] = (ushort)DateTime.Now.Month;        //月Data.InputRegisters[3] = (ushort)DateTime.Now.Day;          //日Data.InputRegisters[4] = (ushort)DateTime.Now.Hour;         //时Data.InputRegisters[5] = (ushort)DateTime.Now.Minute;       //分Data.InputRegisters[6] = (ushort)DateTime.Now.Second;       //秒Data.InputRegisters[7] = (ushort)DateTime.Now.Millisecond;  //毫秒Random ran = new Random();Data.InputRegisters[8] = (ushort)ran.Next(0, 32767);        //产生的随机数}}
}

7.NModbus4模拟从站slave(ModbusRTU协议)

文章

public class slave_RTU{public ModbusSlave modbus_rtu_server;public void create(){SerialPort slavePort = new SerialPort();slavePort.PortName = "COM1";slavePort.BaudRate = 9600;slavePort.DataBits = 8;slavePort.Parity = Parity.Even;slavePort.StopBits = StopBits.One;slavePort.Open();byte slaveID =1;modbus_rtu_server = ModbusSerialSlave.CreateRtu(slaveID, slavePort);modbus_rtu_server.ModbusSlaveRequestReceived += new EventHandler<ModbusSlaveRequestEventArgs>(Modbus_Request_Event);modbus_rtu_server.DataStore = Modbus.Data.DataStoreFactory.CreateDefaultDataStore();modbus_rtu_server.DataStore.DataStoreWrittenTo += new EventHandler<DataStoreEventArgs>(Modbus_DataStoreWriteTo);modbus_rtu_server.DataStore.InputRegisters[1] = (ushort)DateTime.Now.Year;modbus_rtu_server.DataStore.InputRegisters[2] = (ushort)DateTime.Now.Year;modbus_rtu_server.DataStore.InputRegisters[3] = (ushort)DateTime.Now.Year;modbus_rtu_server.DataStore.CoilDiscretes[1] = true;modbus_rtu_server.DataStore.CoilDiscretes[2] = false;modbus_rtu_server.DataStore.CoilDiscretes[3] = false;modbus_rtu_server.Listen();}private void Modbus_Request_Event(object sender, Modbus.Device.ModbusSlaveRequestEventArgs e){try{//request from masterbyte fc = e.Message.FunctionCode;byte[] data = e.Message.MessageFrame;byte[] byteStartAddress = new byte[] { data[3], data[2] };byte[] byteNum = new byte[] { data[5], data[4] };Int16 StartAddress = BitConverter.ToInt16(byteStartAddress, 0);Int16 NumOfPoint = BitConverter.ToInt16(byteNum, 0);bool BOOL = true;string FCNUM = fc.ToString();if (fc.ToString() == "6"){//AOmodbus_rtu_server.DataStore.HoldingRegisters[StartAddress] = 16;modbus_rtu_server.DataStore.HoldingRegisters[StartAddress + 1] = 17;}Console.WriteLine(fc.ToString() + "," + StartAddress.ToString() + "," + NumOfPoint.ToString());}catch (Exception exc){}}private void Modbus_DataStoreWriteTo(object sender, Modbus.Data.DataStoreEventArgs e){//this.Text = "DataType=" + e.ModbusDataType.ToString() + "  StartAdress=" + e.StartAddress;int iAddress = e.StartAddress;//e.StartAddress;switch (e.ModbusDataType){case ModbusDataType.HoldingRegister:for (int i = 0; i < e.Data.B.Count; i++){//Set AO                 modbus_rtu_server.DataStore.HoldingRegisters[e.StartAddress + i + 1] = e.Data.B[i];//e.Data.B[i] already write to slave.DataStore.HoldingRegisters[e.StartAddress + i + 1]//e.StartAddress starts from 0//You can set AO value to hardware here//DoAOUpdate(iAddress, e.Data.B[i].ToString());iAddress++;}break;case ModbusDataType.Coil:for (int i = 0; i < e.Data.A.Count; i++){//Set DOmodbus_rtu_server.DataStore.CoilDiscretes[e.StartAddress + i + 1] = e.Data.A[i];//e.Data.A[i] already write to slave.DataStore.CoilDiscretes[e.StartAddress + i + 1]//e.StartAddress starts from 0//You can set DO value to hardware here//DoDOUpdate(iAddress, e.Data.A[i]);iAddress++;if (e.Data.A.Count == 1){break;}}break;}}}

8.modbusRTU、modbusTCP报文不同之处

在这里插入图片描述

二、明文TCP

博客
视频
在这里插入图片描述
在这里插入图片描述

using System.Net;//创建Socket套接字Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint point = new IPEndPoint(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));try { server.Bind(point); }catch (Exception ex){MessageBox.Show("无法启动服务器");}server.Listen(3);//Socket Client = server.Accept();//Accept 抓取的连接请求是客户端发来的string client = Client.RemoteEndPoint.ToString();MessageBox.Show(client+"连接了服务器");byte[] b = new byte[1024 * 1024 * 2];//缓冲器int length = 0;try{length = Client.Receive(b);}catch{MessageBox.Show(client + "失去连接");}if (length > 0){string msg = Encoding.Default.GetString(b, 0, length);Client.Send(Encoding.Default.GetBytes(textBox3.Text));}else{MessageBox.Show(client + "失去连接");}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/201515.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go 语言中的反射机制

欢迎大家到我的博客浏览&#xff0c;更好的阅读体验请点击 反射 | YinKais Blog 反射在大多数的应用和服务中并不常见&#xff0c;但是很多框架都依赖 Go 语言的反射机制简化代码。<!--more-->因为 Go 语言的语法元素很少、设计简单&#xff0c;所以它没有特别强的表达能…

Mysql的所有数据类型和它们的区别

一、数值类型 1. 普通整数数值类型 以下数据类型只能用以保存整数 整数数值类型类型存储大小&#xff08;字节&#xff09;有符号的取值范围&#xff08;允许存在负数&#xff09;无符号的取值范围TINYINT1-128 ~ 1270 ~ 255SMALLINT2- 327678 ~ 327670 ~ 65535MEDIUMINT3- 8…

华清远见嵌入式学习——C++——作业6

作业要求&#xff1a; 代码&#xff1a; #include <iostream>using namespace std;class Animal { public:virtual void perform() 0;};class Lion:public Animal { private:string foods;string feature; public:Lion(){}Lion(string foods,string feature):foods(foo…

【Spring Boot】如何在IntelliJ IDEA中由同一份spring boot源码运行多个不同端口的实例

我们需要使用一个服务有多个实例的测试场景&#xff0c;那么我们就需要在IntelliJ IDEA中通过不同的端口运行不同的实例&#xff0c;并且运行时的源代码是一样的&#xff0c;那么我们可以在IntelliJ IDEA这样操作&#xff0c;接下来以UserApplication服务为例&#xff1a; 复制…

使用Java API操作HDFS

文章目录 一、了解HDFS Java API&#xff08;一&#xff09;HDFS Java API概述1、配置&#xff08;Configuration&#xff09;2、文件系统&#xff08;FileSystem&#xff09;3、路径&#xff08;Path&#xff09;4、输入输出流&#xff08;FSDataInputStream 和 FSDataOutputS…

codeforces 题目 Powers Of Two

目录 题目&#xff1a; 题目描述&#xff1a; 思路&#xff1a; AC代码&#xff1a; 题目&#xff1a; 题目描述&#xff1a; 给你两个整数 n 和 k 问是否能找到 k 个2的幂&#xff0c;使其总和为 n 若能&#xff0c;则输出这 k 个 2的幂&#xff1b;若不能&#xff0c;…

预览控制;预见控制;预测控制;预观控制(preview control)

预演控制&#xff08;preview control&#xff09;作为一种新兴的控制方法&#xff0c;首次在轮式车辆中被提出。 参考文献&#xff1a; https://www.sciencedirect.com/science/article/pii/S0016003219300390https://www.sciencedirect.com/science/article/pii/S0016003219…

Ardupilot开源飞控之VTOL之旅:配件试装

Ardupilot开源飞控之VTOL之旅&#xff1a;配件试装 1. 源由2. 分析2.1 【修改使用】FC & PDB & GPS打印件2.2 【直接使用】VTX & CRSF打印件 3. 试装3.1 【结构】问题1&#xff1a;GPS座子尺寸非常紧凑&#xff0c;需要用力压入卡座内。3.2 【结构】问题2&#xff…

实验报告-实验四(时序系统实验)

软件模拟电路图 说明 SW&#xff1a;开关&#xff0c;共六个Q1~Q3&#xff1a;输出Y0~Y3&#xff1a;输出 74LS194 首先&#xff0c;要给S1和S0高电位&#xff0c;将A~D的数据存入寄存器中&#xff08;如果开始没有存入数据&#xff0c;那么就是0000在里面移位&#xff0c;不…

智慧小区园区如何布局网络对讲系统

智慧小区园区如何布局网络对讲系统 随着小区住宅的不断更新发展&#xff0c;小区的管理人员也对小区内部的通讯也有了新的要求&#xff0c;要求在工作区域无盲区、语音通讯清晰&#xff0c;小区的安保后勤都能够随时在小区的地下室和室外工作区域、任何时间进行通信。提高小区…

Python 云服务器应用,Https,定时重启

Python 云服务器应用,Https,定时重启 环境搭建Python模块模块导入生成Flask实例GET处理启动服务器打开网页验证 GET接入证书 支持https申请证书下载证书保留 xxx.crt 和 xxx.key文件就可以了 copy到python项目目录ssl_context 配置 宝塔面板操作在www目录下新建python工作目录在…

CRM立项正当时|走过复杂多变的2023年,明年如何锚定确定性增长?

正值年末&#xff0c;又到复盘今年、规划明年的重要节点。 2023年&#xff0c;黑天鹅和新风口轮番登场&#xff0c;当不确定成常态&#xff0c;环境愈发错综复杂&#xff0c;企业家们如何深谋远虑&#xff0c;带领企业实现可持续、高质量发展&#xff1f;这里提供三个思考视角…

LoadRunner12.55的简介与安装

提示&#xff1a;https://mp.weixin.qq.com/s/iK-fh0VP7v8mNSDNxjkBow 文章目录 LoadRunner的简介与安装loadrunner概述loadrunner的下载与安装 LoadRunner的使用启用VuGen LoadRunner的简介与安装 LoadRunner官网&#xff1a;https://www.microfocus.com/zh-cn/products/load…

智能优化算法应用:基于水基湍流算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于水基湍流算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于水基湍流算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.水基湍流算法4.实验参数设定5.算法结果6.参考…

递增子序列(回溯)

题目描述 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情况。 样例…

Unity使用打成图集的Sprite作为模型贴图使用的问题

大家好&#xff0c;我是阿赵。   有时候用Unity引擎做项目的时候&#xff0c;会遇到这样的需求&#xff0c;美术做了一些模型或者特效&#xff0c;然后策划想在游戏运行的时候&#xff0c;读取一些游戏图标放在特效或者模型上面当做贴图使用。   这个需求实现起来很简单&am…

销售人员如何自我提升?

销售人员如何自我提升&#xff1f; 在美国有这么一句流行语&#xff1a;不当总统就干销售员。其实在国内很多老板&#xff0c;高收入人群等大部分是来自销售岗位。因为销售是离钱最近的职业&#xff0c;在销售职业生涯中能收获到很多&#xff0c;比如人际关系能力&#xff0c;…

【刘二大人】pytorch深度学习实践(三):如何实现线性模型的反向传播+代码实现详解(Tensor、backward函数)

目录 参考资料一、反向传播流程1.1 问题1.2 方法1.3 步骤1.4 例题 二、Pytorch中前向传播和反馈的计算2.1 tensor数据类型2.2 定义线性模型并且计算损失2.2.1 torch.tensor.item()2.2.2 代码 2.3 反向传播2.3.1 torch.tensor.backward()2.3.2 tensor.zero_( )2.3.3 代码实现 三…

SystemWeaver—电子电气系统协同研发平台

背景概述 当前电子电气系统在汽车领域应用广泛&#xff0c;其设计整合了多门工程学科&#xff0c;也因系统的复杂性、关联性日益提升&#xff0c;需要其提供面向软件、硬件、网络、电气等多领域交织而导致的复杂系统解决方案。并且随着功能安全、AUTOSAR、SOA、以太网通讯等新要…

Linux基础命令(测试相关)

软件测试相关linux基础命令笔记 操作系统 常见Linux&#xff1a; Redhat系列&#xff1a;RHSL、Centos、FedoraDebian系列&#xff1a;Debian、Ubuntu以上操作系统都是在原生Linux系统上&#xff0c;增加了一些软件或功能。linux的文件及路径特点 Linux没有盘符的概念&#xf…