当前位置: 首页 > news >正文

C#MQTT编程08--MQTT服务器和客户端(cmd版)

1、前言

前面完成了winform版,wpf版,为什么要搞个cmd版,因为前面介绍了mqtt的报文结构,重点分析了【连接报文】,【订阅报文】,【发布报文】,这节就要就看看实际报文是怎么组装的,这也是之前详细每个报文的结构,含义的目的,使用mqttnet这个组件实现mqtt通信是直接应用,不涉及到底层报文的结构内容,用户是看不到报文内容的,这节的目的就是为加深理解而干的。这节不安装任何mqtt的组件,而是直接使用socket的原始方式通信。

2、报文回顾

一共有14个报文,如下图

可以去看看3个报文的详细介绍,什么固定报头,可变报头,有效载荷这些东东:

C#MQTT编程03--连接报文

C#MQTT编程04--订阅报文 

C#MQTT编程05--发布报文 

总结出来就是这样的:

连接报文是客户端发1,服务器回2,

订阅报文是客户端发8,服务器回9,

发布报文是客户端发3,服务器回4,

心跳报文是客户端发12,服务器回13。

3、开始卷

1、创建项目方案

 2、编写连接报文

 完整的连接代码:

/// <summary>
/// 连接
/// </summary>
static void Connection()
{// MQTT不支持UDPsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socket.Connect("127.0.0.1", 1869);//连接报文List<byte> connBytes = new List<byte>();#region 第一部分,固定报头// 第0个字节:固定报头List<byte> headerBytes = new List<byte>{1<<4 //表示连接请求  消息类型  };//第1个字节:剩余字节长度//需要后面计算得到#endregion#region 第二部分,可变报头 // 第2,3个字节:协议名称MQTT的字节长度List<byte> bodyBytes = new List<byte>();string protocolName = "MQTT";byte[] pnameBytes = Encoding.ASCII.GetBytes(protocolName);//得到“MQTT”的字节数组 bodyBytes.Add((byte)(pnameBytes.Length / 256 % 256));//高4位bodyBytes.Add((byte)(pnameBytes.Length % 256));//低4位// 第4,5,6,7个字节:协议名称bodyBytes.AddRange(pnameBytes);// 第8个字节: 协议版本bodyBytes.Add(0x04);// 第9个字节: 负载是否需要用户名,密码等设置byte flagByte = 0;flagByte |= 128;        //  1 0 0 0  0 0 0 0   128      // 需要用户 flagByte |= 64;         //  0 1 0 0  0 0 0 0   64      // 需要密码             flagByte |= 2;  // CleanSessionbodyBytes.Add(flagByte);//  第10,11个字节: Keep Alive保持连接的时间,高位在前,低位在后int seconds = 100;  // 秒为单位bodyBytes.Add((byte)(seconds / 256 % 256));bodyBytes.Add((byte)(seconds % 256));#endregion#region 第三部分,载荷List<byte> loadBytes = new List<byte>();//   第12,13个字节:ClientID字符长度string clientID = "x2";byte[] ciBytes = Encoding.ASCII.GetBytes(clientID);loadBytes.Add((byte)(ciBytes.Length / 256 % 256));loadBytes.Add((byte)(ciBytes.Length % 256));//   第14,115,16,17个字节:ClientIDloadBytes.AddRange(ciBytes);//   第18,19个字节:用户名长度string username = "boss";byte[] unBytes = Encoding.ASCII.GetBytes(username);loadBytes.Add((byte)(unBytes.Length / 256 % 256));loadBytes.Add((byte)(unBytes.Length % 256));//   第20,21,22,23,24个字节:用户名loadBytes.AddRange(unBytes);//   第25,26个字节:密码长度string pwd = "1234";byte[] pwdBytes = Encoding.ASCII.GetBytes(pwd);loadBytes.Add((byte)(pwdBytes.Length / 256 % 256));loadBytes.Add((byte)(pwdBytes.Length % 256));//   第27,28,29,30,31个字节:密码loadBytes.AddRange(pwdBytes);#endregion//第1个字节:剩余字节长度,从第 2 个字节开始。headerBytes.Add((byte)(bodyBytes.Count + loadBytes.Count));//组装成报文connBytes.AddRange(headerBytes);connBytes.AddRange(bodyBytes);connBytes.AddRange(loadBytes);//发送报文socket.Send(connBytes.ToArray());// 异步处理:开始心跳Task.Run(async () =>{byte[] pingBytes = new byte[2] { 12 << 4, 0 };//心跳的字节报文是固定的while (true){Console.WriteLine("心跳时间:" + DateTime.Now.ToString());await Task.Delay(1000);//等待1秒socket.Send(pingBytes);}});//异步处理:服务器返回的报文Task.Run(() =>{//1:请求连接 (C->S)//2:连接确认 (S->C)//3:发布消息 (Both)//4:发布收到确认 (QoS > 0)//5:发布确认收到//6:发布释放//7:发布完成 (QoS 2)//8:订阅请求 (C->S)//9:订阅请求确认 (S->C)//10:取消订阅请求 (C->S)//11:取消订阅请求确认 (S->C)//12:心跳请求 (C->S)//13:心跳确认 (S->C)//14:客户端断开连接 (C->S)byte[] respBytes = new byte[1]; //接收MQTT报文类型,报文类型占1个字节//连接成功,MQTT报文类型(CONNACK),服务器返回2,即0000 0010,高低位交换位置就是返回:0 0 1 0 0 0 0 0  ,转成10进制就是32//发布成功,MQTT报文类型(PUBACK), 服务器返回4,即0000 0100,高低位交换位置就是返回:0 1 0 0 0 0 0 0  ,转成10进制就是64//订阅成功,MQTT报文类型(SUBACK), 服务器返回9,即0000 1001,高低位交换位置就是返回:1 0 0 1 0 0 0 0  ,转成10进制就是144//心跳成功,MQTT报文类型(PINGRESP),服务器返回13,即0000 1101,高低位交换位置就是返回:1 1 0 1 0 0 0 0 ,转成10进制就是208while (true)//循环接收{try{socket.Receive(respBytes, 0, 1, SocketFlags.None);int firstValue = Convert.ToInt32(respBytes[0]);//Console.WriteLine("第一个字节:" + firstValue);//根据报文类型进行处理switch (firstValue){case 32:Console.WriteLine("连接成功!");break;case 64:Console.WriteLine("发布成功!");break;case 144:Console.WriteLine("订阅成功!");break;case 208:Console.WriteLine("心跳成功!");break;}}catch (Exception ex){Console.WriteLine("出错了," + ex.Message);}}});
}

