2025-03-25 Unity 网络基础4——TCP同步通信
文章目录
- 1 Socket
- 1.1 Socket 类型
- 1.2 构造 Socket
- 1.3 常用属性
- 1.4 常用方法
- 2 TCP 通信
- 2.1 服务端配置
- 2.2 客户端配置
- 2.3 进行通信
- 2.4 多设备通信
- 3 区分消息
1 Socket
Socket 是 C# 提供的网络通信类(其它语言也有对应的 Socket 类),是支持 TCP/IP 网络通信的基本操作单位。
- 类名:
Socket - 命名空间:
System.Net.Sockets
一个套接字对象包含以下关键信息:
- 本机的 IP 地址和端口。
- 对方主机的 IP 地址和端口。
- 双方通信的协议信息。
一个 Sccket 对象表示一个本地或者远程套接字信息,可被视为一个数据通道,连接与客户端和服务端,数据的发送和接受均通过这个通道进行。
一般长连接游戏会使用 Socket 套接字作为通信方案。
1.1 Socket 类型
Socket 套接字有 3 种不同的类型:
-
流套接字
主要用于实现 TCP 通信,提供面向连接、可靠的、有序的、数据无差错且无重复的数据传输服务。
-
数据报套接字
主要用于实现 UDP 通信,提供无连接的通信服务,数据包长度不能大于 32KB,不提供正确性检查,不保证顺序,可能出现重发、丢失等情况。
-
原始套接字(不常用)
主要用于实现 IP 数据包通信,用于直接访问协议的较低层,常用于侦听和分析数据包。
1.2 构造 Socket
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);
-
参数 1:AddressFamily
网络寻址 枚举类型,决定寻址方案。
- InterNetwork:IPv4 寻址(常用)
- InterNetwork6:IPv6 寻址(常用)
- UNIX:UNIX 本地到主机地址
- ImpLink:ARPANETIMP 地址
- Ipx:IPX 或 SPX 地址
- Iso:ISO 协议的地址
- Osi:OSI 协议的地址
- NetBios:NetBios 地址
- Atm:本机 ATM 服务地址
-
参数 2:SocketType
套接字枚举类型,决定使用的套接字类型。
- Dgram:支持数据报,最大长度固定的无连接、不可靠的消息(常用,主要用于 UDP 通信)
- Stream:支持可靠、双向、基于连接的字节流(常用,主要用于 TCP 通信)
- Raw:支持对基础传输协议的访问
- Rdm:支持无连接、面向消息、以可靠方式发送的消息
- Seqpacket:提供排序字节流的面向连接且可靠的双向传输
-
参数 3:ProtocolType
协议类型枚举类型,决定套接字使用的通信协议。
- TCP:TCP 传输控制协议(常用)
- UDP:UDP 用户数据报协议(常用)
- IP:IP 网际协议
- Icmp:Icmp 网际消息控制协议
- Igmp:Igmp 网际组管理协议
- Ggp:网关到网关协议
- IPv4:Internet 协议版本 4
- Pup:PARC 通用数据包协议
- Idp:Internet 数据报协议
- Raw:原始 IP 数据包协议
- Ipx:Internet 数据包交换协议
- Spx:顺序包交换协议
- IcmpV6:用于 IPv6 的 Internet 控制消息协议
参数 2、3 的常用搭配:
- SocketType.Dgram + ProtocolType.Udp = UDP 协议通信(常用)
- SocketType.Stream + ProtocolType.Tcp = TCP 协议通信(常用)
- SocketType.Raw + ProtocolType.Icmp = Internet 控制报文协议
- SocketType.Raw + ProtocolType.Raw = 简单 IP 包通信
// TCP 流套接字
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);// UDP 数据报套接字
Socket socketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
1.3 常用属性
// 1.套接字的连接状态
if (socketTcp.Connected)
{ }// 2.获取套接字的类型
print(socketTcp.SocketType);// 3.获取套接字的协议类型
print(socketTcp.ProtocolType);// 4.获取套接字的寻址方案
print(socketTcp.AddressFamily);// 5.从网络中获取准备读取的数据数据量
print(socketTcp.Available);// 6.获取本机 EndPoint 对象(IPEndPoint 继承 EndPoint)
// socketTcp.LocalEndPoint as IPEndPoint// 7.获取远程 EndPoint 对象
// socketTcp.RemoteEndPoint as IPEndPoint
1.4 常用方法
- 主要用于服务端
// 1-1: 绑定IP和端口
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socketTcp.Bind(ipPoint);// 1-2: 设置客户端连接的最大数量
socketTcp.Listen(10);// 1-3: 等待客户端连入
socketTcp.Accept();
- 主要用于客户端
// 2-1: 连接远程服务端
socketTcp.Connect(IPAddress.Parse("118.12.123.11"), 8080);
- 客户端服务端都会用
// 3-1: 同步发送和接收数据
// socketTcp.Send(...);
// socketTcp.Receive(...);// 3-2: 异步发送和接收数据
// socketTcp.SendAsync(...);
// socketTcp.ReceiveAsync(...);// 3-3: 释放连接并关闭 Socket,先于 Close 调用
socketTcp.Shutdown(SocketShutdown.Both);// 3-4: 关闭连接,释放所有Socket关联资源
socketTcp.Close();
2 TCP 通信
2.1 服务端配置
以 Rider IDE 为例,创建控制台程序。
- 创建 TCP 套接字
// 创建一个TCP套接字
var socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{// 创建一个IP地址和端口号的终结点var ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080); // 填写服务器本机的 IP 地址和端口号// 绑定套接字到指定的终结点socketTcp.Bind(ipPoint);
}
catch (Exception e)
{// 如果绑定失败,输出错误信息Console.WriteLine("绑定报错:" + e);return;
}
- 连接客户端
// 开始监听连接
socketTcp.Listen(1024);
Console.WriteLine("服务器已启动,等待客户端连接");// 接受客户端连接
var socketClient = socketTcp.Accept(); // Accept() 会同步等待连接
Console.WriteLine("客户端已连接");
- 发送消息并等待回复
// 向客户端发送消息
socketClient.Send(Encoding.UTF8.GetBytes("你好,客户端!"));// 接收客户端发送的消息
var result = new byte[1024];
var receiveLength = socketClient.Receive(result);
- 输出接受内容并中断连接
// 输出客户端发送的消息
Console.WriteLine($"客户端 {socketClient.RemoteEndPoint} 发送的消息:" + Encoding.UTF8.GetString(result, 0, receiveLength));// 关闭套接字
socketClient.Shutdown(SocketShutdown.Both);
socketClient.Close();Console.WriteLine("按任意键退出");
Console.ReadKey();
2.2 客户端配置
进入 Unity,创建脚本并挂载到场景上。
using System;
using System.Net.Sockets;
using System.Text;public class Lesson6 : MonoBehaviour
{private void Start(){... // 编写代码}
}
- 与服务器建立连接
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);try // 使用 try 块包裹防止连接失败报错
{socket.Connect("127.0.0.1", 8080); // 填写服务器的 ip 地址和端口号
}
catch (SocketException e) // 网络通信异常
{if (e.ErrorCode == 10061) // 10061 错误码表示服务器拒绝连接{Debug.Log("服务器拒绝连接");}else{Debug.Log("连接失败");}return;
}
- 接受与发送消息
var receiveBytes = new byte[1024];
var receiveLength = socket.Receive(receiveBytes); // 接收一条消息后才继续工作
print("接收到数据:" + System.Text.Encoding.UTF8.GetString(receiveBytes, 0, receiveLength));socket.Send(Encoding.UTF8.GetBytes("Hello World!"));
- 断开连接
socket.Shutdown(SocketShutdown.Both);
socket.Close();
2.3 进行通信
首先运行服务器。
进入 Unity,点击运行,可看到通信结果。
服务器端输出结果如下:
2.4 多设备通信
Socket.Accept() 方法会阻塞当前线程,直至接收到设备通信为止。因此,上述方法只能与一台指定设备进行通信。
为实现多设备通信,需创建新线程监听客户端的连接。
class Program
{// TCP 套接字private static Socket _SocketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);// 客户端套接字列表private static List<Socket> _ClientSockets = new List<Socket>();// 运行标志private static bool _Running = false;...
}
Main 函数的处理步骤包括以下 4 步:
- 绑定 IP 和端口,开始监听
var ipAddress = IPAddress.Parse("127.0.0.1");
var port = 8080;
var endPoint = new IPEndPoint(ipAddress, port);
_SocketTcp.Bind(endPoint);
_SocketTcp.Listen(1024);
_Running = true;
- 启动线程等待客户端连接
var acceptThread = new Thread(AcceptClientThread);
acceptThread.Start();
- 启动线程接收客户端消息
var receiveThread = new Thread(ReceiveMessageThread);
receiveThread.Start();
-
主线程处理用户输入
这里规定,输入“exit”退出服务器,输入“send”向所有客户端发送消息。
while (true)
{var input = Console.ReadLine();if (input == "exit") // 输入命令关闭服务器{_Running = false;for (int i = 0; i < _ClientSockets.Count; i++){_ClientSockets[i].Shutdown(SocketShutdown.Both);_ClientSockets[i].Close();}_ClientSockets.Clear();break;}else if (input == "send") // 输入命令向所有客户端发送消息{for (int i = 0; i < _ClientSockets.Count; i++){_ClientSockets[i].Send("Hello, client!"u8.ToArray());Console.WriteLine("Send Hello");}}
}
连接客户端 AcceptClientThread、接受消息 ReceiveMessage 的线程工作如下:
private static void AcceptClientThread()
{while (_Running){var clientSocket = _SocketTcp.Accept();_ClientSockets.Add(clientSocket);clientSocket.Send("Welcome to the server!"u8.ToArray()); // 由于客户端规定为接收一条消息后才继续工作,因此这里需要发送一条消息}
}// 接收客户端消息的线程
private static void ReceiveMessageThread()
{var buffer = new byte[1024 * 1024];int receiveLength;Socket clientSocket;while (_Running){for (int i = 0; i < _ClientSockets.Count; i++){clientSocket = _ClientSockets[i];// 判断是否有可接收的消息if (clientSocket.Available > 0){receiveLength = clientSocket.Receive(buffer);// 使用线程池处理接收到的消息,而不是立即处理// 防止用户等待时间过长ThreadPool.QueueUserWorkItem(ReceiveMessage, (clientSocket, Encoding.UTF8.GetString(buffer, 0, receiveLength)));}}}
}
在 ReceiveMessage 中,使用线程池处理接收到的消息,而不是立即处理,防止用户等待时间过长。接收消息后的工作通过 ReceiveMessage 方法定义:
// 处理接收到的消息
private static void ReceiveMessage(object? state)
{if (state == null) return;(Socket socket, string str) info = ((Socket socket, string str)) state;Console.WriteLine($"Receive message from client {info.socket}: {info.str}");
}
3 区分消息
数据对象序列化后是长度不同的字节数组,将它们发送出去后,对方如何区分是什么消息?如何选择对应的数据类进行反序列化?
解决方案
为发送的信息添加标识 ID。
例如,选用 int 类型作为消息 ID 类型,前 4 个字节为消息 ID,后面的字节为数据类的内容。每次收到消息时,先把前 4 个字节取出来解析为消息 ID,再根据 ID 进行消息反序列化。
实践
-
定义消息接口。
public interface INetMessage {int MessageId { get; }int BytesLength { get; }byte[] ToBytes();int FromBytes(byte[] bytes, int index); } -
创建消息类型
public class PlayerMessage : INetMessage {public int PlayerId;public string Name;public int Atk;public int Lev;public int MessageId { get => 1001; }public int BytesLength{get => this.GetBytesLength(MessageId) + // 消息长度this.GetBytesLength(PlayerId) +this.GetBytesLength(Name) +this.GetBytesLength(Atk) +this.GetBytesLength(Lev);}public byte[] ToBytes(){var bytes = new byte[BytesLength];var index = 0;index = this.Write(bytes, index, MessageId);index = this.Write(bytes, index, PlayerId);index = this.Write(bytes, index, Name);index = this.Write(bytes, index, Atk);index = this.Write(bytes, index, Lev);return bytes;}public int FromBytes(byte[] bytes, int index){// 反序列化不需要解析 Id,在此之前应解析 Id 从而使用该方法index = this.Read(bytes, index, ref PlayerId);index = this.Read(bytes, index, ref Name);index = this.Read(bytes, index, ref Atk);index = this.Read(bytes, index, ref Lev);return index;}public override string ToString(){return $"PlayerMessage: {PlayerId}, {Name}, {Atk}, {Lev}";} }其中的
GetBytesLength、Write和Read方法均由拓展类提供:public static class ByteLengthExtension {public static int GetBytesLength(this INetMessage message, int value){return sizeof(int);}public static int GetBytesLength(this INetMessage message, string value){return sizeof(int) + Encoding.UTF8.GetByteCount(value);}public static int GetBytesLength(this INetMessage message, bool value){return sizeof(bool);}public static int GetBytesLength(this INetMessage message, float value){return sizeof(float);} }public static class INetMessageExtension {public static int Write(this INetMessage message, byte[] bytes, int index, int value){BitConverter.GetBytes(value).CopyTo(bytes, index);return index + sizeof(int);}public static int Read(this INetMessage message, byte[] bytes, int index, ref int value){value = BitConverter.ToInt32(bytes, index);return index + sizeof(int);}public static int Write(this INetMessage message, byte[] bytes, int index, string value){var strBytes = Encoding.UTF8.GetBytes(value);BitConverter.GetBytes(strBytes.Length).CopyTo(bytes, index);index += sizeof(int);strBytes.CopyTo(bytes, index);return index + strBytes.Length;}public static int Read(this INetMessage message, byte[] bytes, int index, ref string value){int length = BitConverter.ToInt32(bytes, index);index += sizeof(int);value = Encoding.UTF8.GetString(bytes, index, length);return index + length;}public static int Write(this INetMessage message, byte[] bytes, int index, bool value){BitConverter.GetBytes(value).CopyTo(bytes, index);return index + sizeof(bool);}public static int Read(this INetMessage message, byte[] bytes, int index, ref bool value){value = BitConverter.ToBoolean(bytes, index);return index + sizeof(bool);}public static int Write(this INetMessage message, byte[] bytes, int index, float value){BitConverter.GetBytes(value).CopyTo(bytes, index);return index + sizeof(float);}public static int Read(this INetMessage message, byte[] bytes, int index, ref float value){value = BitConverter.ToSingle(bytes, index);return index + sizeof(float);}public static int Write(this INetMessage message, byte[] bytes, int index, INetMessage value){value.ToBytes().CopyTo(bytes, index);return index + value.BytesLength;}public static int Read(this INetMessage message, byte[] bytes, int index, ref INetMessage value){value.FromBytes(bytes, index);return index + value.BytesLength;} } -
创建消息类型 PlayerMessage
public class PlayerMessage : INetMessage {public int PlayerId;public string Name;public int Atk;public int Lev;public int MessageId { get => 1001; }public int BytesLength{get => this.GetBytesLength(MessageId) + // 消息长度this.GetBytesLength(PlayerId) +this.GetBytesLength(Name) +this.GetBytesLength(Atk) +this.GetBytesLength(Lev);}public byte[] ToBytes(){var bytes = new byte[BytesLength];var index = 0;index = this.Write(bytes, index, MessageId);index = this.Write(bytes, index, PlayerId);index = this.Write(bytes, index, Name);index = this.Write(bytes, index, Atk);index = this.Write(bytes, index, Lev);return bytes;}public int FromBytes(byte[] bytes, int index){// 反序列化不需要解析 Id,在此之前应解析 Id 从而使用该方法index = this.Read(bytes, index, ref PlayerId);index = this.Read(bytes, index, ref Name);index = this.Read(bytes, index, ref Atk);index = this.Read(bytes, index, ref Lev);return index;}public override string ToString(){return $"PlayerMessage: {PlayerId}, {Name}, {Atk}, {Lev}";} } -
进行通信。
客户端:
public class Lesson6 : MonoBehaviour {private void Start(){// 创建一个 Socket 对象,指定地址族、套接字类型和协议类型var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);try // 使用 try 块包裹防止连接失败报错{socket.Connect("127.0.0.1", 8080); // 填写服务器的 ip 地址和端口号}catch (SocketException e) // 网络通信异常{if (e.ErrorCode == 10061) // 10061 错误码表示服务器拒绝连接{Debug.Log("服务器拒绝连接");}else{Debug.Log("连接失败");}return;}var receiveBytes = new byte[1024];var receiveLength = socket.Receive(receiveBytes); // 接收一条消息后才继续工作// 解析 Idvar id = BitConverter.ToInt32(receiveBytes, 0);switch (id){case 1001:var playerMsg = new PlayerMessage();playerMsg.FromBytes(receiveBytes, sizeof(int));Debug.Log(playerMsg);break;}// print("接收到数据:" + System.Text.Encoding.UTF8.GetString(receiveBytes, 0, receiveLength));socket.Send(Encoding.UTF8.GetBytes("Hello World!"));socket.Shutdown(SocketShutdown.Both);socket.Close();} }服务端:
// See https://aka.ms/new-console-template for more informationusing System.Net; using System.Net.Sockets; using System.Text; using Exercise;// 创建一个TCP套接字 var socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try {// 创建一个IP地址和端口号的终结点var ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080); // 填写服务器本机的 IP 地址和端口号// 绑定套接字到指定的终结点socketTcp.Bind(ipPoint); } catch (Exception e) {// 如果绑定失败,输出错误信息Console.WriteLine("绑定报错:" + e);return; }// 开始监听连接 socketTcp.Listen(1024); Console.WriteLine("服务器已启动,等待客户端连接");// 接受客户端连接 var socketClient = socketTcp.Accept(); Console.WriteLine("客户端已连接");// 向客户端发送消息 // socketClient.Send(Encoding.UTF8.GetBytes("你好,客户端!")); var playerMsg = new PlayerMessage() {PlayerId = 1,Name = "zheliku",Atk = 5,Lev = 10, }; socketClient.Send(playerMsg.ToBytes());// 接收客户端发送的消息 var result = new byte[1024]; var receiveLength = socketClient.Receive(result);// 输出客户端发送的消息 Console.WriteLine($"客户端 {socketClient.RemoteEndPoint} 发送的消息:" + Encoding.UTF8.GetString(result, 0, receiveLength));// 关闭套接字 socketClient.Shutdown(SocketShutdown.Both); socketClient.Close();Console.WriteLine("按任意键退出"); Console.ReadKey();
先运行服务器,后运行 Unity,可看到通信成功:
相关文章:
2025-03-25 Unity 网络基础4——TCP同步通信
文章目录 1 Socket1.1 Socket 类型1.2 构造 Socket1.3 常用属性1.4 常用方法 2 TCP 通信2.1 服务端配置2.2 客户端配置2.3 进行通信2.4 多设备通信 3 区分消息 1 Socket Socket 是 C# 提供的网络通信类(其它语言也有对应的 Socket 类),是…...
C++进阶(一)
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 前言 本篇博客是讲解函数的重载以及引用的知识点的。 文章目录 前言 1.函数重载 1.1何为函数重载 1.2函数重载的作用 1.3函数重载的实现 2.引用 2.1何为引用 2.2定义引用 2.3引用特性 2.4常引用 2…...
深度解读DeepSeek:开源周(Open Source Week)技术解读
深度解读DeepSeek:开源周(Open Source Week)技术解读 深度解读DeepSeek:源码解读 DeepSeek-V3 深度解读DeepSeek:技术原理 深度解读DeepSeek:发展历程 文章目录 一、开源内容概览Day1:FlashMLAD…...
AI Agent开发与应用
AI Agent开发与应用:本地化智能体实践——本地化智能体开发进展与主流框架分析 我要说的都在ppt里面了,相关复现工作请参考ai agent开发实例 OpenManus Dify Owl 第二个版本更新了对话的框架,通过gradio做了一个全新的界面 只测试了阿里云…...
石斛基因组-文献精读122
A chromosome-level Dendrobium moniliforme genome assembly reveals the regulatory mechanisms of flavonoid and carotenoid biosynthesis pathways 《染色体水平的石斛基因组组装揭示了黄酮类和胡萝卜素生物合成途径的调控机制》 摘要 石斛(Dendrobium monil…...
javaSE.多维数组
1 final 引用类型 final int[] arr 继承Object 的引用类型,不能改变引用的对象 存的其实是引用 数组类型数组,其实存的是引用 int [][] arr new int[][] { {1,2,3}, {4,5,6} };int [] a arr[0]; int [] b arr[1];...
Spring IOC容器详解:深入理解控制反转与依赖注入
一、什么是IOC? 在java当中一个类想要使用另一个类的方法,就必须在这个类当中创建这个类的对象,那么可能会出现如下情况, 比如A类当中创建着B对象,B类当中有C对象,C类当中有A对象,这个如果一个类…...
Python条件处理,新手入门到精通
Python条件处理,新手入门到精通 对话实录 **小白**:(崩溃)我写了if x 1:,为什么Python会报错? **专家**:(推眼镜)**是赋值,才是比较**!想判断相…...
JPA实体类注解缺失异常全解:从报错到防御!!!
🚨 JPA实体类注解缺失异常全解:从报错到防御 🛡️ 一、💥 问题现象速览 // 经典报错示例 Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.entity.Product典型症状: &…...
Spring 源码硬核解析系列专题(三十二):Spring Cloud LoadBalancer 的负载均衡源码解析
在前几期中,我们从 Spring 核心到 Spring Boot 的多个模块,再到 Spring Cloud Alibaba,逐步揭示了 Spring 生态在微服务领域的广泛应用。Spring Cloud LoadBalancer 是 Spring Cloud 提供的客户端负载均衡组件,替代 Ribbon,支持服务发现和负载均衡策略。本篇将深入 Spring…...
生成式媒介革命已至,搜索如何借力DeepSeek破局?
作为前沿AI技术的代表,DeepSeek不仅突破了传统大模型的算力瓶颈,更以“高性能低成本开源生态”的特性,重塑传播生态。对于搜索行业从业者而言,这场技术变革既是机遇,也是挑战。 DeepSeek的三大“杀手锏”,…...
【Vue3入门1】02- Vue3的基本操作(上)
本文介绍vue3中的一些方法的操作。 目录 1. 绑定事件 v-on 2. 按键修饰符 3. 显示和隐藏 v-show 4. 条件渲染 v-if 5. 条件渲染if-else 1. 绑定事件 v-on 点击事件 v-on:click" 发生事件 " <body><div id"app">{{ msg }} <h2&g…...
【C语言】多进程/多线程
【C语言】多进程/多线程 参考链接多进程/多线程服务器1. 多进程服务器2. 多线程服务器 结语参考链接 参考链接 c 中文网 菜鸟 c 多进程/多线程服务器 多进程和多线程是常用的并发编程技术。它们都允许程序同时执行多个任务,提高了系统的资源利用率和程序的运行效率…...
模糊数学 | 模型 / 集合 / 关系 / 矩阵
注:本文为来自 “模糊数学 | 模型及其应用” 相关文章合辑。 略作重排。 如有内容异常,请看原文。 模糊数学模型:隶属函数、模糊集合的表示方法、模糊关系、模糊矩阵 wamg 潇潇 于 2019-05-06 22:35:21 发布 1.1 模糊数学简介 1965 年&a…...
Browserlist 使用指南:应对浏览器兼容性问题的解决方案
前言 在前端开发中,我们经常需要处理各种不同的浏览器兼容性问题。每个浏览器的版本众多,处理这些问题可能会让人感到头疼。幸运的是,有一个名为 Browserlist 的工具可以大大简化这项工作。本文将介绍 Browserlist 的作用和使用方法…...
QinQ项展 VLAN 空间
随着以太网技术在网络中的大量部署,利用 VLAN 对用户进行隔离和标识受到很大限制。因为 IEEE802.1Q 中定义的 VLAN Tag 域只有 12 个比特,仅能表示 4096 个 VLAN,无法满足城域以太网中标识大量用户的需求,于是 QinQ 技术应运而生。…...
数据结构—树(java实现)
目录 一、树的基本概念1.树的术语2.常见的树结构 二、节点的定义三、有关树结构的操作1.按照数组构造平衡 二叉搜索树2.层序遍历树3.前、中、后序遍历树(1).前序遍历树(2).中序遍历树(3).后序遍历树(4).各种遍历的情况的效果对比 4.元素添加5.元素删除1.删除叶子节点2.删除单一…...
Unity射击游戏手榴弹笔记
数据 在物品系统增加一个新的物品类,手榴弹类,定义手榴弹依附物体的类、配表数据类、背包内物品数据类、新建配表、在背包增加手榴弹数组;手榴弹的预制体需要可拾取的、扔出的;背包界面增加背包内的手榴弹、场景里的手榴弹、别人…...
S32K144外设实验(七):FTM输出多路互补带死区PWM
文章目录 1. 概述1.1 时钟系统1.2 实验目的2. 代码的配置2.1 时钟配置2.2 FTM模块配置2.3 输出引脚配置2.4 API函数调用1. 概述 互补对的PWM输出是很重要的外设功能,尤其应用再无刷电机的控制。 1.1 时钟系统 笔者再墨迹一遍时钟的设置,因为很重要。 FTM的CPU接口时钟为SY…...
SingleMod
SingleMod SingleMod是一种深度学习模型,专为利用纳米孔直接RNA测序(DRS)数据在单RNA分子中精确检测m6A修饰而设计。该模型通过深度多实例回归框架进行训练,能够充分利用广泛的甲基化率标签。SingleMod是一个通用框架,可轻松适配其他核酸修饰的检测模型训练。 注意: Si…...
[网鼎杯 2020 白虎组]PicDown1 [反弹shell] [敏感文件路径] [文件描述符]
常见读取路径 /etc/passwd一些用户和权限还有一些乱七八糟的 /proc/self/cmdline包含用于开始当前进程的命令 /proc/self/cwd/app.py当前工作目录的app.py /proc/self/environ包含了可用进程的环境变量 /proc/pid/exe 包含了正在进程中运行的程序链接; /proc/pid…...
单纯形法之大M法
1. 问题背景与标准化 在求解某些线性规划问题时,往往难以直接找到初始的基本可行解。特别是当约束中存在等式或 “≥” 类型的不等式时,我们需要引入人工变量来构造一个初始可行解。 考虑如下标准形式问题(假设为最大化问题)&am…...
各类神经网络学习:(四)RNN 循环神经网络(下集),pytorch 版的 RNN 代码编写
上一篇下一篇RNN(中集)待编写 代码详解 pytorch 官网主要有两个可调用的模块,分别是 nn.RNNCell 和 nn.RNN ,下面会进行详细讲解。 RNN 的同步多对多、多对一、一对多等等结构都是由这两个模块实现的,只需要将对输入…...
DeepSeek 发布DeepSeek-V3-0324 版本 前端与网页开发能力、推理与多任务能力提升
DeepSeek 发布 DeepSeek-V3-0324 版本 DeepSeek 发布 DeepSeek-V3-0324 版本,在其前代模型 DeepSeek-V3 的基础上进行了显著升级。 该模型专注于中文和多语言文本生成、推理、代码编写等综合能力的提升,支持 Function Calling(函数调用&…...
航班时间 | 第九届蓝桥杯省赛C++A组
小 h 前往美国参加了蓝桥杯国际赛。 小 h 的女朋友发现小 h 上午十点出发,上午十二点到达美国,于是感叹到“现在飞机飞得真快,两小时就能到美国了”。 小 hh 对超音速飞行感到十分恐惧。 仔细观察后发现飞机的起降时间都是当地时间。 由于…...
传输层安全协议 SSL/TLS 详细介绍
传输层安全性协议TLS及其前身安全套接层SSL是一种安全传输协议,目前TLS协议已成为互联网上保密通信的工业标准,在浏览器、邮箱、即时通信、VoIP等应用程序中得到广泛的应用。本文对SSL和TLS协议进行一个详细的介绍,以便于大家更直观的理解和认…...
编程实现自我指涉(self-reference)
从计算机的组成原理出发,编程实现自我指涉(self-reference)本质上是通过代码操纵代码,形成逻辑上的闭环。这种能力不仅是编程语言设计中的一个奇妙现象,更是计算理论、计算机架构、乃至哲学层面的一种深刻映射。让我们…...
CentOS8 安装 Docker-CE
如果之前安装过docker,请先卸载旧版本: yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine 安装所需的软件包: yum install -y yum-utils 添加软件源信息(设置存储库)…...
【Docker系列八】使用 Docker run 命令部署 Nginx
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
【单元测试】
一、框架 不同的编程语言有不同的测试框架,以下是一些常见的测试框架: 1)Java:JUnit、TestNG2)Python:unittest、pytest3)JavaScript:Jest、Mocha4)C#:NUni…...
