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.rhkb.cn/news/211300.html

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

相关文章

Redis 入门、基础。(五种基本类型使用场景)

文章目录 1. 概况1.1 认识 NoSQL1.1.1 查询方式1.1.2 事务1.1.3 总结 2. 认识 Redis4. Redis 常见命令4.1 Redis 数据结构介绍4.2 Redis 通用命令4.3 Redis 命令之 String 命令4.4 Redis 命令的层级结构4.5 Redis 命令之 Hash 命令4.6 Redis 命令之 List 命令4.7 set 唯一不排序…

DOM 事件的注册和移除

前端面试大全DOM 事件的注册和移除 &#x1f31f;经典真题 &#x1f31f;DOM 注册事件 HTML 元素中注册事件 DOM0 级方式注册事件 DOM2 级方式注册事件 &#x1f31f;DOM 移除事件 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 总结一下 DOM 中如何…

Linux简单部署Yearning并结合内网穿透工具发布至公网可访问

目录 前言 1. Linux 部署Yearning 2. 本地访问Yearning 3. Linux 安装cpolar 4. 配置Yearning公网访问地址 5. 公网远程访问Yearning管理界面 6. 固定Yearning公网地址 前言 Yearning 简单, 高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开发…

深度学习之图像分类(十五)DINAT: Dilated Neighborhood Attention Transformer详解(一)

Dilated Neighborhood Attention Transformer Abstract Transformers 迅速成为跨模态、领域和任务中应用最广泛的深度学习架构之一。在视觉领域&#xff0c;除了对普通Transformer的持续努力外&#xff0c;分层Transformer也因其性能和易于集成到现有框架中而受到重视。这些模…

Domino多Web站点托管

大家好&#xff0c;才是真的好。 看到一篇文档&#xff0c;大概讲述的是他在家里架了一台Domino服务器&#xff0c;上面跑了好几个Internet的Web网站&#xff08;使用Internet站点&#xff09;。再租了一台云服务器&#xff0c;上面安装Nginx做了反向代理&#xff0c;代理访问…

GPTS-生成一个动漫图像GPT

介绍 GPTs是ChatGPT的定制版本,用户可以通过组合指令、知识和功能来定制用于特定任务或主题的GPT。它们可以根据需要简单或复杂,解决从语言学习到技术支持等各种事情。 创建GPTs Plus和Enterprise用户可以在chat.openai.com/create上开始创建GPTs。 您可以通过在ChatGPT上的…

【头歌系统数据库实验】实验4 MySQL单表查询

目录 第1关. 在users表中新增一个用户&#xff0c;user_id为2019100904学号&#xff0c;name为2019-物联网-李明 第2关. 在users表中更新用户 user_id为robot_2 的信息&#xff0c;name设为 机器人二号 第3关. 将solution表中所有 problem_id 为1003 题目的解答结果&#xf…

车联网架构设计(二)_消息缓存

在上一篇博客车联网架构设计(一)_消息平台的搭建-CSDN博客中&#xff0c;我介绍了车联网平台需要实现的一些功能&#xff0c;并介绍了如何用EMQXHAPROXY来搭建一个MQTT消息平台。车联网平台的应用需要消费车辆发布的消息&#xff0c;同时也会下发消息给车辆&#xff0c;以实现车…

UE4/UE5 材质实现带框环形进度条

UE4/UE5 材质实现带框环形进度条 此处使用版本&#xff1a;UE4.27 原理&#xff1a;大圆减小圆可以得到圆环&#xff0c;大圆环减小圆环&#xff0c;可以得到圆环外围线框 实现效果&#xff1a; 实现&#xff08;为了给大家放进一张面前能看的图&#xff0c;我费劲了心思&…

6-55.汽车类的继承

根据给定的汽车类vehicle&#xff08;包含的数据成员有车轮个数wheels和车重weight&#xff09;声明&#xff0c;完成其中成员函数的定义&#xff0c;之后再定义其派生类并完成测试。 小车类car是它的派生类&#xff0c;其中包含载人数passenger_load。每个类都有相关数据的输出…