特别注意这里的处理

 //连接成功,MQTT报文类型(CONNACK),服务器返回2,即0000 0010,高低位交换位置就是返回:0 0 1 0 0 0 0 0  ,转成10进制就是32//发布成功,MQTT报文类型(PUBACK), 服务器返回4,即0000 0100,高低位交换位置就是返回:0 1 0 0 0 0 0 0  ,转成10进制就是64//订阅成功,MQTT报文类型(SUBACK), 服务器返回9,即0000 1001,高低位交换位置就是返回:1 0 0 1 0 0 0 0  ,转成10进制就是144//心跳成功,MQTT报文类型(PINGRESP),服务器返回13,即0000 1101,高低位交换位置就是返回:1 1 0 1 0 0 0 0 ,转成10进制就是208

心跳的处理,它的作用是不断地发送命令,以证明客户端存在

 测试连接

先把前面的wpf版程序运行,启动服务器,启动客户端连接服务器

再启动本项目程序,可以看到连接成功,心跳也成功。

 

3、编写订阅报文

这里设置的qos级别是1,Qos级别-》 0:最多一次的传输,1:至少一次的传输、至多无限次,2:有且仅有一次的传输

完整代码

 

  /// <summary>/// 订阅/// </summary>/// <param name="topics">主题列表</param>static void Subscription(List<string> topics){List<byte> headerBytes = new List<byte>();List<byte> bodyBytes = new List<byte>();//第0个字节:报文类型(10000010)byte msgType = 8 << 4;   //  1000 0000 headerBytes.Add((byte)(msgType | 2));//第1个字节:剩余字节长度,等后面计算获取后再添加//第2,3个字节:Package Identifier的长度,表示报文的标识int pi = random.Next(0, 1000);  // Package Identifier的具体值bodyBytes.Add((byte)(pi / 256 % 256));//高位bodyBytes.Add((byte)(pi % 256));//低位//遍历所有主题foreach (var item in topics){//第8,9个字节:topic字符长度byte[] itemBytes = Encoding.UTF8.GetBytes(item);bodyBytes.Add((byte)(itemBytes.Length / 256 % 256));bodyBytes.Add((byte)(itemBytes.Length % 256));//第10,11,12,13,14,16个字节:topic字符内容bodyBytes.AddRange(itemBytes);//第17个字节:Qos级别-》 0:最多一次的传输,1:至少一次的传输、至多无限次,2:有且仅有一次的传输bodyBytes.Add(0x01);}//第1个字节:剩余字节长度,从第 2 个字节开始。headerBytes.Add((byte)bodyBytes.Count);//组成报文headerBytes.AddRange(bodyBytes);//发送报文socket.Send(headerBytes.ToArray());//接收服务器回应的报文 //byte[] respBytes = new byte[5];//socket.Receive(respBytes, 0, 5, SocketFlags.None);//var objSub = respBytes; }

