Unity网络开发基础 —— 实践小项目
概述
接Unity网络开发基础
导入基础知识中的代码
需求分析
手动写Handler类
手动书写消息池
using GamePlayer;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 消息池中 主要是用于 注册 ID和消息类型以及消息处理器类型的映射关系
/// 方便我们获取对象 进行反序列化和消息逻辑处理
/// </summary>
public class MsgPool
{//记录消息类型和ID的映射关系private Dictionary<int, Type> messages = new Dictionary<int, Type>();//记录消息处理器类型和ID的映射关系private Dictionary<int, Type> handlers = new Dictionary<int, Type>();public MsgPool(){//在构造函数中进行 注册 注册映射关系Register(1001, typeof(PlayerMsg), typeof(PlayerMsgHandler));//后面继续添加消息类}private void Register(int id, Type messageType, Type handlerType){messages.Add(id, messageType);handlers.Add(id, handlerType);}/// <summary>/// 根据ID 得到一个指定的消息类对象/// </summary>/// <param name="id"></param>/// <returns></returns>public BaseMsg GetMessage(int id){if (!messages.ContainsKey(id))return null;return Activator.CreateInstance(messages[id]) as BaseMsg;}/// <summary>/// 根据ID 得到一个指定的消息处理类对象/// </summary>/// <param name="id"></param>/// <returns></returns>public BaseHandler GetHandler(int id){if (!handlers.ContainsKey(id))return null;return Activator.CreateInstance(handlers[id]) as BaseHandler;}}
工具生成Handler
工具生成消息池
源码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using UnityEngine;namespace GamePlayer
{public enum E_Player_Type{Main,Other,}public class PlayerTest : BaseData{public List<int> list;public Dictionary<int, string> dic;public int[] arrays;public E_Player_Type type;public PlayerData player;public override int GetByteNum(){int num = 0;num += 4; //list.Countfor (int i = 0; i < list.Count; i++){num += 4;}num += 4; //dic.Countforeach (int key in dic.Keys){num += 4;//key所占的字节数num += 4;//value 字符串长度 占的字节数num += Encoding.UTF8.GetByteCount(dic[key]);}num += 4; //arrays.Length 数组长度for (int i = 0; i < arrays.Length; i++){num += 4;}num += 4; //枚举 用int来存num += player.GetByteNum(); //PlayerDatareturn num;}public override int Reading(byte[] bytes, int beginIndex = 0){int index = beginIndex;//反序列化 listlist = new List<int>();short listCount = ReadShort(bytes, ref index); for (int i = 0; i < listCount; i++){list.Add(ReadInt(bytes, ref index));}//dicdic = new Dictionary<int, string>();short dicCount = ReadShort(bytes, ref index); for (int i = 0; i < dicCount; i++){dic.Add(ReadInt(bytes, ref index), ReadString(bytes, ref index));}//arraysint arrayLength = ReadShort(bytes, ref index);arrays = new int[arrayLength];for (int i = 0; i < arrayLength; i++){arrays[i] = ReadInt(bytes, ref index);}//枚举type = (E_Player_Type)ReadInt(bytes, ref index);//自定义类型player = ReadData<PlayerData>(bytes, ref index);return index - beginIndex;}public override byte[] Writing(){//固定内容int index = 0;byte[] bytes = new byte[GetByteNum()];//可变的 是根据成员变量来决定如何拼接的//存储 list的长度WriteShort(bytes, (short)list.Count, ref index);for (int i = 0; i < list.Count; i++){WriteInt(bytes, list[i], ref index);}//存储 dic//先长度WriteShort(bytes, (short)dic.Count, ref index);foreach (int key in dic.Keys){WriteInt(bytes, key, ref index);WriteString(bytes, dic[key], ref index);}//存储 数组WriteShort(bytes, (short)arrays.Length, ref index);for (int i = 0; i < arrays.Length; i++){WriteInt(bytes, arrays[i], ref index);}//存储 枚举 (我们用一个 int 来存储枚举)type 是一个枚举类型WriteInt(bytes, Convert.ToInt32(type), ref index);//存储 自定义数据结构类WriteData(bytes, player, ref index);//固定内容return bytes;}}
}public class GenerateCSharp
{//协议保存路径private string SAVE_PATH = Application.dataPath + "/Scripts/Protocol/";//生成枚举public void GenerateEnum(XmlNodeList nodes){//生成枚举脚本的逻辑string namespaceStr = "";string enumNameStr = "";string fieldStr = "";foreach (XmlNode enumNode in nodes){//获取命名空间配置信息namespaceStr = enumNode.Attributes["namespace"].Value;//获取枚举名配置信息enumNameStr = enumNode.Attributes["name"].Value;//获取所有的字段节点 然后进行字符串拼接XmlNodeList enumFields = enumNode.SelectNodes("field");//清空上一次的数据fieldStr = "";foreach (XmlNode enumField in enumFields){fieldStr += "\t\t" + enumField.Attributes["name"].Value;if (enumField.InnerText != "")fieldStr += " = " + enumField.InnerText;fieldStr += ",\r\n";}//对所有可变的内容进行拼接string enumStr = $"namespace {namespaceStr}\r\n" + "{\r\n" + $"\tpublic enum {enumNameStr}\r\n" +"\t{\r\n" +$"{fieldStr}" + "\t}\r\n" + "}";//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Enum/";//如果不存在该文件夹 则创建if (!Directory.Exists(path)){Directory.CreateDirectory(path);}//字符串保存 存储为枚举脚本文件//参数一:文件名//参数二:转换成string 的 数据File.WriteAllText(path + enumNameStr + ".cs", enumStr);}Debug.Log("枚举生成结束");}//生成数据结构类public void GenerateData(XmlNodeList nodes){string namespaceStr = "";string classNameStr = "";string fieldStr = "";//将 GetBytesNum方法 写成字符串string getByteNumStr = "";// 将 Writing方法 写成字符串string writingStr = "";//将 Reading方法 写成字符串string readingStr = "";foreach (XmlNode dataNode in nodes){//命名空间namespaceStr = dataNode.Attributes["namespace"].Value;//类名classNameStr = dataNode.Attributes["name"].Value;//读取所有字段节点XmlNodeList fields = dataNode.SelectNodes("field");//通过这个方法进行成员变量声明的拼接 返回拼接结果fieldStr = GetFieldStr(fields);//通过某个方法 对GetBytesNum函数中的字符串内容进行拼接 返回结果getByteNumStr = GetGetBytesNumStr(fields);//通过某个方法 对Writing函数中的字符串内容进行拼接 返回结果writingStr = GetWritingStr(fields);//通过某个方法 对Reading函数中的字符串内容进行拼接 返回结果readingStr = GetReadingStr(fields);string dataStr = "using System.Collections.Generic;\r\n" +"using System.Text;\r\n" +"using System;\r\n\r\n" + $"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr} : BaseData\r\n" +"\t{\r\n" +$"{fieldStr}\r\n" + "\t\tpublic override int GetByteNum()\r\n" + "\t\t{\r\n" + "\t\t\tint num = 0;\r\n" + $"{getByteNumStr}" + "\t\t\treturn num;\r\n" + "\t\t}\r\n\r\n" + "\t\tpublic override byte[] Writing()\r\n" + "\t\t{\r\n" + "\t\t\tint index = 0;\r\n" + "\t\t\tbyte[] bytes = new byte[GetByteNum()];\r\n" + $"{writingStr}" + "\t\t\treturn bytes;\r\n" + "\t\t}\r\n\r\n" +"\t\tpublic override int Reading(byte[] bytes, int beginIndex = 0)\r\n" +"\t\t{\r\n" +"\t\t\tint index = beginIndex;\r\n" + $"{readingStr}" +"\t\t\treturn index - beginIndex;\r\n" +"\t\t}\r\n\r\n" +"\t}\r\n" +"}";//保存为 脚本对象//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Data/";//如果没有该路径 就创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为数据结构类脚本File.WriteAllText(path + classNameStr + ".cs", dataStr);}Debug.Log("数据结构类生成结束");}//生成消息类public void CenerateMsg(XmlNodeList nodes){string namespaceStr = "";string classNameStr = "";string idStr = "";string fieldStr = "";//将 GetBytesNum方法 写成字符串string getByteNumStr = "";// 将 Writing方法 写成字符串string writingStr = "";//将 Reading方法 写成字符串string readingStr = "";foreach (XmlNode dataNode in nodes){//命名空间namespaceStr = dataNode.Attributes["namespace"].Value;//类名classNameStr = dataNode.Attributes["name"].Value;//消息ididStr = dataNode.Attributes["id"].Value;//读取所有字段节点XmlNodeList fields = dataNode.SelectNodes("field");//通过这个方法进行成员变量声明的拼接 返回拼接结果fieldStr = GetFieldStr(fields);//通过某个方法 对GetBytesNum函数中的字符串内容进行拼接 返回结果getByteNumStr = GetGetBytesNumStr(fields);//通过某个方法 对Writing函数中的字符串内容进行拼接 返回结果writingStr = GetWritingStr(fields);//通过某个方法 对Reading函数中的字符串内容进行拼接 返回结果readingStr = GetReadingStr(fields);string dataStr = "using System.Collections.Generic;\r\n" +"using System.Text;\r\n" +"using System;\r\n\r\n" +$"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr} : BaseMsg\r\n" +"\t{\r\n" +$"{fieldStr}\r\n" +"\t\tpublic override int GetByteNum()\r\n" +"\t\t{\r\n" +"\t\t\tint num = 8;\r\n" + //这个8代表的是 消息ID的4个字节 + 消息体长度的4个字节$"{getByteNumStr}" +"\t\t\treturn num;\r\n" +"\t\t}\r\n\r\n" +"\t\tpublic override byte[] Writing()\r\n" +"\t\t{\r\n" +"\t\t\tint index = 0;\r\n" +"\t\t\tbyte[] bytes = new byte[GetByteNum()];\r\n" +"\t\t\tWriteInt(bytes, GetID(), ref index);\r\n" + "\t\t\tWriteInt(bytes, bytes.Length - 8, ref index);\r\n" + $"{writingStr}" +"\t\t\treturn bytes;\r\n" +"\t\t}\r\n\r\n" +"\t\tpublic override int Reading(byte[] bytes, int beginIndex = 0)\r\n" +"\t\t{\r\n" +"\t\t\tint index = beginIndex;\r\n" +$"{readingStr}" +"\t\t\treturn index - beginIndex;\r\n" +"\t\t}\r\n\r\n" +"\t\tpublic override int GetID()\r\n" +"\t\t{\r\n" +"\t\t\treturn " + idStr + ";\r\n" +"\t\t}\r\n\r\n" +"\t}\r\n" +"}";//保存为 脚本对象//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Msg/";//如果没有该路径 就创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为数据结构类脚本File.WriteAllText(path + classNameStr + ".cs", dataStr);//生成处理器脚本//判断消息处理器脚本是否存在 如果存在 就不要覆盖了 避免把写过的逻辑处理代码覆盖了//如果想要改变 那就直接把没用的删了 它就会自动生成if (File.Exists(path + classNameStr + "Handler.cs"))continue;string handlerStr = $"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr}Handler : BaseHandler\r\n" + "\t{\r\n" + "\t\tpublic override void MsgHandle()\r\n" +"\t\t{\r\n" + $"\t\t\t{classNameStr} msg = message as {classNameStr};\r\n" + "\t\t}\r\n" + "\t}\r\n" + "}\r\n";//把消息处理器类的内容保存到本地File.WriteAllText(path + classNameStr + "Handler.cs", handlerStr);}Debug.Log("消息类生成结束");}//生成消息池 主要就是ID和消息类型以及消息处理器类型的对应关系public void GenerateMsgPool(XmlNodeList nodes){List<string> ids = new List<string>();List<string> names = new List<string>();List<string> nameSpaces = new List<string>();foreach (XmlNode dataNode in nodes){//记录所有消息的IDstring id = dataNode.Attributes["id"].Value;if (!ids.Contains(id))ids.Add(id);elseDebug.LogError("存在相同ID的消息" + id);//记录所有消息的名字string name = dataNode.Attributes["name"].Value;if (!names.Contains(name))names.Add(name);elseDebug.LogError("存在相同名字的消息" + id + ",建议即使在不同命名空间中也不要要同名的信息");//获取所有消息的命名空间string msgNamespace = dataNode.Attributes["namespace"].Value;if (!nameSpaces.Contains(msgNamespace))nameSpaces.Add(msgNamespace);//获取所有消息注册相关的内容string registerStr = "";for (int i = 0; i < ids.Count; i++){registerStr += $"\t\tRegister({ids[i]}, typeof({names[i]}), typeof({names[i]}Handler));\r\n";}//获取所有需要引用的命名空间 string nameSpaceStr = "";for (int i = 0; i < nameSpaces.Count; i++){nameSpaceStr += $"using {nameSpaces[i]};\r\n";}//消息池对应的类的字符串信息string msgPoolStr = "using System;\r\n" +"using System.Collections.Generic;\r\n" +nameSpaceStr +"public class MsgPool\r\n" +"{\r\n" +"\tprivate Dictionary<int, Type> messages = new Dictionary<int, Type>();\r\n" +"\tprivate Dictionary<int, Type> handlers = new Dictionary<int, Type>();\r\n\r\n" +"\tpublic MsgPool()\r\n" +"\t{\r\n" +registerStr +"\t}\r\n" +"\tpublic void Register(int id, Type messageType, Type handlerType)\r\n" +"\t{\r\n" +"\t\tmessages.Add(id, messageType);\r\n" +"\t\thandlers.Add(id, handlerType);\r\n" +"\t}\r\n" +"\tpublic BaseMsg GetMessage(int id)\r\n" +"\t{\r\n" +"\t\tif (!messages.ContainsKey(id))\r\n" +"\t\t\treturn null;\r\n" +"\t\treturn Activator.CreateInstance(messages[id]) as BaseMsg;\r\n" +"\t}\r\n" +"\tpublic BaseHandler GetHandler(int id)\r\n" +"\t{\r\n" +"\t\tif (!handlers.ContainsKey(id))\r\n" +"\t\t\treturn null;\r\n" +"\t\treturn Activator.CreateInstance(handlers[id]) as BaseHandler;\r\n" +"\t}\r\n" +"}\r\n";//看看有没有这个路径string path = SAVE_PATH + "/Pool/";if (!Directory.Exists(path))Directory.CreateDirectory(path);//保存到本地File.WriteAllText(path + "MsgPool.cs", msgPoolStr);Debug.Log("消息池生成结束");}}/// <summary>/// 获取成员变量声明内容/// </summary>/// <param name="fields"></param>/// <returns></returns>private string GetFieldStr(XmlNodeList fields){string fieldStr = "";foreach (XmlNode field in fields){//变量类型string type = field.Attributes["type"].Value;//变量名string fieldName = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;fieldStr += "\t\tpublic " + "List<" + T + "> ";}else if (type == "array"){string data = field.Attributes["data"].Value;fieldStr += "\t\tpublic " + data + "[] ";}else if(type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;fieldStr += "\t\tpublic " + "Dictionary<" + Tkey + ", " + Tvalue + "> ";}else if(type == "enum"){string data = field.Attributes["data"].Value;fieldStr += "\t\tpublic " + data + " ";}else{fieldStr += "\t\tpublic " + type + " ";}fieldStr += fieldName + ";\r\n";}return fieldStr;}/// <summary>/// 拼接 GetBytesNum函数的方法/// </summary>/// <param name="fields"></param>/// <returns></returns>private string GetGetBytesNumStr(XmlNodeList fields){string bytesNumStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;bytesNumStr += "\t\t\tnum += 2;\r\n"; // 2 是为了节约字节数 用一个short去存储数量信息bytesNumStr += "\t\t\tfor(int i = 0; i < " + name + ".Count; i++)\r\n";//这里使用的是 name + [i] 目的是获取 list当中的元素传入进行使用bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(T, name + "[i]") + ";\r\n";}else if (type == "array"){string data = field.Attributes["data"].Value;bytesNumStr += "\t\t\tnum += 2;\r\n";bytesNumStr += "\t\t\tfor(int i = 0; i < " + name + ".Length; i++)\r\n";bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(data, name + "[i]") + ";\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;bytesNumStr += "\t\t\tnum += 2;\r\n";bytesNumStr += "\t\t\tforeach(" + Tkey + " key in " + name + ".Keys)\r\n";bytesNumStr += "\t\t\t{\r\n";bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(Tkey, "key") + ";\r\n";bytesNumStr += "\t\t\t\tnum += " + GetValueBytesNum(Tvalue, name + "[key]") + ";\r\n";bytesNumStr += "\t\t\t}\r\n";}elsebytesNumStr += "\t\t\tnum += " + GetValueBytesNum(type, name) + ";\r\n";}return bytesNumStr;}//获取指定类型的字节数private string GetValueBytesNum(string type, string name){//其它类型根据需求添加switch (type){case "int":case "float":case "enum":return "4";case "bool":case "byte":return "1";case "short":return "2";case "long":case "double":return "8";case "string":return "4 + Encoding.UTF8.GetByteCount(" + name + ")";default:return name + ".GetByteNum()";}}/// <summary>/// 拼接 Writing函数的方法/// </summary>/// <param name="field"></param>/// <returns></returns>private string GetWritingStr(XmlNodeList fields){string writingStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Count, ref index);\r\n";writingStr += "\t\t\tfor(int i = 0; i < " + name + ".Count; i++)\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(T, name + "[i]") + "\r\n";}else if(type == "array"){string data = field.Attributes["data"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Length, ref index);\r\n";writingStr += "\t\t\tfor(int i = 0; i < " + name + ".Length; i++)\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(data, name + "[i]") + "\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Count, ref index);\r\n";writingStr += "\t\t\tforeach(" + Tkey + " key in " + name + ".Keys)\r\n";writingStr += "\t\t\t{\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(Tkey, "key") + "\r\n";writingStr += "\t\t\t\t" + GetFieldWritingStr(Tvalue, name + "[key]") + "\r\n";writingStr += "\t\t\t}\r\n";}else{writingStr += "\t\t\t" + GetFieldWritingStr(type, name) + "\r\n";}}return writingStr;}private string GetFieldWritingStr(string type, string name){switch (type){case "byte":return "WriteByte(bytes, " + name + ", ref index);";case "int":return "WriteInt(bytes, " + name + ", ref index);";case "short":return "WriteShort(bytes, " + name + ", ref index);";case "long":return "WriteLong(bytes, " + name + ", ref index);";case "float":return "WriteFloat(bytes, " + name + ", ref index);";case "bool":return "WriteBool(bytes, " + name + ", ref index);";case "string":return "WriteString(bytes, " + name + ", ref index);";case "enum":return "WriteInt(bytes, Convert.ToInt32(" + name + "), ref index);"; default:return "WriteData(bytes, " + name + ", ref index);";}}//private string GetReadingStr(XmlNodeList fields){string readingStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;readingStr += "\t\t\t" + name + " = new List<" + T + ">();\r\n";readingStr += "\t\t\tshort " + name + "Count = ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\tfor(int i = 0; i < " + name + "Count; i++)\r\n";readingStr += "\t\t\t\t" + name + ".Add(" + GetFieldReadingStr(T) + ");\r\n";}else if (type == "array"){string data = field.Attributes["data"].Value; readingStr += "\t\t\tshort " + name + "Length = ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\t" + name + " = new " + data + "[" + name + "Length];\r\n";readingStr += "\t\t\tfor(int i = 0; i < " + name + "Length; i++)\r\n";readingStr += "\t\t\t\t" + name + "[i] = " + GetFieldReadingStr(data) + ";\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;readingStr += "\t\t\t" + name + " = new Dictionary<" + Tkey + ", " + Tvalue + ">();\r\n";readingStr += "\t\t\tshort " + name + "Count = ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\tfor(int i = 0; i < " + name + "Count; i++)\r\n";readingStr += "\t\t\t\t" + name + ".Add(" + GetFieldReadingStr(Tkey)+ ", " + GetFieldReadingStr(Tvalue) + ");\r\n";}else if (type == "enum"){string data = field.Attributes["data"].Value;readingStr += "\t\t\t" + name + " = (" + data + ")ReadInt(bytes, ref index);\r\n"; }elsereadingStr += "\t\t\t" + name + " = " + GetFieldReadingStr(type) + ";\r\n";}return readingStr;}private string GetFieldReadingStr(string type){switch (type){case "byte":return "ReadByte(bytes, ref index)";case "int":return "ReadInt(bytes, ref index)";case "short":return "ReadShort(bytes, ref index)";case "long":return "ReadLong(bytes, ref index)";case "bool":return "ReadBool(bytes, ref index)";case "float":return "ReadFloat(bytes, ref index)";case "string":return "ReadString(bytes, ref index)";default:return "ReadData<" + type + ">(bytes, ref index)";}}}
断线重连
总结
相关文章:

Unity网络开发基础 —— 实践小项目
概述 接Unity网络开发基础 导入基础知识中的代码 需求分析 手动写Handler类 手动书写消息池 using GamePlayer; using System; using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 消息池中 主要是用于 注册 ID和消息类…...

四、Spring Boot集成Spring Security之认证流程
Spring Boot集成Spring Security之认证流程 一、概要说明二、基于内存的用户名密码1、默认用户名密码2、自定义用户名密码3、为方便测试添加测试接口TestController 三、登录登出重要概念介绍四、登录业务逻辑1、登录业务相关过滤器2、访问业务请求处理流程①、访问业务请求地址…...

Chromium 中chrome.bookmarks扩展接口c++实现
一、扩展接口定义 chrome.bookmarks 使用 chrome.bookmarks API 创建、整理以及以其他方式操纵书签。另请参阅覆盖网页(可用于创建自定义“书签管理器”页面)。 更多参考chrome.bookmarks | API | Chrome for Developers (google.cn) 扩展可以请从…...

编程思想:编程范式:响应式编程
文章目录 概述实现的设计模式举例总结概述 响应 响应一般指对于事件的响应,事件包括数据变化或其他事件 响应流程包括事件的发生,事件的传递,和事件的最终处理 事件在起点处发生,开始传递过程 传递过程,包括对事件的一系列处理,如事件封装的数据的类型转化,数据集合…...

Leetcode 颜色分类
这个算法采用了荷兰国旗问题(Dutch National Flag Problem)的解法思想,用三个指针将数组中的元素分为三个区域,并且对这些区域进行动态调整,达到排序的目的。 算法思想: 三个指针: low 指针表示…...

ssh连接阿里云长连接
如何让ssh保持连接? 有时候用ssh连接阿里云莫名奇妙断开了。怎么样才能保持连接呢? 修改系统的链接参数: (1)修改/etc/ssh/sshd_config文件,找到 ClientAliveInterval 0和ClientAliveCountMax 3并将注释符号&#x…...

栈的C实现
栈的C实现 栈简介栈的C实现1.栈结构体2.初始化栈3.栈的基本操作 栈简介 栈(Stack)是一种后进先出的数据结构,类似于一个垂直的容器。 栈的特点是后进先出,即最后入栈的元素最先出栈。栈可以用来解决递归问题、实现函数调用、以及…...

【MySQL】入门篇—数据库基础:关系数据库概念
一、背景与重要性 在当今数字化时代,数据的管理和存储变得尤为重要。无论是企业的客户信息、产品数据,还是社交媒体上的用户互动,数据都是推动业务和决策的核心。 关系数据库管理系统(RDBMS)是一种广泛使用的数据管理…...

不到千元的自动猫砂盆是智商税吗?这四大选购技巧不看就亏大了
虽然现在的人都说,猫砂盆等上班一天回来再清理也没有任何关系,但实际上在这一天里,猫咪的粪便已经在猫砂盆里滋生了很多无法察觉的细菌,久而久之就会影响猫咪的健康,导致尿闭,放了一天的便便臭味也让人无法…...

【图论】(二)图论基础与路径问题
图论基础与路径问题 图的构造邻接矩阵邻接表 所有可达路径邻接矩阵存储邻接表存储 字符串接龙有向图的完全可达性 图的构造 这里仅对图论路径问题中图的构造做整理总结归纳,具体详细相关概念请参考代码随想录上的整理总结: 图论理论基础深度优先搜索理…...

Git常用命令(持续更新中)
mkdir one 在当前目录下创建一个名为one的文件夹 cd one 进入one 文件夹 git init 初始化git 仓库 touch README.md 创建一个后缀为.md的新文件README.md git add README.md 将README.md添加到git暂存区 git add * . * 将所有文件添加到暂存区 git add "E:/t…...

什么是PLM系统?PLM系统对制造业起到哪些作用?三品PLM系统对汽车制造业意义
在当今竞争激烈的制造业环境中,企业面临着来自市场、技术、客户需求等多方面的挑战。为了应对这些挑战,许多制造企业纷纷引入产品生命周期管理PLM系统,以实现更高效、更灵活的产品全生命周期管理。PLM系统以其独特的优势,在优化产…...

Pr 视频效果:元数据和时间码刻录
视频效果/视频/元数据和时间码刻录 Video/Metadata & Timecode Burn-in 元数据和时间码刻录 Metadata & Timecode Burn-in效果是一种在视频画面上叠加显示剪辑元数据或时间码的工具。它允许在导出视频时,将需用的元数据信息直接刻录在画面上,方便…...

前端MD5加密
1.导入包 npm install --save ts-md5 2.使用方式 import { Md5 } from ts-md5;//md5加密后的密码 const md5PwdMd5.hashStr("123456").toUpperCase(); 3. Vue解析token中携带的数据 3.1 安装插件 npm install jwt-decode --save 3.2 引入 import {jwtDecode} fro…...

仿IOS桌面悬浮球(支持拖拽、自动吸附、自动改变透明度与点击、兼容PC端与移动端)
使用 pointerdown/pointermove/pointerup 实现仿IOS桌面悬浮球效果,支持拖拽、指定拖拽选对容器,指定拖拽安全区、自动吸附、自动改变透明度与点击,兼容PC端与移动端。 效果展示 https://code.juejin.cn/pen/7423757568268304421 代码实现 …...

智谱开放平台API调用解析
一、什么是智谱AI 智谱AI成立于2019年,由清华大学计算机系知识工程实验室的技术成果转化而来,是一家致力于人工智能技术研发和应用的公司。智谱致力于打造新一代认知智能大模型,专注于做大模型的中国创新。 二、智谱开放平台API调用 官方文…...

Linux中定时删除10天前的日志文件
例如:删除/data/log/目录下所有10天前的.log文件 find /data/log/ -type f -name "*.log" -mtime 10 -exec rm -f {} \;只查看要删除的文件有哪些,不真正删除文件 logfiles$(find /data/log/ -type f -name "*.log" -mtime 10) ec…...

贝壳Android面试题及参考答案
详细说Final关键字 在编程语言中,final关键字具有重要的作用。以下为你详细介绍final关键字: 一、final关键字的主要作用 修饰变量 当final修饰基本数据类型变量时,该变量的值一旦被初始化就不能再被改变。例如:final int num = 10;num = 20; // 这会导致编译错误当final修…...

基于vue的酒店预订管理系统(源码+定制+开发)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...

FreeRTOS——TCB任务控制块、任务句柄、任务栈详解
任务控制块结构体 任务控制块是 FreeRTOS 中用于描述和管理任务的数据结构,包含了任务的状态、优先级、堆栈等信息。 TCB_t的全称为Task Control Block,也就是任务控制块,这个结构体包含了一个任务所有的信息,它的定义以及相关变…...

【STM32单片机_(HAL库)】4-5-2【定时器TIM】【感应开关盖垃圾桶项目】HC-SR04超声波模块实验
1.硬件 STM32单片机最小系统HC-SR04超声波模块 2.软件 hcsr04驱动文件添加main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "uart1.h" #include "hcsr04.h"int main(void) {HAL_Init(); …...

安全网络架构
网络安全解决方案是指通过一系列技术和措施来保护网络系统和数据的安全。它涉及多个方面,包括网络设备的防护、数据的加密和备份、安全策略的制定和执行等。以下是一些常见的网络安全解决方案: 防火墙:防火墙是一种硬件或软件设备,…...

【万字长文】Word2Vec计算详解(二)Skip-gram模型
【万字长文】Word2Vec计算详解(二)Skip-gram模型 写在前面 本篇介绍Word2Vec中的第二个模型Skip-gram模型 【万字长文】Word2Vec计算详解(一)CBOW模型 markdown行 9000 【万字长文】Word2Vec计算详解(二)S…...

随机掉落的项目足迹:解决TypeError: Cannot read properties of undefined (reading ‘push‘)报错
问题引入 下面是request.js中请求拦截器相关的代码 但是运行时却出现了报错 问题解决 useRouter() 是 Vue Router 提供的组合式 API,它只能在 Vue 组件的 setup() 函数中有效。如果在其他地方(例如 Axios 的拦截器中)调用它,可…...

ChatTTS 本地安装和测试
Ubuntu 22服务器,3.9/3.10都可以,但是 3.11不可以 sudo apt install python3.10 apt install python3.10 python3.10-dev #ubuntu 22 安装python3.10对应的pip3.10 # 下载 get-pip.py curl -sS https://bootstrap.pypa.io/get-pip.py -o get-pip.py # 使…...

[Leetcode] 560 Subarray Sum Equals K
题意:给定一个数组求连续的子数组的和为k的有几个 Input: nums [1,1,1], k 2 Output: 2 https://leetcode.com/problems/subarray-sum-equals-k/description/ 首先思考1.因为是subarray sum前缀和很容易想到,那问题就转化成preSum[i] preSum[j] - k…...

TCL Android面试题大全及参考答案
能谈谈Jetpack组件吗? Jetpack 是一套用于 Android 开发的工具和组件库,它可以帮助开发者更高效地构建高质量的 Android 应用。 一、主要组件分类 架构组件: ViewModel:负责存储和管理与界面相关的数据,当屏幕旋转或配置发生变化时,ViewModel 可以帮助保存数据,避免数据…...

JVM错误:OutOfMemoryError: GC overhead limit exceeded
OutOfMemoryError: GC overhead limit exceeded 在Window服务器上跑一个项目,无意中出现服务访问不了,查看日志文档,第一次遇到了这个异常信息。 1. 错误含义 OutOfMemoryError: GC overhead limit exceeded 是 JVM 中的一种错误ÿ…...

Unity网络开发 - C#开源网络通信库PESocket的使用
概述 在现代多人在线游戏中,稳定且高效的网络通信是确保游戏体验的关键。本文将探讨如何利用C#开源网络通信库PESocket来构建一个简单的Unity客户端与.NET控制台服务器之间的实时消息传递系统。通过本例,读者不仅能够了解PESocket的基本用法,…...

【完-网络安全】Shell与脚本
文章目录 1.CLI与GUI2.终端和Shell2.1 Shell 壳层2.2 终端2.3 终端和Shell区别3.标准流 4.PowerShell4.1 管理员与非管理员4.2 指令4.3 重定向4.4 管道 5.环境变量5.1 影响范围5.2环境变量的作用5.3 常见的环境变量 6.脚本 1.CLI与GUI CLI命令行界面(CLl,Command Line Interfa…...