Unity网络开发快速回顾
知识点来源:总结人间自有韬哥在, 唐老狮,豆包
目录
- 1.网络通信-通信必备知识-IP地址和端口类
- 2.网络通信中序列化和反序列化2进制数据
- 3.Socket类
- 4.TCP同步服务端和客户端基础实现
- 4.1.服务端基本实现
- 4.2.客户端实现:
- 5.区分消息类型
- 6.分包和粘包
- 7.TCP同步退出消息和心跳消息
- 7.1.客服端主动断开连接
- 7.2.心跳消息
- 8.Socket类TCP异步常用成员
- 9.UDP
- 10.Socket类UDP异步常用方法
- 11.FTP
- 11.1.FTP上传文件
- 11.2.FTP下载文件
- 12.HTTP
- 13.WWW
- 14.WWWForm
- 15.UnityWebRequest
- 15.1.常用上传和获取数据
- 15.2.高级上传和获取数据
- 16.Protobuf
- 16.1.Protobuf配置规则
- 16.2.Protobuf协议使用
- 序列化存储为本地文件
- 反序列化本地文件
- 得到序列化后的字节数组
- 从字节数组反序列化
- 17.大小端模式
1.网络通信-通信必备知识-IP地址和端口类
分类 | 详情 |
---|---|
IPAddress类 | 命名空间:System.Net |
IPEndPoint类 | 命名空间:System.Net |
IPHostEntry | 作为域名解析返回值,用于获取IP、主机名等信息。成员变量有:AddressList(获取关联IP)、Aliases(获取主机别名列表)、HostName(获取DNS名称) |
Dns | 静态类,提供根据域名获取IP的方法。常用方法有:GetHostName(获取本地主机名,如 |
2.网络通信中序列化和反序列化2进制数据
类名 | 详情 |
---|---|
BitConverter | 所在命名空间:System |
Encoding | 所在命名空间:System.Text |
3.Socket类
类名 | 详情 |
---|---|
Socket | 命名空间:System.Net.Sockets |
4.TCP同步服务端和客户端基础实现
4.1.服务端基本实现
(1)创建套接字Socket对象(TCP):
//1.创建套接字Socket(TCP)
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(2)用Bind方法将套接字与本地地址绑定:
//2.用Bind方法将套接字与本地地址绑定
try
{IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);//把本机作为服务端程序 IP地址传入本机socketTcp.Bind(iPEndPoint);//绑定
}
catch (Exception e)
{//如果IP地址不合法或者端口号被占用可能报错Console.WriteLine("绑定报错" + e.Message);return;
}
(3)用Listen方法监听:
//3.用Listen方法监听
socketTcp.Listen(1024);//最大接收1024个客户端
Console.WriteLine("服务端绑定监听结束,等待客户端连入");
(4)用Accept方法等待客户端连接,建立连接,Accept返回新套接字:
//5.建立连接,Accept返回新套接字
Socket socketClient = socketTcp.Accept();
//Accept是阻塞式的方法 会把主线程卡主 一定要等到客户端接入后才会继续执行后面的代码
//客户端接入后 返回新的Socket对象 这个新的Socket可以理解为客户段和服务端的通信通道
Console.WriteLine("有客户端连入了");
(5)用Send和Receive相关方法收发数据:
//6.用Send和Receive相关方法收发数据
//发送字符串转成的字节数组给客户端
socketClient.Send(Encoding.UTF8.GetBytes("欢迎连入服务端"));
//声明接受客户端信息的字节数组 声明1024容量代表能接受1kb的信息
byte[] result = new byte[1024];
//接受客户端信息 返回值为接受到的字节数
int receiveNum = socketClient.Receive(result);
//打印 远程发送信息的客户端的IP和端口 以及 发送过来的字符串
Console.WriteLine("接受到了{0}发来的消息:{1}",socketClient.RemoteEndPoint.ToString(),Encoding.UTF8.GetString(result, 0, receiveNum));
(6)用Shutdown方法释放连接:
//7.用Shutdown方法释放连接
//注意断开的是客户段和服务端的通信通道
socketClient.Shutdown(SocketShutdown.Both);
(7)关闭套接字:
//8.关闭套接字
//注意关闭的是客户段和服务端的通信通道
socketClient.Close();
4.2.客户端实现:
(1)创建套接字Socket Tcp
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(2)用Connect方法与服务端相连:
//确定服务端的IP和端口 正常来说填的应该是远端服务器的ip地址以及端口号
//由于只有一台电脑用于测试 本机也当做服务器 所以传入当前电脑的ip地址
IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
try
{//连接socketTcp.Connect(iPEndPoint);
}
catch (SocketException e)
{//如果连接没有开启或者服务器异常 会报错 不同的返回码代表不同报错if (e.ErrorCode == 10061)print("服务器拒绝连接");elseprint("连接服务器失败" + e.ErrorCode);return;
}
(3)用Send和Receive相关方法收发数据:
//接收数据
//声明接收数据字节数组
byte[] receiveBytes = new byte[1024];
//Receive方法接受数据 返回接收多少字节
int receiveNum = socketTcp.Receive(receiveBytes);print("收到服务端发来的消息:" + Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));//发送数据
socketTcp.Send(Encoding.UTF8.GetBytes("你好,我是韬老狮的客户端"));
(4)用Shutdown方法释放连接:
//4.用Shutdown方法释放连接
socketTcp.Shutdown(SocketShutdown.Both);
(5)关闭套接字:
//5.关闭套接字
socketTcp.Close();
5.区分消息类型
为发送的信息添加标识,比如添加消息 ID。在所有发送的消息的头部加上消息 ID(可以是 int、short、byte、long)。
举例说明:
如果选用 int 类型作为消息 ID 的类型,前 4 个字节为消息 ID,后面的字节为数据类的内容,这样每次收到消息时,先把前 4 个字节取出来解析为消息 ID,再根据 ID 进行消息反序列化即可。
6.分包和粘包
什么是分包、黏包?
分包、黏包指在网络通信中由于各种因素(网络环境、API规则等)造成的消息与消息之间出现的两种状态。
分包:一个消息分成了多个消息进行发送。
黏包:一个消息和另一个消息黏在了一起。
注意:分包和黏包可能同时发生。
解决思路:为消息添加头部记录长度,依据长度判断并处理分包、黏包,仅处理完整消息。
7.TCP同步退出消息和心跳消息
7.1.客服端主动断开连接
- 解决思路及主要修改逻辑
- 客户端:使用
Disconnect
方法主动断开连接,在TcpNetManager
的Close
方法中,依次执行关闭套接字发送和接收、手动停止连接、关闭套接字连接、将套接字置空以及标记连接已关闭等操作。 - 服务端
- 在
ServerSocket
中添加CloseClientSocket
方法,用于关闭客户端连接并从字典中移除,操作客户端对象字典时添加线程锁以保证线程安全。 - 在
ServerSocket
的其他操作或访问字典的方法(如AcceptClientConnect
、ReceiveClientMessage
、Broadcast
)中添加线程锁,确保字典操作的线程安全性。 - 为避免在遍历字典进行消息收发操作时直接移除客户端导致报错,在
ServerSocket
中创建List
存储待移除的客户端socket(delList
),并定义AddDelSocket
方法用于添加待移除的socket。 - 将
Program
的serverSocket
改为静态变量,在ClientSocket
的发送和接收消息方法中,检测到断开连接或解析报错时,将当前客户端添加到服务端待移除的客户端列表中。
- 在
- 客户端:使用
7.2.心跳消息
什么是心跳消息
所谓心跳消息,就是在长连接中,客户端和服务端之间定期发送的一种特殊的数据包,用于通知对方自己还在线,以确保长连接的有效性。由于其发送的时间间隔往往是固定的持续的,就像是心跳一样一直存在,所以我们称之为心跳消息。
为什么需要心跳消息
避免非正常关闭客户端时,服务器无法正常收到关闭连接消息。通过心跳消息我们可以自定义超时判断,如果超时没有收到客户端消息,证明客户端已经断开连接。避免客户端长期不发送消息,防火墙或者路由器会断开连接,我们可以通过心跳消息一直保持活跃状态。
实现心跳消息
客户端:主要功能是定时发送消息。
服务器端:主要功能是不停检测上次收到某客户端消息的时间,如果超时则认为连接已经断开。
8.Socket类TCP异步常用成员
类名 | 方法 | 参数 | 详情 |
---|---|---|---|
Socket | BeginAccept(AsyncCallback callback, object state) | callback :处理异步接受客户端连接操作完成时的回调函数;state :服务端Socket 对象 | 服务端开始接收客户端的连接。 |
Socket | EndAccept(IAsyncResult asyncResult) | asyncResult :BeginAccept 方法返回的IAsyncResult 对象 | 服务端检测到客户端的连接结束,得到客户端Socket 。 |
Socket | BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state) | remoteEP :服务器的终结点(如IPEndPoint );callback :处理客户端异步连接操作完成时的回调函数;state :客户端的Socket 对象 | 客户端异步连接到服务器。 |
Socket | EndConnect(IAsyncResult asyncResult) | asyncResult :BeginConnect 方法返回的IAsyncResult 对象 | 客户端完成异步连接到服务器端的操作。 |
Socket | BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) | buffer :接收消息的字节数组;offset :从接收消息的字节数组第几位开始存储;size :字节数组长度;socketFlags :Socket 标识;callback :接收消息回调函数;state :Socket 对象 | 开始接收消息。 |
Socket | EndReceive(IAsyncResult asyncResult) | asyncResult :BeginReceive 方法返回的IAsyncResult 对象 | 结束接收消息,返回接收的字节数组长度。 |
Socket | BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) | buffer :发送消息的字节数组;offset :从发送消息的字节数组第几位开始发送;size :字节数组长度;socketFlags :Socket 标识;callback :发送消息回调函数;state :Socket 对象 | 开始发送消息。 |
Socket | EndSend(IAsyncResult asyncResult) | asyncResult :BeginSend 方法返回的IAsyncResult 对象 | 结束发送消息,返回成功发送的字节数。 |
Socket | AcceptAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 对象,用于处理异步接受客户端连接操作完成后的操作 | 服务端异步接受客户端连接。 |
Socket | ConnectAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 对象,用于处理异步连接到服务器操作完成后的操作 | 客户端异步连接到服务器。 |
Socket | SendAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 对象,设置好发送数据的缓冲区等信息,用于处理异步发送消息操作完成后的操作 | 异步发送消息。 |
Socket | ReceiveAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 对象,设置好接收数据的缓冲区等信息,用于处理异步接受消息操作完成后的操作 | 异步接受消息。 |
9.UDP
实现UDP客户端通信收发字符串
//1.创建套接字 寻址类型还是Ipv4 Soket类型使用数据包 协议选择Udp
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//2.绑定本机地址 注意模拟测试时客户端本机和服务端远程机的端口号不能一样
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.Bind(ipPoint);//3.发送到指定目标 注意模拟测试时客户端本机和服务端远程机的端口号不能一样
IPEndPoint remoteIpPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
//指定要发送的字节数 和 远程计算机的 IP和端口
socket.SendTo(Encoding.UTF8.GetBytes("韬老狮来了"), remoteIpPoint);//4.接受消息
//装接收消息的字节数组
byte[] bytes = new byte[512];
//装接收消息的IP地址和端口的对象 在ReceiveFrom方法会使用ref赋值 主要是用来记录 谁发的信息给你 传入函数后 在内部 它会帮助我们进行赋值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
//接收消息 得到消息长度
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
print("IP:" + (remoteIpPoint2 as IPEndPoint).Address.ToString() +"port:" + (remoteIpPoint2 as IPEndPoint).Port +"发来了" +Encoding.UTF8.GetString(bytes, 0, length));//5.释放关闭
socket.Shutdown(SocketShutdown.Both);
socket.Close();
实现UDP服务端通信收发字符串
//1.创建套接字 寻址类型还是Ipv4 Soket类型使用数据包 协议选择Udp
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//2.绑定本机地址 注意模拟测试时客户端本机和服务端远程机的端口号不能一样
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
socket.Bind(ipPoint);
Console.WriteLine("服务器开启");//3.接受消息
//装接收消息的字节数组
byte[] bytes = new byte[512];
//装接收消息的IP地址和端口的对象 在ReceiveFrom方法会使用ref赋值 主要是用来记录 谁发的信息给你 传入函数后 在内部 它会帮助我们进行赋值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
Console.WriteLine("IP:" + (remoteIpPoint2 as IPEndPoint).Address.ToString() +"port:" + (remoteIpPoint2 as IPEndPoint).Port +"发来了" +Encoding.UTF8.GetString(bytes, 0, length));//4.发送到指定目标
//由于服务端先收了消息 所以服务端已经知道谁发了消息给服务端 是使用remoteIpPoint2记录的 服务端直接发给它就行了
socket.SendTo(Encoding.UTF8.GetBytes("欢迎发送消息给服务器"), remoteIpPoint2);//5.释放关闭
socket.Shutdown(SocketShutdown.Both);
socket.Close();
10.Socket类UDP异步常用方法
类名 | 方法 | 参数 | 详情 |
---|---|---|---|
Socket | BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state) | buffer :要发送的数据字节数组;offset :从字节数组的哪个位置开始发送;size :发送数据的长度;socketFlags :SocketFlags 标识;remoteEP :目标IP和端口号;callback :回调函数;state :回调函数的参数 | 开始向指定IP和端口异步发送数据,将结果异步传回回调函数进行处理 |
Socket | EndSendTo(IAsyncResult asyncResult) | asyncResult :BeginSendTo 方法返回的IAsyncResult 对象 | 结束异步发送操作 |
Socket | BeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state) | buffer :缓存区;offset :缓存区的起始位置;size :最大接收数据长度;socketFlags :SocketFlags 标识;remoteEP :接收数据的来源IPEndPoint ;callback :回调函数;state :回调函数的参数 | 开始异步从UDP客户端接收数据,将接收到的数据放入缓存区,接收数据的来源ip和端口号会被保存,有数据到达时调用回调函数处理 |
Socket | EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint remoteEP) | asyncResult :BeginReceiveFrom 方法返回的IAsyncResult 对象;remoteEP :接收数据的来源IPEndPoint | 结束异步接收操作,并返回接收到的字节数 |
Socket | SendToAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 对象,设置好要发送的数据、目标IP地址等信息 | 开始向指定IP和端口发送数据,发送完成后在回调函数处理 |
Socket | ReceiveFromAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 对象,设置好接收数据的缓冲区、接收来源的IPEndPoint 等信息 | 开始异步从UDP客户端接收数据,接收完成后在回调函数进行处理 |
11.FTP
类名 | 用途/功能 | 重要方法 | 重要成员 |
---|---|---|---|
NetworkCredential (System.Net ) | 在网络身份验证中存储用户名和密码信息,用于 FTP 文件传输场景下提供访问 FTP 服务器所需的认证凭据 | 无 | 无 |
FtpWebRequest (System.Net ) | 用于执行上传、下载、删除 FTP 服务器上的文件等操作 | 1. Create :创建新 Ftp 请求。2. Abort :终止 Ftp 传输。3. GetRequestStream :获取用于上传的流。4. GetResponse :返回 FTP 服务器响应。 | 1. Credentials :设置通信凭证为NetworkCredential 对象。2. KeepAlive :设置完成请求时是否关闭到 FTP 服务器的控制连接。3. Method :设置 FTP 请求操作命令,如删除、下载、列目录等。4. UseBinary :指定是否采用二进制模式传输数据。5. RenameTo :重命名文件。 |
FtpWebResponse (System.Net ) | 封装 FTP 服务器对请求的响应,提供操作状态及从服务器下载的数据,用于表示响应信息,含响应代码、消息等 | 1. Close :释放所有资源。2. GetResponseStream :返回从 FTP 服务器下载数据的流。 | 1. ContentLength :接收到数据的长度。2. ContentType :接收数据的类型。3. StatusCode :FTP 服务器下发的最新状态码。4. StatusDescription :状态代码的文本描述。5. BannerMessage :登录前建立连接时服务器发送的消息。6. ExitMessage :获取 FTP 会话结束时服务器发送的消息。7. LastModified :获取 FTP 服务器上文件的最后修改日期和时间。 |
11.1.FTP上传文件
try
{// 创建一个Ftp连接,pic.png代表想上传名叫pic的png图片。// 这里的ftp://127.0.0.1是使用本机开启服务器进行测试,实际使用时应该是远端服务器IP。FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;// 设置通信凭证(如果不支持匿名,就必须设置这一步)。// 将代理相关信息置空,避免服务器同时有http相关服务造成冲突。ftpWebRequest.Proxy = null;// 创建并设置通信凭证。NetworkCredential networkCredential = new NetworkCredential("MrTao", "MrTao");ftpWebRequest.Credentials = networkCredential;// 请求完毕后是否关闭控制连接,如果想要关闭,可以设置为false。ftpWebRequest.KeepAlive = false;// 设置操作命令。ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile;//设置命令操作为上传文件。// 指定传输类型,使用二进制。ftpWebRequest.UseBinary = true;// 得到用于上传的流对象。Stream uploadRequestStream = ftpWebRequest.GetRequestStream();// 开始上传,使用流读取StreamingAssets文件夹下的名叫test的图片。using (FileStream fileStream = File.OpenRead(Application.streamingAssetsPath + "/test.png")){// 我们可以一点一点的把这个文件中的字节数组读取出来,然后存入到上传流中。byte[] bytes = new byte[1024];// 返回值是真正从文件中读了多少个字节。int contentLength = fileStream.Read(bytes, 0, bytes.Length);// 不停的去读取文件中的字节,除非读取完毕了,不然一直读,并且写入到上传流中。while (contentLength != 0){// 写入上传流中。uploadRequestStream.Write(bytes, 0, contentLength);// 写完了继续读。contentLength = fileStream.Read(bytes, 0, bytes.Length);}// 出了循环就证明写完了。fileStream.Close();uploadRequestStream.Close();// 上传完毕。print("上传结束");}
}
catch (Exception e)
{print("上传出错,失败" + e.Message);
}
11.2.FTP下载文件
try
{// 创建一个Ftp连接。// 这里和上传不同,上传的文件名是自己定义的,下载的文件名一定是资源服务器上有的,比如一张叫pic的图片。FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;// 设置通信凭证(如果不支持匿名,就必须设置这一步)。ftpWebRequest.Credentials = new NetworkCredential("MrTao", "MrTao");// 请求完毕后是否关闭控制连接,如果要进行多次操作,可以设置为false。ftpWebRequest.KeepAlive = false;// 设置操作命令。ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;// 指定传输类型。ftpWebRequest.UseBinary = true;// 代理设置为空。ftpWebRequest.Proxy = null;// 得到用于下载的流对象。// 相当于把请求发送给FTP服务器,返回值就会携带我们想要的信息。FtpWebResponse ftpWebResponse = ftpWebRequest.GetResponse() as FtpWebResponse;// 这就是下载的流。Stream downloadResponseStream = ftpWebResponse.GetResponseStream();// 开始下载。print(Application.persistentDataPath);using (FileStream fileStream = File.Create(Application.persistentDataPath + "/pic2.png")){// 读取流的字节数组。byte[] bytes = new byte[1024];// 读取下载下来的流数据。int contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);// 一点一点的下载到本地流中。while (contentLength != 0){// 把读取出来的字节数组写入到本地文件流中。fileStream.Write(bytes, 0, contentLength);// 继续读。contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);}// 下载结束,关闭流。downloadResponseStream.Close();fileStream.Close();}print("下载结束");
}
catch (Exception e)
{print("下载出错" + e.Message);
}
12.HTTP
类名(命名空间) | 用途 | 重要方法 | 重要成员 |
---|---|---|---|
HttpWebRequest(System.Net) | 用于向服务器发送HTTP客户端请求,进行消息通信、上传、下载等操作 | 1. Create:创建新的WebRequest,用于HTTP相关操作 2. Abort:终止文件传输 3. GetRequestStream:获取用于上传的流 4. GetResponse:返回HTTP服务器响应 5. Begin/EndGetRequestStream:异步获取用于上传的流 6. Begin/EndGetResponse:异步获取返回的HTTP服务器响应 | 1. Credentials:通信凭证,设置为NetworkCredential对象 2. PreAuthenticate:是否随请求发送一个身份验证标头,一般需身份验证时设为true 3. Headers:构成标头的名称/值对的集合 4. ContentLength:发送信息的字节数,上传信息时需先设置 5. ContentType:POST请求时,需对发送内容进行内容类型设置 6. Method:操作命令设置,如Get、Post、Head等 |
HttpWebResponse(System.Net) | 用于获取服务器反馈信息,通过HttpWebRequest对象的GetResponse()方法获取,使用完毕用Close释放 | 1. Close:释放所有资源 2. GetResponseStream:返回从FTP服务器下载数据的流 | 1. ContentLength:接受到数据的长度 2. ContentType:接受数据的类型 3. StatusCode:HTTP服务器下发的最新状态码 4. StatusDescription:HTTP服务器下发的状态代码的文本 5. BannerMessage:登录前建立连接时HTTP服务器发送的消息 6. ExitMessage:HTTP会话结束时服务器发送的消息 7. LastModified:HTTP服务器上的文件的上次修改日期和时间 |
NetworkCredential、Uri、Stream、FileStream(相关命名空间) | 在HTTP通讯时使用方式与FTP类似 | - | - |
13.WWW
类名(命名空间) | 作用 | 常用方法 | 常用变量 |
---|---|---|---|
WWW(UnityEngine) | 用于简单访问网页,可进行数据的下载和上传。使用 HTTP 协议时默认请求类型是 Get,进行 Post 上传需配合 WWWForm 类 | 1. WWW:构造函数,用于创建一个 WWW 请求 2. GetAudioClip:从下载数据返回一个音效切片 AudioClip 对象 3. LoadImageIntoTexture:用下载数据中的图像来替换现有的一个 Texture2D 对象 4. LoadFromCacheOrDownload:从缓存加载 AB 包对象,如果该包不在缓存则自动下载存储到缓存中,以便以后直接从本地缓存中加载 | 1. assetBundle:如果加载的数据是 AssetBundle,则可直接获取加载结果 2. audioClip:如果加载的数据是音效切片文件,可直接获取加载结果(新版本用 GetAudioClip 方法) 3. bytes:以字节数组形式获取加载内容 4. bytesDownloaded:已下载的字节数 5. error:下载出错时返回错误消息 6. isDone:判断下载是否完成 7. movie:下载视频时可获取 MovieTexture 类型结果(新版本用 GetMovieTexture 方法) 8. progress:下载进度 9. text:下载数据为字符串时,以字符串形式返回内容 10. texture:下载数据为图片时,以 Texture2D 形式返回加载结果 |
14.WWWForm
类名 | 作用 | 注意事项 | 常用方法 |
---|---|---|---|
WWWForm | 在使用 WWW 类下载数据的基础上,若需上传数据,可结合该类使用。主要用于集成数据,可设置上传的参数或者二进制数据,结合使用时主要用 Post 请求类型,通过 Http 协议上传处理 | 使用 WWW 结合 WWWForm 上传数据通常需要与后端程序制定上传规则 | 1. WWWForm:构造函数 2. AddBinaryData:添加二进制数据 3. AddField:添加字段 |
15.UnityWebRequest
15.1.常用上传和获取数据
获取
类名 | 方法 | 方法解析 | 参数 |
---|---|---|---|
UnityWebRequest | Get | 创建一个用于获取文本或二进制数据的 UnityWebRequest 对象。 | uri :请求资源的 URI 地址,如 "http://192.168.1.101:8000/HTTPRoot/test.txt" |
UnityWebRequestTexture | GetTexture | 创建一个用于获取纹理数据的 UnityWebRequest 对象。 | uri :请求纹理资源的 URI 地址,可以是 http 、ftp 或 file 协议的地址,如 "http://192.168.1.101:8000/HTTPRoot/pic.png" 、"ftp://127.0.0.1/pic.png" 或 "file://" + Application.streamingAssetsPath + "/test.png" |
UnityWebRequestAssetBundle | GetAssetBundle | 创建一个用于获取 AB 包数据的 UnityWebRequest 对象。 | uri :请求 AB 包资源的 URI 地址,如 "http://192.168.1.101:8000/HTTPRoot/model" |
UnityWebRequest | SendWebRequest | 发送 Web 请求并等待服务器响应。 | 无 |
DownloadHandlerTexture | GetContent | 从 UnityWebRequest 对象中获取下载的纹理数据。 | request :UnityWebRequest 对象,用于获取其中的纹理数据 |
DownloadHandlerAssetBundle | GetContent | 从 UnityWebRequest 对象中获取下载的 AB 包数据。 | request :UnityWebRequest 对象,用于获取其中的 AB 包数据 |
上传
类名/接口 | 方法/描述 | 参数 |
---|---|---|
IMultipartFormSection | 数据相关类继承的父接口,可使用父类类型的集合来存储子类对象 | 无 |
MultipartFormDataSection | 用于传递键值对数据的类 | 1. 构造函数(二进制字节数组): |
MultipartFormFileSection | 用于传递文件数据的类 | 1. 构造函数(字节数组): |
UnityWebRequest | Post 方法:创建一个用于使用 POST 请求上传数据的UnityWebRequest 对象Put 方法:创建一个用于使用 PUT 请求上传数据的UnityWebRequest 对象SendWebRequest 方法:发送 Web 请求并等待服务器响应 | 1. |
NetWWWManager | UploadFile 方法:启动异步上传文件的操作UploadFileAsync 方法:异步上传文件的具体实现 | 1. |
15.2.高级上传和获取数据
获取
类名 | 方法/属性 | 方法的参数 |
---|---|---|
UnityWebRequest | 构造函数 | 无 |
UnityWebRequest | url | 字符串类型的服务器地址 |
UnityWebRequest | method | 如UnityWebRequest.kHttpVerbPOST等请求类型常量 |
UnityWebRequest | timeout | 整数类型的超时时间(单位:毫秒) |
UnityWebRequest | redirectLimit | 整数类型,0表示不进行重定向 |
UnityWebRequest | responseCode | 无 |
UnityWebRequest | result | 无 |
UnityWebRequest | error | 无 |
UnityWebRequest | downloadHandler | 实现了DownloadHandler的对象,如DownloadHandlerBuffer等 |
UnityWebRequest | uploadHandler | 实现了UploadHandler的对象 |
UnityWebRequest | Get | 字符串类型的请求URL |
UnityWebRequest | GetTexture | 字符串类型的纹理URL |
UnityWebRequestAssetBundle | GetAssetBundle | 字符串类型的AssetBundle URL |
UnityWebRequest | Put | 字符串类型的请求URL和字节数组形式的上传内容 |
UnityWebRequest | Post | 字符串类型的请求URL和List类型的上传数据列表 |
UnityWebRequest | isDone | 无 |
UnityWebRequest | downloadProgress | 无 |
UnityWebRequest | downloadedBytes | 无 |
UnityWebRequest | uploadProgress | 无 |
UnityWebRequest | uploadedBytes | 无 |
UnityWebRequest | SendWebRequest | 无 |
DownloadHandlerBuffer | 构造函数 | 无 |
DownloadHandlerFile | 构造函数 | 字符串类型的本地文件保存路径 |
DownloadHandlerTexture | 构造函数 | 无 |
DownloadHandlerAssetBundle | 构造函数 | 字符串类型的请求URL和校验码(ulong类型,不知道则传入0) |
UnityWebRequestMultimedia | GetAudioClip | 字符串类型的音频文件URL和AudioType类型的音频类型 |
DownloadHandlerAudioClip | GetContent | UnityWebRequest对象 |
CustomDownLoadFileHandler | 构造函数(无参) | 无 |
CustomDownLoadFileHandler | 构造函数(字节数组) | 字节数组类型的参数 |
CustomDownLoadFileHandler | 构造函数(路径) | 字符串类型的本地存储路径 |
CustomDownLoadFileHandler | GetData | 无 |
CustomDownLoadFileHandler | ReceiveData | byte[]类型的数据和int类型的数据长度 |
CustomDownLoadFileHandler | ReceiveContentLengthHeader | ulong类型的内容长度 |
CustomDownLoadFileHandler | CompleteContent | 无 |
上传
类名 | 方法/属性 | 方法的参数 | 描述 |
---|---|---|---|
UnityWebRequest | 构造函数 | string uri , string method :请求的 URL 和 HTTP 请求方法(如 UnityWebRequest.kHttpVerbPOST ) | 创建一个 UnityWebRequest 对象,用于网络请求 |
UnityWebRequest | uploadHandler | 实现了 UploadHandler 的对象,如 UploadHandlerRaw 、UploadHandlerFile 等 | 设置上传处理对象,用于处理上传的数据 |
UnityWebRequest | SendWebRequest | 无 | 发送网络请求,并等待请求返回 |
UnityWebRequest | result | 无 | 获取网络请求的结果(成功或失败) |
UploadHandlerRaw | 构造函数 | byte[] data :要上传的字节数组 | 创建一个用于上传字节数组的 UploadHandlerRaw 对象 |
UploadHandlerRaw | contentType | string 类型的内容类型,如 "类型/细分类型" | 设置上传数据的内容类型,若不设置,默认是 application/octet - stream |
UploadHandlerFile | 构造函数 | string path :要上传的文件路径 | 创建一个用于上传文件的 UploadHandlerFile 对象 |
16.Protobuf
16.1.Protobuf配置规则
(1)配置文件后缀:统一使用.proto
,可通过多个该后缀文件配置。
(2)版本号:第一行需指定,如syntax = "proto3";
,不写默认用proto2。
(3)注释方式:支持//
单行注释和/* */
多行注释。
(4)命名空间:用package
指定,如package 命名空间名;
。
(5)消息类:使用message
定义,格式为message 类名 { // 字段声明 }
。
(6)成员类型和编号:成员类型包含浮点数(float
、double
)、整数(int32
、int64
等)、其他(bool
、string
、bytes
),每个成员需指定从1开始的唯一编号。
(7)特殊标识:optional
表示可选赋值字段;repeated
表示数组(required
在proto3中已移除 )。
(8)枚举:用enum
定义,首个常量必须为0,如enum 枚举名 { 常量1 = 0; 常量2 = 1; }
。
(9)默认值:string
为空字符串、bytes
为空字节、bool
为false
、数值为0、枚举为0、message
在C#中为空。
(10)嵌套:支持消息类和枚举的嵌套定义。
(11)保留字段:使用reserved
关键字保留字段,防止已删编号被重新使用。
(12)导入定义:若使用其他配置中的类型,用import "配置文件路径";
导入。
syntax = "proto3"; // 决定了 proto 文档的版本号// 规则1:版本号// 规则2:注释方式
// 注释方式一
/* 注释方式二 */// 规则11:导入定义
// 两个配置在同一路径可以这样写,在不同路径要包含文件夹路径
import "test2.proto";// 规则3:命名空间
package GamePlayerTest; // 这决定了命名空间// 规则4:消息类
message TestMsg {// 规则5:成员类型 和 唯一编号// 浮点数// = 1 不代表默认值,而是代表唯一编号,方便我们进行序列化和反序列化的处理// 规则6:特殊标识// required: 必须赋值的字段required float testF = 1; // C# - float// optional: 可以不赋值的字段optional double testD = 2; // C# - double// 变长编码// 所谓变长,就是会根据数字的大小来使用对应的字节数来存储,1 2 4// Protobuf 帮助我们优化的部分,可以尽量少的使用字节数来存储内容int32 testInt32 = 3; // C# - int 它不太适用于来表示负数,请使用 sint32// 1 2 4 8int64 testInt64 = 4; // C# - long 它不太适用于来表示负数,请使用 sint64// 更实用与表示负数类型的整数sint32 testSInt32 = 5; // C# - int 适用于来表示负数的整数sint64 testSInt64 = 6; // C# - long 适用于来表示负数的整数// 无符号 变长编码// 1 2 4uint32 testUInt = 7; // C# - uint 变长的编码uint64 testULong = 8; // C# - ulong 变长的编码// 固定字节数的类型fixed32 testFixed32 = 9; // C# - uint 它通常用来表示大于2的28次方的数,比 uint32 更有效,始终是4个字节fixed64 testFixed64 = 10; // C# - ulong 它通常用来表示大于2的56次方的数,比 uint64 更有效,始终是8个字节sfixed32 testSFixed32 = 11; // C# - int 始终4个字节sfixed64 testSFixed64 = 12; // C# - long 始终8个字节// 其它类型bool testBool = 13; // C# - boolstring testStr = 14; // C# - stringbytes testBytes = 15; // C# - BytesString 字节字符串// 数组 Listrepeated int32 listInt = 16; // C# - 类似 List<int> 的使用// 字典 Dictionarymap<int32, string> testMap = 17; // C# - 类似 Dictionary<int, string> 的使用// 规则7:枚举// 枚举成员变量的声明需要唯一编码TestEnum testEnum = 18;// 规则8:默认值// 声明自定义类对象 需要唯一编码// 默认值是 nullTestMsg2 testMsg2 = 19;// 规则9:允许嵌套// 嵌套一个类在另一个类当中,相当于是内部类message TestMsg3 {int32 testInt32 = 1;}TestMsg3 testMsg3 = 20;// 规则9:允许嵌套enum TestEnum2 {NORMAL = 0; // 第一个常量必须映射到0BOSS = 1;}TestEnum2 testEnum2 = 21;// 规则10:保留字段// int32 testInt3233333 = 22;bool testBool2123123 = 23;// 告诉编译器 22 被占用,不准用户使用// 之所以有这个功能,是为了在版本不匹配时,反序列化时,不会出现结构不统一,解析错误的问题reserved 22;reserved testInt3233333;// 规则11:导入定义GameSystemTest.HeartMsg testHeart = 24;
}// 规则7:枚举的声明
enum TestEnum {NORMAL = 0; // 第一个常量必须映射到0BOSS = 5;
}// 规则8:默认值
message TestMsg2 {int32 testInt32 = 1;
}syntax = "proto3"; // 决定了 proto 文档的版本号
package GameSystemTest; // 这决定了命名空间message HeartMsg {int64 time = 1;
}
16.2.Protobuf协议使用
序列化存储为本地文件
- 主要使用方法:生成的类中的
WriteTo
方法和文件流FileStream
对象。 - 示例代码:
TestMsg testMsg1 = new TestMsg();
testMsg1.ListInt.Add(1);
testMsg1.TestBool = false;
testMsg1.TestD = 5.5;
testMsg1.TestInt32 = 99;
testMsg1.TestMap.Add(1, "杨");
testMsg1.TestMsg2 = new TestMsg2();
testMsg1.TestMsg2.TestInt32 = 88;
testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
testMsg1.TestMsg3.TestInt32 = 66;testMsg1.TestHeart = new GameSystemTest.HeartMsg();
testMsg1.TestHeart.Time = 7777;print(Application.persistentDataPath);
using (FileStream fileStream = File.Create(Application.persistentDataPath + "/TestMsg.tao"))
{testMsg1.WriteTo(fileStream);
}
- 代码解释:先创建
TestMsg
实例并设置其属性值,然后使用File.Create
方法创建一个文件流,将TestMsg
实例序列化后写入该文件流,实现数据的本地存储。
反序列化本地文件
- 主要使用方法:生成的类中的
Parser.ParseFrom
方法和文件流FileStream
对象。 - 示例代码:
using (FileStream fileStream = File.OpenRead(Application.persistentDataPath + "/TestMsg.tao"))
{TestMsg testMsg2 = null;testMsg2 = TestMsg.Parser.ParseFrom(fileStream);print(testMsg2.TestMap[1]);print(testMsg2.ListInt[0]);print(testMsg2.TestD);print(testMsg2.TestMsg2.TestInt32);print(testMsg2.TestMsg3.TestInt32);print(testMsg2.TestHeart.Time);
}
- 代码解释:使用
File.OpenRead
方法打开之前存储的文件流,然后调用Parser.ParseFrom
方法从文件流中反序列化出TestMsg
实例,最后打印该实例的属性值。
得到序列化后的字节数组
- 主要使用方法:生成的类中的
WriteTo
方法和内存流MemoryStream
对象。 - 示例代码:
byte[] bytes = null;
using (MemoryStream memoryStream = new MemoryStream())
{testMsg1.WriteTo(memoryStream);bytes = memoryStream.ToArray();print("字节数组长度" + bytes.Length);
}
- 代码解释:创建一个内存流,将
TestMsg
实例序列化后写入该内存流,再通过ToArray
方法将内存流中的数据转换为字节数组,方便进行网络传输。
从字节数组反序列化
- 主要使用方法:生成的类中的
Parser.ParseFrom
方法和内存流MemoryStream
对象。 - 示例代码:
using (MemoryStream memoryStream = new MemoryStream(bytes))
{print("内存流当中反序列化的内容");TestMsg testMsg2 = TestMsg.Parser.ParseFrom(memoryStream);print(testMsg2.TestMap[1]);print(testMsg2.ListInt[0]);print(testMsg2.TestD);print(testMsg2.TestMsg2.TestInt32);print(testMsg2.TestMsg3.TestInt32);print(testMsg2.TestHeart.Time);
}
- 代码解释:使用包含字节数组的内存流,调用
Parser.ParseFrom
方法从内存流中反序列化出TestMsg
实例,最后打印该实例的属性值。
17.大小端模式
(1)大小端模式的基本概念
- 大端模式:数据的高字节保存在内存的低地址中,低字节保存在内存的高地址中。地址由小向大增加时,数据从高位往低位放置,类似字符串顺序处理,符合人类阅读习惯。
- 小端模式:数据的高字节保存在内存的高地址中,低字节保存在内存的低地址中。例如十六进制数据 0x11223344,大端模式存储为 11 22 33 44(地址 0 1 2 3),小端模式存储为 44 33 22 11(地址 0 1 2 3)。
- 存在原因:是计算机硬件的两种存储数据方式(字节序)。计算机内部处理按顺序读取字节,因处理低位字节效率高而采用小端模式,人类读写习惯是大端字节序,网络传输和文件存储等场合一般用大端模式。
(2)大小端模式的影响及转换
- 影响:只有读取数据时需区分大小端字节序。网络传输中,前后端语言和设备差异可能致大小端不统一,如 C# 和 Java/Erlang/AS3 通讯需转换,C# 与 C++ 通信通常无需特殊处理。
- 转换方法
- 判断模式:用
BitConverter.IsLittleEndian
判断,如print("是否是小端模式:" + BitConverter.IsLittleEndian);
。 - 转为大端模式:通过
IPAddress.HostToNetworkOrder
将本机字节序转网络字节序(即大端模式),如int i = 99; byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(i));
。 - 转为小端模式:用
IPAddress.NetworkToHostOrder
将网络字节序转本机字节序(即小端模式),如int receI = BitConverter.ToInt32(bytes, 0); receI = IPAddress.NetworkToHostOrder(receI);
。 - 倒序数组转换:使用
Array.Reverse
倒序数组,若后端需大端模式且当前是小端模式则转换,如if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
。
- 判断模式:用
相关文章:
Unity网络开发快速回顾
知识点来源:总结人间自有韬哥在, 唐老狮,豆包 目录 1.网络通信-通信必备知识-IP地址和端口类2.网络通信中序列化和反序列化2进制数据3.Socket类4.TCP同步服务端和客户端基础实现4.1.服务端基本实现4.2.客户端实现: 5.区分消息类型…...
鸿蒙学习笔记(1)-文件解读、编写程序、生命周期
一、文件解读 .hvigor:装有一些编译过程中的依赖缓存。 .idea:工具自动生成的,标记我们的工具是基于idea。 AppScope:代表着整个APP的配置,最后打包使用。之中的resources目录下是应用的名称和图片存放路径,其中app.json5: bund…...
多维动态规划 力扣hot100热门面试算法题 面试基础 核心思路 背题
多维动态规划 不同路径 https://leetcode.cn/problems/unique-paths/ 核心思路 比较简单 f[i][j] f[i - 1][j] f[i][j - 1] ; 示例代码 class Solution {public int uniquePaths(int n, int m) {int[][] f new int[n][m];for (int i 0; i < n; i)f[i][0] 1;for…...
C++ 多线程简要讲解
std::thread是 C11 标准库中用于多线程编程的核心类,提供线程的创建、管理和同步功能。下面我们一一讲解。 一.构造函数 官网的构造函数如下: 1.默认构造函数和线程创建 thread() noexcept; 作用:创建一个 std::thread 对象,但…...
乐仓VUE常用点
页面跳转 发送 router.push({ name: config.editRouteName, query: { type: create, from: route.name as string, }, }); router.push({ name: MbdCalScheme }); 接收 const route useRoute(); const type compu…...
单一主数据系统 vs. 统一主数据中心,哪种更优?
在企业中,主数据管理(MDM)已成为确保数据一致性和高效运营的关键。企业通常使用多个系统来处理业务数据,如ERP、CRM、SRM等。在这种多系统环境下,如何统一管理企业数据、避免数据孤岛,成为了一项重大挑战。…...
数据结构—树(java实现)
目录 一、树的基本概念1.树的术语2.常见的树结构 二、节点的定义三、有关树结构的操作1.按照数组构造平衡 二叉搜索树2.层序遍历树3.前、中、后序遍历树(1).前序遍历树(2).中序遍历树(3).后序遍历树(4).各种遍历的情况的效果对比 4.元素添加5.元素删除1.删除叶子节点2.删除单一…...
Modbus RTU ---> Modbus TCP透传技术实现(Modbus透传、RS485透传、RTU透传)分站代码实现、协议转换器
文章目录 Modbus RTU到Modbus TCP透传技术实现1. 透传技术概述1.1 透传基本原理- 协议帧格式转换- 地址映射与管理- 通信时序适配- 错误检测与处理 2. 透传网关硬件架构2.1 典型硬件结构- 微控制器/处理器(ARM、STM32等)- RS-485/RS-232收发器- 以太网控制器(如W5500)- 电源管理…...
Flask(三)路由与视图函数
在 Flask 中,路由 (Route) 是将 URL 地址映射到特定的视图函数 (View Function) 的机制。视图函数处理用户请求,并返回 HTTP 响应。理解路由和视图函数是构建 Flask 应用的基础。 3.1 路由的基本概念 Flask 使用 app.route() 装饰器来定义路由。以下是…...
mysql 磐维(opengauss)tidb误删数据之高级恢复
Mysql参考: Mysql 8.0 XtraBackupMysqlbinlog 完全恢复 - 墨天轮 Mysql 8.0 XtraBackupMysqlbinlog 完全恢复[TOC]# 一、安装mysql 8.0.19## 1.1https://www.modb.pro/db/509223MySQL 的全量备份、增量备份与 Binlog 时间点恢复_mysqlbinlog自动备份吗-CSDN博客文章…...
常见框架漏洞(一)----Thinkphp(TP)
Thinkphp框架介绍: ThinkPHP是为了简化企业级应⽤开发和敏捷WEB应⽤开发⽽诞⽣的,是⼀个快速、兼容⽽ 且简单的轻量级国产PHP开发框架,诞⽣于2006年初,原名FCS,2007年元旦正式更名为 ThinkPHP,遵循Apache…...
Git 使用指南:从设置用户信息到项目提交的全流程教程(持续更新)
一、设置 Git 用户信息 1.全局设置(适用于所有 Git 仓库): git config --global user.name "Your Name" git config --global user.email youexample.com 2.仅针对当前仓库设置(如果您只想为当前项目设置࿰…...
Json在扩展属性xdata中的应用实例——cad 二次开发c#
以下是一个使用AutoCAD C#.NET API实现你需求的示例代码,代码实现了提示用户选择一个实体,将一些字符串变量及其对应的值组成JSON格式数据存储到实体的扩展数据(XData)中,并在弹出窗口中显示该实体的所有扩展数据信息。…...
Unity Animation的其中一种运用方式
Animation是Unity的旧的动画系统,先说目的,其使用是为了在UI中播放动效,并且在动效播放结束后接自定义事件而设计的 设计的关键点在于,这个脚本不是通过Animation直接播放动画片段,而是通过修改AnimationState的nor…...
C++ 的基本内置类型(十二)
C 作为一门强大的编程语言,其内置类型是开发者必须掌握的基础。本文将详细介绍 C 的基本内置类型,包括算术类型和空类型,探讨它们的特性、尺寸以及在机器上的实现方式。 一、 基本内置类型概述 C 定义了一套基本数据类型,主要包…...
Android设计模式之单例模式
一、定义:确保一个类只有一个实例,并且自动实例化,并向整个系统提供这个实例。 二、使用场景:避免重复创建对象,过多消耗系统资源。 三、使用方式 3.1饿汉式:类加载时立即初始化,线程安全&…...
随笔(1)
1、解除切屏限制命令 document.body.onblur null2、vue3 getCurrentInstance import { getCurrentInstance } from vue const {proxy} getCurrentInstance() proxy.mittBus.emit(message,xxxx)3、 自定义双向绑定,modelValue 就是对应输入框的值 emit(update:mo…...
系统与网络安全------网络应用基础(3)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 路由器 认识路由器 负责在不同网络之间转发数据的设备 路由器决定到达目标的路径 路由器也为直连网络的主机充当”网关“角色 路由器是实现网络互连的最核心设备 工作原理 每台路由器维护一份路由表&…...
Java 基于微信小程序的开放实验室预约管理系统
博主介绍:✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不…...
Linux内核NIC网卡驱动实战案例分析
以下Linux 内核模块实现了一个虚拟网络设备驱动程序,其作用和意义如下: 1. 作用 (1)创建虚拟网络设备对 驱动程序动态创建了两个虚拟网络设备(nic_dev[0]和nic_dev[1]),模拟物理网卡的功能。这两…...
R 基础语法
R 基础语法 引言 R 是一种针对统计计算和图形表示而设计的编程语言和环境。它广泛应用于统计学、生物信息学、数据挖掘等领域。本文将为您介绍 R 语言的基础语法,帮助您快速上手。 R 的基本结构 R 语言的基本结构包括:变量、数据类型、运算符、控制结构、函数等。 变量 …...
JDK 24 发布,新特性解读!
一、版本演进与技术格局新动向 北京时间3月20日,Oracle正式发布Java SE 24。作为继Java 21之后的第三个非LTS版本,其技术革新力度远超预期——共集成24项JEP提案,相当于Java 22(12项)与Java 23(12项&#…...
在 Qt 中,不带参数或整形的参选的信号能够从 std::thread 发送成功,而带枚举离线的信号却发送失败
在 Qt 中,不带参数或整形的参选的信号能够从 std::thread 发送成功,而带枚举离线的信号却发送失败 当信号和槽在不同线程时,默认使用 队列连接(Qt::QueuedConnection),信号会被放入接收线程的事件队列&…...
慧通测控汽车智能座舱测试技术
一、引言 随着科技的飞速发展,汽车正从单纯的交通工具向智能化移动空间转变。智能座舱作为这一转变的核心体现,融合了多种先进技术,为用户带来前所未有的驾驶体验。从简单的信息娱乐系统到高度集成的人机交互、智能驾驶辅助以及车辆状态监测…...
【CSS】CSS 使用全教程
CSS 使用全教程 介绍 CSS(层叠样式表,Cascading Style Sheets)是一种样式表语言,用于描述 HTML 或 XML 文档的布局和外观,它允许开发者将文档的内容结构与样式表现分离,通过定义一系列的样式规则来控制网页…...
【Linux知识】RPM软件包安装命令行详细说明
文章目录 概述安装软件包升级软件包卸载软件包查询软件包信息验证软件包从软件包中提取文件注意事项 概述 rpm(Red Hat Package Manager)是红帽系 Linux 发行版(如 Red Hat、CentOS、Fedora 等)用于管理软件包的工具,…...
SpringBoot3.0不建议使用spring.factories,使用AutoConfiguration.imports新的自动配置方案
文章目录 一、写在前面二、使用imports文件1、使用2、示例比对3、完整示例 参考资料 一、写在前面 spring.factories是一个位于META-INF/目录下的配置文件,它基于Java的SPI(Service Provider Interface)机制的变种实现。 这个文件的主要功能是允许开发者声明接口的…...
c++项目-KV存储-模仿redis实现kv键值对存储的基本功能。
KV存储引擎的技术解析:数组、哈希与红黑树实现及其在网络I/O中的应用。 内容概要:本文档深入介绍了基于数组、哈希表和红黑树的键值存储引擎的设计与实现。文档首先阐述了系统的总体架构与类图关系,之后分别对底层存储结构进行了详细解释&am…...
docker ssh远程连接
目录 操作命令: 确保 SSH 配置允许 root 登录: docker提交: 操作命令: # 进入容器 docker exec -ti lbg04 /bin/bash# 更新包管理并安装 SSH 服务(Ubuntu/Debian 示例) apt-get update apt-get install…...
边缘计算场景下的分布式推理系统架构设计
一、边缘节点推理优化 1.1 模型轻量化技术矩阵 1.2 TensorRT加速配置示例 # 使用TensorRT优化YOLOv8builder trt.Builder(TRT_LOGGER)network builder.create_network()parser trt.OnnxParser(network, TRT_LOGGER)with open("yolov8s.onnx", "rb") a…...
css基础-浮动
一、浮动是什么? 比喻:就像泳池里的救生圈 原始用途:让文字环绕图片(像杂志排版)意外发展:被用来做页面布局(像用救生圈搭浮桥) 二、浮动怎么产生的? 场景还原&#…...
Linux TTY设备汇总
目录 1. tty(终端设备统称) 2. ptm(伪终端主设备)与pts(伪终端从设备) 3. ttys(串行端口终端) 4. ttyACM(USB CDC ACM设备) 5. ttyGS(USB Gadget Serial设备) 主要联系 典型应用场景 TTY_CORE: drivers/tty/tty_io.c:tty_register_driver…...
Android studio组合教程--做出一个类似于QQ的登录页面
之前我们学过了html与Android的开发,以及各种组件的学习,这次我们做一个完整向的登录页面,作为一次大作业。 注意 里面的一图片可以自由发挥,但要注意文件路径保持准确,这里给出参考路径: 背景路径&…...
iPhone 16 Plus :凉凉了
大屏就是生产力,这句话就像思想钢印一样,深入人心。 但苹果用户是个例外,根据内行人的爆料,iPhone 16 Plus 彻底凉凉了,难怪它会是最后一代Plus。 根据知名博主数码闲聊站透露,截止3 月 9 号,i…...
【MySQL报错】:Column count doesn’t match value count at row 1
MySQL报错:Column count doesn’t match value count at row 1 意思是存储的数据与数据库表的字段类型定义不相匹配. 由于类似 insert 语句中,前后列数不等造成的 主要有3个易错点: 要传入表中的字段数和values后面的值的个数不相等。 由于类…...
2025 polarctf春季个人挑战赛web方向wp
来个弹窗 先用最基础的xss弹窗试一下 <script>alert("xss")</script>没有内容,猜测过滤了script,双写绕过一下 <scrscriptipt>alert("xss")</scscriptript>background 查看网页源代码 查看一下js文件 类…...
Midscene.js自然语言驱动的网页自动化全指南
一、概述 网页自动化在数据抓取、UI 测试和业务流程优化中发挥着重要作用。然而,传统工具如 Selenium 和 Puppeteer 要求用户具备编程技能,编写复杂的选择器和脚本维护成本高昂。Midscene.js 通过自然语言接口革新了这一领域,用户只需描述任…...
PDF与Markdown的量子纠缠:一场由VLM导演的文档界奇幻秀
缘起:当格式界的"泰坦尼克号"撞上"黑客帝国" 某个月黑风高的夜晚,在"二进制酒吧"的霓虹灯下: PDF(西装革履地晃着威士忌): “我的每一页都像瑞士手表般精密,连华尔街的秃鹫都为我倾倒!” Markdown(穿着带洞的拖鞋): “得了吧老古董!…...
Spring Boot JSON序列化深度管控:忽略指定字段+Jackson扩展策略破解双向实体循环引用问题
一、JsonIgnore的核心原理与工作机制 1. 注解作用原理 JsonIgnore是Jackson库的核心注解之一,其工作原理基于 Jackson的AnnotationIntrospector机制。在序列化/反序列化过程中,Jackson会扫描Java对象的所有字段和方法上的注解。当检测到JsonIgnore时&a…...
msvcp140.dll是什么文件?修复丢失msvcp140.dll的方法指南
当计算机显示"msvcp140.dll未找到"的报错信息时,这实际反映了Windows系统运行机制中的一个关键环节出现断链。作为Microsoft Visual C可再发行组件包的核心动态链接库,msvcp140.dll承担着程序与系统资源之间的桥梁作用,特别是在处理…...
ES集群的部署
实验步骤 实验目的: 验证ES集群的容错性、扩展性数据分布与查询性能优化。 环境准备 1、准备两台服务器 服务器 1、10.1.1.20 cpu 2核 内存:4G 硬盘100G 2、10.1.1.21 cpu 2核 内存:4G 硬盘100G 2、修改两台静态ip 3、关闭防…...
resetForm() 方法用于重置表单
resetForm() 方法是 Vue.js 中用于重置表单的一个常见操作。下面是对这段代码的详细解析: 1. 代码作用 resetForm() 方法的作用是重置表单,将表单中的所有输入字段恢复到初始状态(通常是清空或恢复到默认值)。 2. 代码解析 re…...
Java后端API限流秘籍:高并发的防护伞与实战指南
目录导航 📜 🛡️ 为什么需要API限流?🧠 主流限流算法大解析👩💻 阿里巴巴的限流实践📏 四大黄金定律🤼 限流策略组合拳🏆 限流场景实战💻 技术实现方案🌟 最佳实践分享📈 结语与展望📚 推荐阅读 1. 🛡️ 为什么需要API限流? 在高并发环境中,未…...
团体协作项目总结Git
使用Git开放时候发现本地, 有些代码并没有被拉取到本地仓库, 又不想再commit一次, 这时候我就想到了 git commit --amend 合并提交 git commit --amend 修改git提交记录用法详解 可以将本次提交记录合并到上一次合并提交 git commit --amendgit rebase -i master^^ // 假设我…...
mysql 入门
1.已经下载过却卸载不干净?注册表清理不到位? 使用greek绿色版 强力卸载,可以一键卸载注册表里的信息。 2.如何启动mysql服务? 以管理员方式启动cmd 输入 net start mysql80 如何停止? net stop mysql80 2.将mysql客…...
1.基于TCP的简单套接字服务器实现
目录 1. TCP通信流程 2. 服务器端的通信流程 2.1 创建用于监听的套接字 2.2 绑定本地IP地址和端口 2.3 设置监听 2.4 等待接受客户端的连接请求 2.5 与客户端进行通信 2.6 客户端连接服务器 3.代码实现 client.cpp server.cpp 运行方式 在本文中,我们将…...
MantisBT在Windows10上安装部署详细步骤
MantisBT 是一款基于 Web 的开源缺陷跟踪系统,以下是在 Windows 10 上安装部署 MantisBT 的详细步骤: 1. 安装必要的环境 MantisBT 是一个基于 PHP 的 Web 应用程序,因此需要安装 Web 服务器(如 Apache)、PHP 和数据…...
zookeepernacoskafka之间的联系
一、ZooKeeper与Kafka的协同工作原理 1. 核心关系:Kafka对ZooKeeper的依赖 在Kafka 2.8版本之前,ZooKeeper是Kafka集群的“大脑”,负责管理集群元数据、协调节点状态和故障恢复。两者的协同主要通过以下关键机制实现: Broker注册…...
【QT】 布局器
参考博客:https://blog.csdn.net/Fdog_/article/details/107522283 目录 布局管理器概念常见的布局管理器及特点🔵QHBoxLayout水平布局🔵QVBoxLayout垂直布局 🔵QGridLayout网格布局 🔵QFormLayout表单布局 QT 高级布…...
力扣45.跳跃游戏
45. 跳跃游戏 II - 力扣(LeetCode) 代码区: #include<vector> class Solution {public:int jump(vector<int>& nums) {int ans[10005] ;memset(ans,1e4,sizeof(ans));ans[0]0;for(int i0;i<nums.size();i){for(int j1;j…...