测试订阅

先让wpf客户端订阅一个主题“shanghai",订阅成功

再看本项目程序订阅主题”shanghai",可以看到订阅成功

4、编写发布报文

完整代码,注释详情

 /// <summary>/// 发布消息:服务级别(Qos1)/// </summary>static void Publish_Qos1(){#region 方法1List<byte> headerBytes = new List<byte>();//报文类型byte msgType = 3 << 4;   //  1000 0000 headerBytes.Add((byte)(msgType | 2));   // QoS-0低4位全为1List<byte> bodyBytes = new List<byte>();string topic = "shanghai";string msg = "hello9098";// 添加主题长度byte[] topicBytes = Encoding.UTF8.GetBytes(topic);bodyBytes.Add((byte)(topicBytes.Length / 256 % 256));bodyBytes.Add((byte)(topicBytes.Length % 256));// 添加主题内容bodyBytes.AddRange(topicBytes);// 必须添加Package Identifier:只包括它的字节长度int pi = random.Next(0, 1000);  // Package Identifier//Console.WriteLine(pi);bodyBytes.Add((byte)(pi / 256 % 256));bodyBytes.Add((byte)(pi % 256));// 添加消息长度 byte[] msgBytes = Encoding.UTF8.GetBytes(msg);bodyBytes.Add((byte)(msgBytes.Length / 256 % 256));bodyBytes.Add((byte)(msgBytes.Length % 256));// 添加消息内容bodyBytes.AddRange(msgBytes);//添加第1个字节:剩余字节长度headerBytes.Add((byte)bodyBytes.Count);// 组装头   headerBytes.AddRange(bodyBytes);//发送消息socket.Send(headerBytes.ToArray());#endregion //#region 方法2//string topic = "shanghai";//string msg = "hello9098";//int pi = random.Next(0, 1000);  // Package Identifier//List<byte> topicbytes = new List<byte>();//byte[] topicArray = Encoding.UTF8.GetBytes(topic);//byte[] payloadArray = Encoding.UTF8.GetBytes(msg);//topicbytes.Add((byte)((int)topicArray.Length / 256));//topicbytes.Add((byte)((int)topicArray.Length % 256));//topicbytes.AddRange(topicArray);//byte[] id = new byte[] { (byte)(pi / 256 % 256), (byte)(pi % 256) };//byte[] bufferLen = new byte[] { (byte)(topicbytes.Count + payloadArray.Length + id.Length) };//using (MemoryStream memoryStream = new MemoryStream())//{//    memoryStream.WriteByte((3 << 4) | 2 | 1);// 写入消息类型(QoS-1)//    memoryStream.Write(bufferLen, 0, (int)bufferLen.Length);// 写入后续报文长度//    memoryStream.Write(topicbytes.ToArray(), 0, (int)topicbytes.Count);// 写入Topic字节//    memoryStream.Write(id.ToArray(), 0, (int)id.Length);// 写入Package Identifier字节//    memoryStream.Write(payloadArray.ToArray(), 0, (int)payloadArray.Length);// 写入消息//    byte[] sendArray = memoryStream.ToArray();//    socket.Send(sendArray);//}接收服务器回应的报文 //byte[] respBytes = new byte[4];//socket.Receive(respBytes, 0, 4, SocketFlags.None);//var objSub = respBytes;//#endregion }