使用 Mybatis 的 TypeHandler 存取 Postgresql jsonb 类型

文章目录 使用 TypeHandler 存取 Postgresql jsonb 类型常见错误column "" is of type jsonb but expression is of type character varying 使用 TypeHandler 存取 Postgresql jsonb 类型 首先在数据库表中定义 jsonb 类型&#xff1a; create table tb_user_info…

MyCAT读写分离

Mycat 是一个开源的数据库系统&#xff0c;但是由于真正的数据库需要存储引擎&#xff0c;而 Mycat 并没有存 储引擎&#xff0c;所以并不是完全意义的数据库系统。 那么 Mycat 是什么&#xff1f;Mycat 是数据库中间件&#xff0c;就是介于数据库与应用之间&#xff0c;进行数…

打工人副业变现秘籍,某多/某手变现底层引擎-StableDiffusionUI引擎部署

Stable Diffusion Web UI是一个基于Stable Diffusion的交互式程序,使用gradio模块构建而成。除了基本的txt2img、img2img等功能外,该模块还包含许多模型融合改进、图片质量修复等附加升级。所有这些功能都可以通过易于使用的Web应用程序图形用户界面进行访问。 一、简介 St…

MQTT框架和使用

目录 MQTT框架 1. MQTT概述 1.1 形象地理解三个角色 1.2 消息的传递 2. 在Windows上体验MQTT 2.1 安装APP 2.2 启动服务器 2.3 使用MQTTX 2.3.1 建立连接 2.3.2 订阅主题 2.3.3 发布主题 2.4 使用mosquitto 2.4.1 发布消息 2.4.2 订阅消息 3. kawaii-mqtt源码分析…

git bash查看远程仓库地址

进入代码路径 git remote -vgit remote -v

LeetCode力扣每日一题(Java):13、罗马数字转整数

一、题目 二、解题思路 1、我的思路 整体思路是将字符串转成字符&#xff0c;再遍历每一个字符&#xff0c;找到各个罗马字母对应的数值大小&#xff0c;同时需要将当前罗马字母的数值大小&#xff08;后文称为“前”&#xff09;与后一个罗马字母的数值大小&#xff08;后文…

计算机视觉GPT时刻!UC伯克利三巨头祭出首个纯CV大模型,推理惊现AGI火花

计算机视觉的GPT时刻&#xff0c;来了&#xff01; 最近&#xff0c;来自UC伯克利的计算机视觉「三巨头」联手推出了第一个无自然语言的纯视觉大模型&#xff08;Large Vision Models&#xff09;&#xff0c;并且第一次证明了纯视觉模型本身也是可扩展的&#xff08;scalabil…

【GIT】.gitignore 在忽略目录中放开某目录

示例&#xff1a;忽略build下面的所有目录&#xff0c;只放开build/ast2500-default/workspace/recipes-phosphor/ 目录 .gitignore 实现文件代码 # 忽略 build 目录下的所有目录 # 并放开build/ast2500-default/workspace/recipes-phosphor/ build/* !build/ast2500-defaul…

微信小程序收款手续费怎么搞成0.2

今天&#xff0c;我将分享如何有效地降低日常中的收款手续费率。我们都知道&#xff0c;不管是微信支付还是支付宝&#xff0c;平台都会从中扣除一定的手续费。但你是否知道&#xff0c;其实手续费率是可以降低的呢&#xff1f;今天介绍如何申请最低手续费率为0.2%的方法&#…

梯度下降(批量梯度下降、随机梯度下降、小批量梯度下降)

在上一篇中我们推导了损失函数 J ( θ ) 1 2 m ∑ i 1 m ( y i − h θ ( x i ) ) 2 J(\theta) \frac{1}{2m} \sum_{i1}^{m} (y^{i} - h_{\theta}(x^{i}))^2 J(θ)2m1​∑i1m​(yi−hθ​(xi))2的由来&#xff0c;结尾讲到最小化这个损失函数来找到最优的参数 θ \theta θ&…