测试发布

 前面的c1订阅了主题“shanghai",现在的x2客户端向shanghai主题发布一个消息,看看c1能不能收到

 

最后全部完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace MQTTNETClientCMD
{internal class Program{static Socket socket;//socket对象static Random random = new Random();//随机数,用于产生package identifierstatic List<string> topic = new List<string> { "shanghai" };//主题static void Main(string[] args){Console.WriteLine("Hello MQTT!");Connection();//连接  Subscription(topic);//订阅Publish_Qos1();// 发布Qos=1Console.ReadKey();}/// <summary>/// 连接/// </summary>static void Connection(){// MQTT不支持UDPsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socket.Connect("127.0.0.1", 1869);//连接报文List<byte> connBytes = new List<byte>();#region 第一部分,固定报头// 第0个字节:固定报头List<byte> headerBytes = new List<byte>{1<<4 //表示连接请求  消息类型  };//第1个字节:剩余字节长度//需要后面计算得到#endregion#region 第二部分,可变报头 // 第2,3个字节:协议名称MQTT的字节长度List<byte> bodyBytes = new List<byte>();string protocolName = "MQTT";byte[] pnameBytes = Encoding.ASCII.GetBytes(protocolName);//得到“MQTT”的字节数组 bodyBytes.Add((byte)(pnameBytes.Length / 256 % 256));//高4位bodyBytes.Add((byte)(pnameBytes.Length % 256));//低4位// 第4,5,6,7个字节:协议名称bodyBytes.AddRange(pnameBytes);// 第8个字节: 协议版本bodyBytes.Add(0x04);// 第9个字节: 负载是否需要用户名,密码等设置byte flagByte = 0;flagByte |= 128;        //  1 0 0 0  0 0 0 0   128      // 需要用户 flagByte |= 64;         //  0 1 0 0  0 0 0 0   64      // 需要密码             flagByte |= 2;  // CleanSessionbodyBytes.Add(flagByte);//  第10,11个字节: Keep Alive保持连接的时间,高位在前,低位在后int seconds = 100;  // 秒为单位bodyBytes.Add((byte)(seconds / 256 % 256));bodyBytes.Add((byte)(seconds % 256));#endregion#region 第三部分,载荷List<byte> loadBytes = new List<byte>();//   第12,13个字节:ClientID字符长度string clientID = "x2";byte[] ciBytes = Encoding.ASCII.GetBytes(clientID);loadBytes.Add((byte)(ciBytes.Length / 256 % 256));loadBytes.Add((byte)(ciBytes.Length % 256));//   第14,115,16,17个字节:ClientIDloadBytes.AddRange(ciBytes);//   第18,19个字节:用户名长度string username = "boss";byte[] unBytes = Encoding.ASCII.GetBytes(username);loadBytes.Add((byte)(unBytes.Length / 256 % 256));loadBytes.Add((byte)(unBytes.Length % 256));//   第20,21,22,23,24个字节:用户名loadBytes.AddRange(unBytes);//   第25,26个字节:密码长度string pwd = "1234";byte[] pwdBytes = Encoding.ASCII.GetBytes(pwd);loadBytes.Add((byte)(pwdBytes.Length / 256 % 256));loadBytes.Add((byte)(pwdBytes.Length % 256));//   第27,28,29,30,31个字节:密码loadBytes.AddRange(pwdBytes);#endregion//第1个字节:剩余字节长度,从第 2 个字节开始。headerBytes.Add((byte)(bodyBytes.Count + loadBytes.Count));//组装成报文connBytes.AddRange(headerBytes);connBytes.AddRange(bodyBytes);connBytes.AddRange(loadBytes);//发送报文socket.Send(connBytes.ToArray());// 异步处理:开始心跳Task.Run(async () =>{byte[] pingBytes = new byte[2] { 12 << 4, 0 };//心跳的字节报文是固定的while (true){Console.WriteLine("心跳时间:" + DateTime.Now.ToString());await Task.Delay(1000);//等待1秒socket.Send(pingBytes);}});//异步处理:服务器返回的报文Task.Run(() =>{//1:请求连接 (C->S)//2:连接确认 (S->C)//3:发布消息 (Both)//4:发布收到确认 (QoS > 0)//5:发布确认收到//6:发布释放//7:发布完成 (QoS 2)//8:订阅请求 (C->S)//9:订阅请求确认 (S->C)//10:取消订阅请求 (C->S)//11:取消订阅请求确认 (S->C)//12:心跳请求 (C->S)//13:心跳确认 (S->C)//14:客户端断开连接 (C->S)byte[] respBytes = new byte[1]; //接收MQTT报文类型,报文类型占1个字节//连接成功,MQTT报文类型(CONNACK),服务器返回2,即0000 0010,高低位交换位置就是返回:0 0 1 0 0 0 0 0  ,转成10进制就是32//发布成功,MQTT报文类型(PUBACK), 服务器返回4,即0000 0100,高低位交换位置就是返回:0 1 0 0 0 0 0 0  ,转成10进制就是64//订阅成功,MQTT报文类型(SUBACK), 服务器返回9,即0000 1001,高低位交换位置就是返回:1 0 0 1 0 0 0 0  ,转成10进制就是144//心跳成功,MQTT报文类型(PINGRESP),服务器返回13,即0000 1101,高低位交换位置就是返回:1 1 0 1 0 0 0 0 ,转成10进制就是208while (true)//循环接收{try{socket.Receive(respBytes, 0, 1, SocketFlags.None);int firstValue = Convert.ToInt32(respBytes[0]);//Console.WriteLine("第一个字节:" + firstValue);//根据报文类型进行处理switch (firstValue){case 32:Console.WriteLine("连接成功!");break;case 64:Console.WriteLine("发布成功!");break;case 144:Console.WriteLine("订阅成功!");break;case 208:Console.WriteLine("心跳成功!");break;}}catch (Exception ex){Console.WriteLine("出错了," + ex.Message);}}});}/// <summary>/// 订阅/// </summary>/// <param name="topics">主题列表</param>static void Subscription(List<string> topics){List<byte> headerBytes = new List<byte>();List<byte> bodyBytes = new List<byte>();//第0个字节:报文类型(10000010)byte msgType = 8 << 4;   //  1000 0000 headerBytes.Add((byte)(msgType | 2));//第1个字节:剩余字节长度,等后面计算获取后再添加//第2,3个字节:Package Identifier的长度,表示报文的标识int pi = random.Next(0, 1000);  // Package Identifier的具体值bodyBytes.Add((byte)(pi / 256 % 256));//高位bodyBytes.Add((byte)(pi % 256));//低位//遍历所有主题foreach (var item in topics){//第8,9个字节:topic字符长度byte[] itemBytes = Encoding.UTF8.GetBytes(item);bodyBytes.Add((byte)(itemBytes.Length / 256 % 256));bodyBytes.Add((byte)(itemBytes.Length % 256));//第10,11,12,13,14,16个字节:topic字符内容bodyBytes.AddRange(itemBytes);//第17个字节:Qos级别-》 0:最多一次的传输,1:至少一次的传输、至多无限次,2:有且仅有一次的传输bodyBytes.Add(0x01);}//第1个字节:剩余字节长度,从第 2 个字节开始。headerBytes.Add((byte)bodyBytes.Count);//组成报文headerBytes.AddRange(bodyBytes);//发送报文socket.Send(headerBytes.ToArray());//接收服务器回应的报文 //byte[] respBytes = new byte[5];//socket.Receive(respBytes, 0, 5, SocketFlags.None);//var objSub = respBytes; }/// <summary>/// 发布消息:服务级别(Qos1)/// </summary>static void Publish_Qos1(){#region 方法1List<byte> headerBytes = new List<byte>();//报文类型byte msgType = 3 << 4;   //  1000 0000 headerBytes.Add((byte)(msgType | 2));   // QoS-0低4位全为1List<byte> bodyBytes = new List<byte>();string topic = "shanghai";string msg = "hello9098";// 添加主题长度byte[] topicBytes = Encoding.UTF8.GetBytes(topic);bodyBytes.Add((byte)(topicBytes.Length / 256 % 256));bodyBytes.Add((byte)(topicBytes.Length % 256));// 添加主题内容bodyBytes.AddRange(topicBytes);// 必须添加Package Identifier:只包括它的字节长度int pi = random.Next(0, 1000);  // Package Identifier//Console.WriteLine(pi);bodyBytes.Add((byte)(pi / 256 % 256));bodyBytes.Add((byte)(pi % 256));// 添加消息长度 byte[] msgBytes = Encoding.UTF8.GetBytes(msg);bodyBytes.Add((byte)(msgBytes.Length / 256 % 256));bodyBytes.Add((byte)(msgBytes.Length % 256));// 添加消息内容bodyBytes.AddRange(msgBytes);//添加第1个字节:剩余字节长度headerBytes.Add((byte)bodyBytes.Count);// 组装头   headerBytes.AddRange(bodyBytes);//发送消息socket.Send(headerBytes.ToArray());#endregion //#region 方法2//string topic = "shanghai";//string msg = "hello9098";//int pi = random.Next(0, 1000);  // Package Identifier//List<byte> topicbytes = new List<byte>();//byte[] topicArray = Encoding.UTF8.GetBytes(topic);//byte[] payloadArray = Encoding.UTF8.GetBytes(msg);//topicbytes.Add((byte)((int)topicArray.Length / 256));//topicbytes.Add((byte)((int)topicArray.Length % 256));//topicbytes.AddRange(topicArray);//byte[] id = new byte[] { (byte)(pi / 256 % 256), (byte)(pi % 256) };//byte[] bufferLen = new byte[] { (byte)(topicbytes.Count + payloadArray.Length + id.Length) };//using (MemoryStream memoryStream = new MemoryStream())//{//    memoryStream.WriteByte((3 << 4) | 2 | 1);// 写入消息类型(QoS-1)//    memoryStream.Write(bufferLen, 0, (int)bufferLen.Length);// 写入后续报文长度//    memoryStream.Write(topicbytes.ToArray(), 0, (int)topicbytes.Count);// 写入Topic字节//    memoryStream.Write(id.ToArray(), 0, (int)id.Length);// 写入Package Identifier字节//    memoryStream.Write(payloadArray.ToArray(), 0, (int)payloadArray.Length);// 写入消息//    byte[] sendArray = memoryStream.ToArray();//    socket.Send(sendArray);//}接收服务器回应的报文 //byte[] respBytes = new byte[4];//socket.Receive(respBytes, 0, 4, SocketFlags.None);//var objSub = respBytes;//#endregion }}
}

 

讲解不易,分析不易,原创不易,整理不易,伙伴们动动你的金手指,你的支持是我最大的动力。

讲解不易,分析不易,原创不易,整理不易,伙伴们动动你的金手指,你的支持是我最大的动力。

相关文章:

C#MQTT编程08--MQTT服务器和客户端(cmd版)

1、前言 前面完成了winform版&#xff0c;wpf版&#xff0c;为什么要搞个cmd版&#xff0c;因为前面介绍了mqtt的报文结构&#xff0c;重点分析了【连接报文】&#xff0c;【订阅报文】&#xff0c;【发布报文】&#xff0c;这节就要就看看实际报文是怎么组装的&#xff0c;这…...

【高等数学之牛莱公式】

一、深入挖掘定积分 二、变限积分 三、变限积分的"天然"连续性 四、微积分基本定理 五、定积分基本方法 5.1、换元法 5.2、分部积分法 六、定积分经典结论 七、区间再现公式 八、三角函数积分变换公式 九、周期函数积分变换公式 十、分段函数求定积分...

基于HFSS的微带线特性阻抗仿真-与基于FDTD的计算电磁学方法对比(Matlab)

基于HFSS的微带线特性阻抗仿真-与基于FDTD的计算电磁学方法对比&#xff08;Matlab&#xff09; 工程下载&#xff1a; HFSS的微带线特性阻抗仿真工程文件&#xff08;注意版本&#xff1a;HFSS2023R2&#xff09;&#xff1a; https://download.csdn.net/download/weixin_445…...

【SQL】SQL语法小结

相关资料 参考链接1&#xff1a;SQL 语法&#xff08;超级详细&#xff09; 参考链接2&#xff1a;史上超强最常用SQL语句大全 SQL练习网站&#xff1a;CSDN、牛客、LeetCode、LintCode SQL相关视频&#xff1a; 推荐书籍&#xff1a; 文章目录 数据分析对SQL的要求SQL语法简介…...

Open CASCADE学习|显示模型

目录 1、编写代码 Viewer.h Viewer.cpp ViewerInteractor.h ViewerInteractor.cpp helloworld.cpp 2、配置 3、编译运行 1、编写代码 Viewer.h #pragma once ​ #ifdef _WIN32 #include <Windows.h> #endif ​ // Local includes #include "ViewerInteract…...

【C++】string的基本使用

从这篇博客开始&#xff0c;我们的C部分就进入到了STL&#xff0c;STL的出现可以说是C发展历史上非常关键的一步&#xff0c;自此C和C语言有了较为明显的差别。那么什么是STL呢&#xff1f; 后来不断的演化&#xff0c;发展成了知名的两个版本&#xff0c;一个叫做P.J.版本&am…...

vue 里 props 类型为 Object 时设置 default: () => {} 返回的是 undefined 而不是 {}?

问题 今天遇到个小坑&#xff0c;就是 vue 里使用 props 传参类型为 Object 的时候设置 default: () > {} 报错&#xff0c;具体代码如下 <template><div class"pre-archive-info"><template v-if"infoData.kaimo ! null">{{ infoD…...

04 SpringMVC响应数据之页面跳转控制+返回JSON数据+返回静态资源

1. handler方法分析 /*** TODO: 一个controller的方法是控制层的一个处理器,我们称为handler* TODO: handler需要使用RequestMapping/GetMapping系列,声明路径,在HandlerMapping中注册,供DS查找!* TODO: handler作用总结:* 1.接收请求参数(param,json,pathVariable,共享…...

Python圣诞主题绘图:用turtle库打造冬日奇妙画面【第31篇—python:圣诞节】

文章目录 Python圣诞主题绘图导言代码结构概览详细解析drawlight函数tree函数xzs函数drawsnow函数五角星的绘制 完整代码代码解析总结 Python圣诞主题绘图 导言 圣诞季节是个充满欢乐和创意的时刻。在这个技术博客中&#xff0c;我们将深入探讨如何使用Python的turtle库创建一…...

[开发语言][c++]:Static关键字和全局变量

Static关键字和全局变量 1. 生命周期、作用域和初始化时机2. 全局变量3. Static 关键字3.1 面向过程3.1.1 静态全局变量3.1.2 静态局部变量&#xff08;单例中会使用&#xff09;3.1.3 静态函数 3.2 面向对象3.2.1 类内静态成员变量3.2.2 类内静态成员函数 Reference 写在前面&…...

计算机组成原理 第一弹

ps&#xff1a;本文章的图片来源都是来自于湖科大教书匠高老师的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1-2 计算机的发展_哔哩哔哩_bilibili ​​ 目录 &#x1f680;计算机系统 &#x1f680;计…...

Hadoop基础知识

Hadoop基础知识 1、Hadoop简介 广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念——Hadoop生态圈。狭义上说&#xff0c;Hadoop指Apache这款开源框架&#xff0c;它的核心组件有&#xff1a; HDFS&#xff08;分布式文件系统&#xff09;&#xff1a;解决海量数据存储Y…...

Java进阶之旅第五天

Java进阶之旅第五天 不可变集合 应用场景 1.如果某个数据不能被修改,把它拷贝到不可变集合中是个很好的实践2.当集合对象被不可信的库调用时,不可变形式是安全的3.不可变集合不能修改,只能进行查询 获取方式 在List,Set,Map接口中,都存在静态的of方法,可以获取一个不可变的…...

拓展边界:前端世界的跨域挑战

目录 什么是跨域 概念 同源策略及限制内容 常见跨域场景 如何解决跨域 CORS Nginx代理跨域 Node中间件代理跨域 WebSocket postMessage JSONP 其他 什么是跨域 概念 在此之前&#xff0c;我们了解一下一个域名地址的组成&#xff1a; 跨域指的是在网络安全中&…...

旅游项目day03

1. 前端整合后端发短信接口 2. 注册功能 后端提供注册接口&#xff0c;接受前端传入的参数&#xff0c;创建新的用户对象&#xff0c;保存到数据库。 接口设计&#xff1a; 实现步骤&#xff1a; 手机号码唯一性校验&#xff08;后端一定要再次校验手机号唯一性&#xff09…...

单片机学习记录(一)

简答题 第1章 1.微处理器、微计算机、CPU、单片机、嵌入式处理器他们之间有何区别&#xff1f; 答&#xff1a;微处理器、CPU都是中央处理器的不同称谓&#xff0c;微处理器芯片本身不是计算机&#xff1b; 单片机、微计算机都是一个完整的计算机系统&#xff0c;单片机是集…...

MacBookPro怎么数据恢复? mac电脑数据恢复?

使用电脑的用户都知道&#xff0c;被删除的文件一般都会经过回收站&#xff0c;想要恢复它直接点击“还原”就可以恢复到原始位置。mac电脑同理也是这样&#xff0c;但是“回收站”在mac电脑显示为“废纸篓”。 如果电脑回收站&#xff0c;或者是废纸篓里面的数据被清空了&…...

Python多线程—threading模块

参考&#xff1a;《Python核心编程》 threading 模块的Thread 类是主要的执行对象&#xff0c;而且&#xff0c;使用Thread类可以有很多方法来创建线程&#xff0c;这里介绍以下两种方法&#xff1a; 创建 Thread 实例&#xff0c;传给它一个函数。派生 Thread 的子类&#xf…...

mysql limit

语法 SELECT * FROM TABLE_NAME LIMIT 起始位置&#xff0c;偏移量注&#xff1a; 起始位置从0开始 示例 查询的第1条数据到第100条数据 limit 0,100查询的第101条数据到第200条数据 limit 100,100注意不要用 limit 101,100示例2 limit 语句应放在order by语句后面执行 …...

解决国内Linux服务器无法使用Github的方法

解决思路&#xff1a;修改Host https://www.ipaddress.com/ 利用上面的网站查询github.com和raw.githubusercontent.com的DNS解析的IP地址 最后&#xff0c;修改服务器的/etc/hosts 添加如下两行&#xff1a; 140.82.112.3 github.com 185.199.108.133 raw.githubuserconte…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...