网络开发基础(游戏)之 Socket API
Socket简介
Socket (套接字)是网络编程的基础,在 C# 中通过 System.Net.Sockets
命名空间提供了一套完整的 API 来实现网络通信。
网络上的两个程序通过一个双向的通信连接实现数据交换, 这个连接的一端称为一个Socket。 一个Socket包含了进行网络通信必需的五种信息:连接使用的协议、 本地主机的IP地址、 本地的协议端口、 远程主机的IP地址和远程协议端口。
网络通信流程
服务端
1、注册绑定阶段:服务端初始化一个Socket对象,绑定服务器的IP地址和端口号(使用Bind方法)。
2、开启监听 ,使用Listen方法。
3、等待客户端连接,使用Accept方法。
4、收发数据:成功连接后,可以进行接收数据(使用Receive方法)和发送数据(使用Send方法)。
5、关闭连接,使用Close方法。
客户端
1、注册绑定阶段:客户端初始化一个Socket对象,绑定服务器的IP地址和端口号(使用Bind方法)。
2、连接服务器,使用Connect方法。
3、收发数据:成功连接后,可以进行接收数据(使用Receive方法)和发送数据(使用Send方法)。
4、关闭连接,使用Close方法。
Socket API
常用属性
API | 说明 |
AddressFamily | 指定Socket类的实例可以使用的寻址方案,一般知道以下两个即可: InterNetwork:IPv4地址 nterNetworkV6 :IPv6地址 |
SocketType | 指定Socket类的实例表示的套接字类型,一般知道以下两个即可: Stream:支持可靠、双向、基于连接的字节流,而不重复数据,也不保留边界。 此类型的 Socket 与单个对方主机通信,并且在通信开始之前需要建立远程主机连接。 Stream 使用传输控制协议 (Tcp) 和 Dgram:支持数据报,即最大长度固定(通常很小)的无连接、不可靠消息。 消息可能会丢失或重复并可能在到达时不按顺序排列。 Socket 类型的 Dgram 在发送和接收数据之前不需要任何连接,并且可以与多个对方主机进行通信。 Dgram 使用 Datagram 协议 (Udp) 和 |
ProtocolType | 指定Socket类支持的协议b一般知道以下两个即可: Tcp:传输控制协议 Udp:用户数据报协议 |
Available | 获取已经从网络接收且可供读取的数据量。 |
Blocking | |
Connected | 如果 Socket 在最近操作时连接到远程资源,则为 true;否则为 false。 |
IsBound | |
SendBufferSize | |
SendTimeout | |
ReceiveBufferSize | |
ReceiveTimeout | |
常用方法
API | 说明 |
Bind | 使Socket与一个本地终结点相关联 |
Listen | 将Socket置于监听状态 |
Close | 关闭Socket连接并释放所有关联的资源 |
Shutdown | 禁用某Socket上的发送和接收 |
GetSocketOption | 获取Socket选项的值 |
SetSocketOption | 设置Socket选项 |
Poll | 确定Socket状态 |
同步方法
均为阻塞方法,程序会卡住直到成功响应
API | 说明 |
Connect | 建立与远端主机的连接 |
Accept | 为新建连接创建新的Socket |
Send | 发送数据 |
Receive | 接收数据 |
Disconnect | 关闭套接字连接并允许重用套接字 |
异步方法
与同步方法不同的是,异步方法不会阻塞线程。
只有在主线程中才能操作UI组件,由于异步回调是在其他线程执行的。可以通过缓存接收数据,再在Update中进行读取并传递给UI组件。
两种异步方式说明
在 C# 中,xxxAsync 和 Beginxxx/Endxxx 都是用于异步网络通信的方法,但它们属于不同的异步编程模型。
xxxAsync
ReceiveAsync
是 .NET Framework 4.5+ 和 .NET Core/.NET 5+ 引入的现代异步方法,基于 Task
的异步模式(TAP),适合 async/await
编程。
适用场景
-
推荐在新项目中使用,代码更简洁,可读性更好。
-
适用于 .NET Core / .NET 5+ 和 .NET Framework 4.5+。
-
配合
async/await
使用,避免回调地狱。
特点
1、简洁:直接 await
等待数据,无需回调。
2、可取消:可配合 CancellationToken
使用。
3、现代推荐:适用于新代码。
Beginxxx
/ Endxxx
BeginReceive
和 EndReceive
是 .NET 早期的异步编程模型(APM,Asynchronous Programming Model),基于 IAsyncResult
和回调机制。
适用场景
-
旧版 .NET Framework(4.0 及以下) 兼容代码。
-
需要手动管理
IAsyncResult
和回调。
特点
1、回调模式:需要手动处理 IAsyncResult
和回调函数。
2、较旧 API:不推荐在新代码中使用,除非维护旧项目。
3、无 async/await
支持:代码结构可能更复杂。
对比总结
特性 | ReceiveAsync (TAP) | BeginReceive /EndReceive (APM) |
---|---|---|
编程模型 | async/await (推荐) | 回调模式(IAsyncResult ) |
代码可读性 | 高(线性执行) | 低(嵌套回调) |
适用版本 | .NET 4.5+, .NET Core | .NET Framework 所有版本 |
取消支持 | 支持 (CancellationToken ) | 不支持 |
推荐使用场景 | 新项目、现代异步代码 | 旧代码维护 |
如何选择?
-
新项目 ➔
ReceiveAsync
+async/await
(代码更清晰,维护方便)。 -
旧项目维护 ➔
BeginReceive
/EndReceive
(兼容旧版 .NET)。 -
高性能场景 ➔ 也可以考虑
SocketAsyncEventArgs
(更低级别的异步模型)。
结论
-
优先使用
ReceiveAsync
(现代、简洁、可取消)。 -
仅在旧代码中保留
BeginReceive
/EndReceive
(兼容性需求)。
Beginxxx/Endxxx
API | 说明 |
BeginConnect | 开启一个与远端主机的连接的异步请求 |
EndConnect | 结束挂起的异步连接请求 |
BeginAccept | 开始一个异步操作来接受一个传入的连接尝试 |
EndAccept | 异步接受传入的连接尝试 |
BeginSend | 将数据异步发送到连接的 Socket |
EndSend | 结束挂起的异步发送 |
BeginReceive | 开始从连接的 Socket 中异步接收数据 |
EndReceive | 结束挂起的异步读取 |
BeginDisconnect | 开始异步请求从远程终结点断开连接 |
EndDisconnect | 结束挂起的异步断开连接请求 |
BeginConnect/EndConnect
用于客户端异步连接服务服务端
public void ConnectToServer(string host, int port)
{Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);// 开始异步连接clientSocket.BeginConnect(host, port, ConnectCallback, clientSocket);
}private void ConnectCallback(IAsyncResult ar)
{try{Socket clientSocket = (Socket)ar.AsyncState;// 完成连接操作clientSocket.EndConnect(ar);Console.WriteLine("Connected to server");// 连接成功后开始接收数据StartReceiving(clientSocket);}catch (SocketException ex){Console.WriteLine($"Connection failed: {ex.SocketErrorCode}");}catch (Exception ex){Console.WriteLine($"Connection error: {ex.Message}");}
}
BeginAccept/EndAccept
用于服务端异步接受客户端连入
public void StartServer(int port)
{Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);listener.Bind(new IPEndPoint(IPAddress.Any, port));listener.Listen(100);// 开始异步接受连接listener.BeginAccept(AcceptCallback, listener);
}private void AcceptCallback(IAsyncResult ar)
{Socket listener = (Socket)ar.AsyncState;try{// 完成接受连接操作Socket clientSocket = listener.EndAccept(ar);Console.WriteLine($"New connection from {clientSocket.RemoteEndPoint}");// 开始接收数据StartReceiving(clientSocket);// 继续接受新连接listener.BeginAccept(AcceptCallback, listener);}catch (SocketException ex){Console.WriteLine($"Accept failed: {ex.SocketErrorCode}");}catch (ObjectDisposedException){// 监听器已关闭}catch (Exception ex){Console.WriteLine($"Accept error: {ex.Message}");}
}
BeginSend/EndSend
用于客户端/服务端异步发送数据
public void SendData(Socket socket, byte[] data)
{try{// 开始异步发送socket.BeginSend(data, 0, data.Length, SocketFlags.None, SendCallback, socket);}catch (SocketException ex){Console.WriteLine($"Send error: {ex.SocketErrorCode}");socket.Close();}
}private void SendCallback(IAsyncResult ar)
{Socket socket = (Socket)ar.AsyncState;try{// 完成发送操作int bytesSent = socket.EndSend(ar);Console.WriteLine($"Sent {bytesSent} bytes");}catch (SocketException ex){Console.WriteLine($"Send completion error: {ex.SocketErrorCode}");socket.Close();}catch (ObjectDisposedException){// Socket已关闭}catch (Exception ex){Console.WriteLine($"Send error: {ex.Message}");socket.Close();}
}
BeginReceive/EndReceive
用于客户端/服务端异步接收数据
private void StartReceiving(Socket socket)
{byte[] buffer = new byte[8192];try{// 开始异步接收socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, new ReceiveState { Socket = socket, Buffer = buffer });}catch (SocketException ex){Console.WriteLine($"Receive error: {ex.SocketErrorCode}");socket.Close();}
}private void ReceiveCallback(IAsyncResult ar)
{ReceiveState state = (ReceiveState)ar.AsyncState;Socket socket = state.Socket;try{// 完成接收操作int bytesRead = socket.EndReceive(ar);if (bytesRead > 0){// 处理接收到的数据byte[] receivedData = new byte[bytesRead];Array.Copy(state.Buffer, 0, receivedData, 0, bytesRead);Console.WriteLine($"Received: {Encoding.UTF8.GetString(receivedData)}");// 继续接收数据StartReceiving(socket);}else{// 连接已关闭Console.WriteLine("Connection closed by remote host");socket.Close();}}catch (SocketException ex){Console.WriteLine($"Receive error: {ex.SocketErrorCode}");socket.Close();}catch (ObjectDisposedException){// Socket已关闭}catch (Exception ex){Console.WriteLine($"Receive error: {ex.Message}");socket.Close();}
}// 用于传递状态的辅助类
class ReceiveState
{public Socket Socket { get; set; }public byte[] Buffer { get; set; }
}
BeginDisconnect/EndDisconnect
用于客户端异步断开连接服务端
public void DisconnectSocket(Socket socket)
{try{// 开始异步断开连接socket.BeginDisconnect(false, DisconnectCallback, socket);}catch (SocketException ex){Console.WriteLine($"Disconnect error: {ex.SocketErrorCode}");socket.Close();}
}private void DisconnectCallback(IAsyncResult ar)
{Socket socket = (Socket)ar.AsyncState;try{// 完成断开连接操作socket.EndDisconnect(ar);Console.WriteLine("Disconnected successfully");socket.Close();}catch (SocketException ex){Console.WriteLine($"Disconnect error: {ex.SocketErrorCode}");socket.Close();}catch (ObjectDisposedException){// Socket已关闭}catch (Exception ex){Console.WriteLine($"Disconnect error: {ex.Message}");socket.Close();}
}
xxxAsync
如果异步套接字方法 (xxxAsync) 返回 true,请在Completed 回调中查询完成状态、获取操作结果。如果异步套接字方法 (xxxAsync) 返回 false,则操作同步完成,将不会引发 e 参数的 Completed 事件。 可查询上下文属性获取操作结果。
API | 说明 |
AcceptAsync | 开始一个异步操作来接受一个传入的连接尝试 |
ConnectAsync | 开启一个与远端主机的连接的异步请求 |
SendAsync | 将数据异步发送到连接的 Socket |
ReceiveAsync | 开始从连接的 Socket 中异步接收数据 |
DisconnectAsync | 开始异步请求从远程终结点断开连接 |
SocketAsyncEventArgs
SocketAsyncEventArgs
类通过重用操作上下文和缓冲区来减少内存分配,主要特点包括:
-
重用操作上下文对象
-
支持缓冲区池
-
减少异步操作中的内存分配
-
提供更细粒度的控制
通过合理使用 SocketAsyncEventArgs
,可以构建出高性能、可扩展的网络应用程序,特别适合需要处理大量并发连接的场景。
关键属性
// 缓冲区相关
public byte[] Buffer { get; } // 当前使用的缓冲区
public int Offset { get; } // 缓冲区起始偏移
public int Count { get; } // 可操作字节数
public int BytesTransferred { get; } // 已传输字节数// 连接信息
public EndPoint RemoteEndPoint { get; set; }
public EndPoint LocalEndPoint { get; }// 操作状态
public SocketError SocketError { get; set; } // 操作结果
public SocketFlags SocketFlags { get; set; } // 特殊标志// 用户自定义数据
public object UserToken { get; set; } // 用户自定义对象
重要方法
// 缓冲区管理
public void SetBuffer(byte[] buffer, int offset, int count);
public void SetBuffer(int offset, int count);// 内存缓冲区管理
public void SetBuffer(Memory<byte> buffer); // .NET Core 新增// 操作结果获取
public Socket AcceptSocket { get; set; } // 用于Accept操作
AcceptAsync
var acceptArgs = new SocketAsyncEventArgs();
acceptArgs.UserToken = acceptSocket;
acceptArgs.Completed += OnAcceptCompleted;if (!listener.AcceptAsync(acceptArgs))
{// 操作同步完成OnAcceptCompleted(listener, acceptArgs);
}private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
{if (e.SocketError == SocketError.Success){Socket clientSocket = e.AcceptSocket;Console.WriteLine($"Client connected from: {clientSocket.RemoteEndPoint}");// 开始接收数据 ReceiveAsync// 准备接受下一个连接 AcceptAsynce.AcceptSocket = null; // 必须重置}else{Console.WriteLine($"Accept error: {e.SocketError}");}
}
ConnectAsync
var connectArgs = new SocketAsyncEventArgs();
connectArgs.RemoteEndPoint = new DnsEndPoint(host, port);
connectArgs.Completed += OnConnectCompleted;if (!clientSocket.ConnectAsync(connectArgs))
{// 操作同步完成OnConnectCompleted(clientSocket, connectArgs);
}private void OnConnectCompleted(object sender, SocketAsyncEventArgs e)
{if (e.SocketError == SocketError.Success){Console.WriteLine("Connected to server");// 连接成功后可以开始发送或接收数据}else{Console.WriteLine($"Connection failed: {e.SocketError}");}
}
SendAsync
public void SendData(Socket socket, byte[] data)
{var sendArgs = new SocketAsyncEventArgs();sendArgs.SetBuffer(data, 0, data.Length);sendArgs.Completed += OnSendCompleted;sendArgs.UserToken = socket;if (!socket.SendAsync(sendArgs)){// 操作同步完成OnSendCompleted(socket, sendArgs);}
}private void OnSendCompleted(object sender, SocketAsyncEventArgs e)
{if (e.SocketError == SocketError.Success){Console.WriteLine($"Sent {e.BytesTransferred} bytes");}else{Console.WriteLine($"Send error: {e.SocketError}");}
}
ReceiveAsync
private void StartReceiving(Socket socket)
{var receiveArgs = new SocketAsyncEventArgs();var buffer = new byte[4096];receiveArgs.SetBuffer(buffer, 0, buffer.Length);receiveArgs.Completed += OnReceiveCompleted;receiveArgs.UserToken = socket;if (!socket.ReceiveAsync(receiveArgs)){// 操作同步完成OnReceiveCompleted(socket, receiveArgs);}
}private void OnReceiveCompleted(object sender, SocketAsyncEventArgs e)
{Socket socket = (Socket)e.UserToken;if (e.SocketError == SocketError.Success && e.BytesTransferred > 0){// 处理接收到的数据byte[] data = new byte[e.BytesTransferred];Buffer.BlockCopy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);Console.WriteLine($"Received {e.BytesTransferred} bytes: {Encoding.UTF8.GetString(data)}");// 继续接收}else{// 连接已关闭或出错Console.WriteLine("Connection closed or error occurred");socket.Close();}
}
DisconnectAsync
public void DisconnectSocket(Socket socket)
{var disconnectArgs = new SocketAsyncEventArgs();disconnectArgs.Completed += OnDisconnectCompleted;disconnectArgs.UserToken = socket;disconnectArgs.DisconnectReuseSocket = false; // 是否重用socketif (!socket.DisconnectAsync(disconnectArgs)){// 操作同步完成OnDisconnectCompleted(socket, disconnectArgs);}
}private void OnDisconnectCompleted(object sender, SocketAsyncEventArgs e)
{if (e.SocketError == SocketError.Success){Console.WriteLine("Disconnected successfully");Socket socket = (Socket)e.UserToken;socket.Close();}else{Console.WriteLine($"Disconnect error: {e.SocketError}");}
}
Socket同步
第一版 Echo程序
此版本主要实现基础通信流程步骤
Echo 程序是最基础的网络通信案例,它能够将客户端发送的消息原样返回。
同步案例均使用同步API实现,是阻塞方法,会卡住程序进程直到成功响应。
客户端
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;
using System.Text;public class EchoClient : MonoBehaviour
{public InputField inputTxt;public Text txtShow;private Socket clientSocket;public void OnConnect(){if (clientSocket != null) return;clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);clientSocket.Connect(endPoint);txtShow.text = "成功连接服务器!";}//发送数据public void OnSend(){if (clientSocket == null) return;var str = inputTxt.text;var sendBytes = Encoding.UTF8.GetBytes(str);clientSocket.Send(sendBytes);txtShow.text = "成功发送消息!";if (str == "Close"){OnClose();}else{OnReceive();}}//接收数据public void OnReceive(){if (clientSocket == null) return;var buffer = new byte[1024];var index = clientSocket.Receive(buffer);var recStr = Encoding.UTF8.GetString(buffer, 0, index);txtShow.text = recStr;}//关闭Socketpublic void OnClose(){if (clientSocket == null) return;clientSocket.Shutdown(SocketShutdown.Both);clientSocket.Close();}
}
服务端
using System.Text;
using System.Net;
using System.Net.Sockets;public class EchoServer
{private Socket serverSocket;public byte[] bufferArray;public void Start(){if (serverSocket == null){serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);}if (bufferArray == null){bufferArray = new byte[1024];}IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);//绑定服务器IP地址和端口号serverSocket.Bind(endPoint);//开启监听,等待客户端连接//参数backlog指定队列中最多可容纳等待接受的连接数,0表示不限制serverSocket.Listen(0);Console.WriteLine("服务器启动成功!");//成功接收一个客户端连接var clientSocket = serverSocket.Accept();Console.WriteLine("有一个客户端连入!");Array.Clear(bufferArray);while (true){//接收客户端发来的消息var index = clientSocket.Receive(bufferArray);var recStr = Encoding.UTF8.GetString(bufferArray, 0, index);Console.WriteLine($"客户端发来消息:{recStr}");//向客户端发送消息var sendBytes = Encoding.UTF8.GetBytes($"我收到了你的消息:{recStr}");clientSocket.Send(sendBytes);Array.Clear(bufferArray);if (recStr == "Close"){break;}}Console.WriteLine("服务器关闭!");serverSocket.Close();}
}
第二版 多人聊天室
此版本主要实现需求:
1、多个客户端可连接
2、服务器可处理多个客户端的连入和消息广播
3、因为使用同步方法,为避免阻塞主线,使用多线程来处理对应的同步逻辑。
4、服务器使用一个容器来缓存已连接的客户端Socket,用于处理广播和持续通信。
客户端
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;
using System.Text;
using System.Threading;public class SimpleClient : MonoBehaviour
{public InputField inputTxt;public Text txtShow;private Socket clientSocket;private bool isClose = true;private Queue<string> msgList;private StringBuilder sb;private void Update(){if (msgList == null) return;if (msgList.Count > 0){sb.Clear();sb.Append(txtShow.text);while (msgList.Count > 0){sb.Append(msgList.Dequeue());sb.Append("\n");}txtShow.text = sb.ToString();}}public void OnConnect(){if (clientSocket != null) return;if (msgList == null){msgList = new Queue<string>();}sb = new StringBuilder();clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);clientSocket.Connect(endPoint);txtShow.text = "成功连接服务器!\n";isClose = false;ThreadPool.QueueUserWorkItem(OnReceive);}//发送数据public void OnSend(){if (clientSocket == null) return;var str = inputTxt.text;var sendBytes = Encoding.UTF8.GetBytes(str);clientSocket.Send(sendBytes);inputTxt.text = "";Debug.Log("成功发送消息!");}//接收数据public void OnReceive(object obj){while (!isClose){if (clientSocket == null) return;var buffer = new byte[1024];var index = clientSocket.Receive(buffer);var recStr = Encoding.UTF8.GetString(buffer, 0, index);msgList.Enqueue(recStr);}}//关闭Socketpublic void OnClose(){if (clientSocket == null){Application.Quit();return;}var sendBytes = Encoding.UTF8.GetBytes("Quit");clientSocket.Send(sendBytes);Debug.Log("关闭Socket");isClose = true;clientSocket.Shutdown(SocketShutdown.Both);clientSocket.Close();Application.Quit();}
}
服务端
using System.Text;
using System.Net;
using System.Net.Sockets;public class BetterServer()
{private Socket serverSocket;private Dictionary<int, ClientSocket> clientList;private bool isColse = true;private int clientFlag = 0;public void Start(string ip,int port){if (serverSocket != null) return;if(clientList == null){clientList = new Dictionary<int, ClientSocket>(); }serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);serverSocket.Bind(iPEndPoint);isColse = false;serverSocket.Listen(0);Console.WriteLine("服务器启动成功!");ThreadPool.QueueUserWorkItem(Accect);}//等待接收客户端连接请求public void Accect(object obj){while (!isColse){var client = serverSocket.Accept();clientFlag++;ClientSocket clientSocket = new ClientSocket(clientFlag, client);clientList.TryAdd(clientFlag, clientSocket);var connectTip = $"有一个客户端:{clientSocket.id}连入!";Console.WriteLine(connectTip); Broadcast(connectTip);ThreadPool.QueueUserWorkItem((obj) =>{while (!isColse){var str = clientSocket.Receive(() =>{clientList.Remove(clientSocket.id); clientSocket.Close();Console.WriteLine($"还有{clientList.Count}个客户端连接中"); });if (!string.IsNullOrEmpty(str)){Broadcast(str); }; }});}}//向客户端广播消息public void Broadcast(string info){foreach (ClientSocket client in clientList.Values){client.Send(info);}}//关闭服务器public void Close(){if (serverSocket == null) return;isColse = true; foreach (var client in clientList.Values){client.Close(); }serverSocket.Shutdown(SocketShutdown.Both);serverSocket.Close();}
}public class ClientSocket
{public int id;private Socket clientSocket; public ClientSocket(int clientId,Socket instance){id = clientId;clientSocket = instance;}public void Send(string str){if (clientSocket == null) return;ThreadPool.QueueUserWorkItem((a) =>{var sendBytes = Encoding.UTF8.GetBytes(str); clientSocket.Send(sendBytes);});}public string Receive(Action callback){if (clientSocket != null && clientSocket.Available > 0){var buffer = new byte[1024];var index = clientSocket.Receive(buffer);var recvStr = Encoding.UTF8.GetString(buffer, 0, index);var isQuit = recvStr == "Quit";if (isQuit){callback?.Invoke();}var msg = isQuit ? $"客户端{id}离开了" : $"客户端{id}:{recvStr}";Console.WriteLine(msg);return msg;}return "";}public void Close(){if (clientSocket == null) return;clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close();clientSocket = null;}
}
BetterServer server = new BetterServer();
server.Start("127.0.0.1",8080);
while (true)
{var flag = Console.ReadLine(); if(flag == "Close"){server.Close(); break; }else if(flag == "广播"){server.Broadcast("好好学习,天天向上");}
}
Socket异步
Async方法实现
客户端
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;
using System.Text;public class SimpleClient : MonoBehaviour
{public InputField inputTxt;public Text txtShow;private Socket clientSocket;//只有在主线程中才能操作UI组件,由于异步回调是在其他线程执行的。通过缓存接收的数据,再在Update中进行读取并传递给UI组件private Queue<string> msgList = new Queue<string>();private StringBuilder _stringBuilder = new StringBuilder();private void Update(){if (msgList.Count > 0){_stringBuilder.Clear();_stringBuilder.Append(txtShow.text);while (msgList.Count > 0){_stringBuilder.Append(msgList.Dequeue());_stringBuilder.Append("\n");}txtShow.text = _stringBuilder.ToString();}}//连接服务器public void OnConnect(){if (clientSocket != null) return;clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);var connectArgs = new SocketAsyncEventArgs();connectArgs.RemoteEndPoint = endPoint;connectArgs.Completed += ConnectCallback;if (!clientSocket.ConnectAsync(connectArgs)){//操作同步完成ConnectCallback(clientSocket, connectArgs);}}//连接服务器异步回调private void ConnectCallback(object sender, SocketAsyncEventArgs args){var socket = (Socket)sender;if (args.SocketError == SocketError.Success){msgList.Enqueue("成功连接服务器!");OnReceive(socket);}else{Debug.LogError($"连接服务器失败:{args.SocketError}");}}//发送数据public void OnSend(){if (clientSocket == null) return;var str = inputTxt.text;if (string.IsNullOrEmpty(str)){return;}var sendBytes = Encoding.UTF8.GetBytes(str);var sendArgs = new SocketAsyncEventArgs();sendArgs.SetBuffer(sendBytes,0,sendBytes.Length);sendArgs.Completed += SendCallback;if (!clientSocket.SendAsync(sendArgs)){//操作同步完成SendCallback(clientSocket, sendArgs);}inputTxt.text = "";}//发送数据异步回调private void SendCallback(object sender, SocketAsyncEventArgs args){if (args.SocketError == SocketError.Success){Debug.Log("发送消息成功!");}else{Debug.LogError($"发送消息失败:{args.SocketError}");}}//接收数据public void OnReceive(Socket socket){if (socket == null) return;var receiveArgs = new SocketAsyncEventArgs();var readBuff = new byte[1024];receiveArgs.SetBuffer(readBuff,0,readBuff.Length);receiveArgs.Completed += ReceiveCallback;if (!socket.ReceiveAsync(receiveArgs)){ReceiveCallback(socket, receiveArgs);}}//接收数据异步回调private void ReceiveCallback(object sender, SocketAsyncEventArgs args){if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success){var dataLength = args.BytesTransferred;byte[] data = new byte[dataLength];Buffer.BlockCopy(args.Buffer,args.Offset,data,0,dataLength);var recvMsg = Encoding.UTF8.GetString(data, 0, dataLength);msgList.Enqueue(recvMsg);// 继续接收下一条消息OnReceive((Socket)sender);}else{Debug.LogError($"接收数据失败:{args.SocketError}");OnClose();}}//关闭Socketpublic void OnClose(){if (clientSocket == null) return;clientSocket.Shutdown(SocketShutdown.Both);clientSocket.Close();Application.Quit();}
}
服务端
using System.Text;
using System.Net;
using System.Net.Sockets;public class EchoServer
{private Socket serverSocket;private class ClientInfo{public Socket clientSocket;public byte[] readBuff;public ClientInfo(Socket socket){clientSocket = socket; readBuff = new byte[1024];}}private Dictionary<Socket, ClientInfo> clientList;public void Start(){if (serverSocket == null){serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);}if (clientList == null){clientList = new Dictionary<Socket, ClientInfo>();}IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);//绑定服务器IP地址和端口号serverSocket.Bind(endPoint);//开启监听,等待客户端连接//参数backlog指定队列中最多可容纳等待接受的连接数,0表示不限制serverSocket.Listen(0);Console.WriteLine("服务器启动成功!");//成功接收一个客户端连接StartAccept();Console.ReadLine(); Console.WriteLine("服务器关闭!");serverSocket.Close();}private void StartAccept(){//成功接收一个客户端连接var acceptArgs = new SocketAsyncEventArgs();acceptArgs.Completed += AcceptCallback;if (!serverSocket.AcceptAsync(acceptArgs)){AcceptCallback(serverSocket, acceptArgs);}}//成功接收一个客户连接回调private void AcceptCallback(object sender, SocketAsyncEventArgs args){var mainSocket = (Socket)sender; if(args.SocketError == SocketError.Success){var socket = args.AcceptSocket;var clientInfo = new ClientInfo(socket);clientList.TryAdd(socket, clientInfo);Console.WriteLine($"有一个客户端连入!在线人数:{clientList.Count}");//开始接收客户端数据StartReceive(clientInfo);//继续等待接收下一个客户端连入args.AcceptSocket = null; // 必须重置if (!mainSocket.AcceptAsync(args)){AcceptCallback(mainSocket, args);};}else{Console.WriteLine($"Accept error: {args.SocketError}");}}//接收从客户端发来的消息private void StartReceive(ClientInfo clientInfo){var receiveArgs = new SocketAsyncEventArgs();var readBuff = new byte[1024]; receiveArgs.SetBuffer(readBuff,0,readBuff.Length);receiveArgs.Completed += ReceiveCallback;var socket = clientInfo.clientSocket;if (!socket.ReceiveAsync(receiveArgs)){ReceiveCallback(socket, receiveArgs);}}//接收数据异步回调private void ReceiveCallback(object sender, SocketAsyncEventArgs args){var clientSocket = (Socket)sender;var clientInfo = clientList[clientSocket];if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success){var dataLength = args.BytesTransferred;byte[] data = new byte[dataLength];Buffer.BlockCopy(args.Buffer, args.Offset, data, 0, dataLength);var recvMsg = Encoding.UTF8.GetString(data, 0, dataLength);Console.WriteLine($"接收客户端消息:{recvMsg}");//把接收的消息重新返回给客户端var sendBytes = Encoding.UTF8.GetBytes($"我收到了你的消息:{recvMsg}");StartSend(clientInfo, sendBytes);//继续接收客户端消息if (!clientSocket.ReceiveAsync(args)){ReceiveCallback(clientSocket, args);}}else{Console.WriteLine($"接收数据失败:{args.SocketError}");CloseClient(clientInfo);}}//发送消息给客户端private void StartSend(ClientInfo clientInfo, byte[] data){if (clientInfo == null) return;var sendArgs = new SocketAsyncEventArgs();sendArgs.SetBuffer(data,0,data.Length);sendArgs.Completed += SendCallback;var socket = clientInfo.clientSocket;if (!socket.SendAsync(sendArgs)){SendCallback(socket, sendArgs); } }//发送消息成功回调private void SendCallback(object sender,SocketAsyncEventArgs args){if(args.SocketError == SocketError.Success){Console.WriteLine($"成功告知客户端");}else{Console.WriteLine($"发送数据失败:{args.SocketError}");}}//关闭客户端连接private void CloseClient(ClientInfo clientInfo){if(clientInfo == null) return;clientInfo.clientSocket.Close();clientList.Remove(clientInfo.clientSocket);Console.WriteLine($"客户端断开连接了 在线人数:{clientList.Count}");}
}
Begin/End方法实现
客户端
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;
using System.Text;public class SimpleClient : MonoBehaviour
{public InputField inputTxt;public Text txtShow;private Socket clientSocket;//只有在主线程中才能操作UI组件,由于异步回调是在其他线程执行的。通过缓存接收的数据,再在Update中进行读取并传递给UI组件private Queue<string> msgList = new Queue<string>();private StringBuilder _stringBuilder = new StringBuilder();private byte[] readBuff = new byte[1024];private void Update(){if (msgList.Count > 0){_stringBuilder.Clear();_stringBuilder.Append(txtShow.text);while (msgList.Count > 0){_stringBuilder.Append(msgList.Dequeue());_stringBuilder.Append("\n");}txtShow.text = _stringBuilder.ToString();}}//连接服务器public void OnConnect(){if (clientSocket != null) return;clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);clientSocket.BeginConnect(endPoint, ConnectCallback, clientSocket);}//连接服务器异步回调private void ConnectCallback(IAsyncResult ar){try{var socket = (Socket)ar.AsyncState;socket.EndConnect(ar);msgList.Enqueue("成功连接服务器!");OnReceive();}catch (SocketException ex){Debug.LogError($"连接服务器失败:{ex}");}}//发送数据public void OnSend(){if (clientSocket == null) return;var str = inputTxt.text;var sendBytes = Encoding.UTF8.GetBytes(str);clientSocket.BeginSend(sendBytes, 0, sendBytes.Length,SocketFlags.None, SendCallback, clientSocket);}//发送数据异步回调private void SendCallback(IAsyncResult ar){try{var socket = (Socket)ar.AsyncState;int count = socket.EndSend(ar);}catch (SocketException ex){Debug.LogError($"发送消息失败:{ex}");}}//接收数据public void OnReceive(){if (clientSocket == null) return;clientSocket.BeginReceive(readBuff,0,readBuff.Length,SocketFlags.None,ReceiveCallback,clientSocket);}//接收数据异步回调private void ReceiveCallback(IAsyncResult ar){try{var socket = (Socket)ar.AsyncState;int count = socket.EndReceive(ar);var recStr = Encoding.UTF8.GetString(readBuff, 0, count);msgList.Enqueue(recStr);clientSocket.BeginReceive(readBuff,0,readBuff.Length,SocketFlags.None,ReceiveCallback,clientSocket);}catch (SocketException ex){Debug.LogError($"接收数据失败:{ex}");}}//关闭Socketpublic void OnClose(){if (clientSocket == null) return;clientSocket.Shutdown(SocketShutdown.Both);clientSocket.Close();}
}
服务端
using System.Text;
using System.Net;
using System.Net.Sockets;public class EchoServer
{private Socket serverSocket;private class ClientInfo{public Socket clientSocket;public byte[] readBuff;public ClientInfo(Socket socket){clientSocket = socket; readBuff = new byte[1024];}}private Dictionary<Socket, ClientInfo> clientList;public void Start(){if (serverSocket == null){serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);}if (clientList == null){clientList = new Dictionary<Socket, ClientInfo>();}IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);//绑定服务器IP地址和端口号serverSocket.Bind(endPoint);//开启监听,等待客户端连接//参数backlog指定队列中最多可容纳等待接受的连接数,0表示不限制serverSocket.Listen(0);Console.WriteLine("服务器启动成功!");//成功接收一个客户端连接serverSocket.BeginAccept(AcceptCallback, serverSocket);Console.ReadLine(); Console.WriteLine("服务器关闭!");serverSocket.Close();}private void AcceptCallback(IAsyncResult ar){try{var socket = (Socket)ar.AsyncState;var client = socket.EndAccept(ar);var clientInfo = new ClientInfo(client);clientList.TryAdd(client, clientInfo); clientInfo.clientSocket.BeginReceive(clientInfo.readBuff, 0, clientInfo.readBuff.Length, SocketFlags.None, ReceiveCallback, clientInfo);socket.BeginAccept(AcceptCallback, socket);Console.WriteLine($"有一个客户端连入!在线人数:{clientList.Count}");}catch (SocketException ex){Console.WriteLine($"发送消息失败:{ex}");}}//发送数据异步回调private void SendCallback(IAsyncResult ar){try{var clientInfo = (ClientInfo)ar.AsyncState;int count = clientInfo.clientSocket.EndSend(ar);Console.WriteLine($"发送消息成功!");}catch (SocketException ex){Console.WriteLine($"发送消息失败:{ex}");}}//接收数据异步回调private void ReceiveCallback(IAsyncResult ar){try{var clientInfo = (ClientInfo)ar.AsyncState;int count = clientInfo.clientSocket.EndReceive(ar);//如果收到客户端关闭连接信号:“if(count)== 0”,断开连接if(count == 0){clientInfo.clientSocket.Close();clientList.Remove(clientInfo.clientSocket);Console.WriteLine($"客户端断开连接了 在线人数:{clientList.Count}");return;}var recStr = Encoding.UTF8.GetString(clientInfo.readBuff, 0, count);Console.WriteLine($"客户端发来消息:{recStr}");var sendBytes = Encoding.UTF8.GetBytes($"我收到了你的消息:{recStr}");clientInfo.clientSocket.BeginSend(sendBytes, 0, sendBytes.Length, SocketFlags.None, SendCallback, clientInfo);clientInfo.clientSocket.BeginReceive(clientInfo.readBuff, 0, clientInfo.readBuff.Length, SocketFlags.None, ReceiveCallback, clientInfo);}catch (SocketException ex){Console.WriteLine($"接收数据失败:{ex}");}}
}
到底了~
相关文章:
网络开发基础(游戏)之 Socket API
Socket简介 Socket (套接字)是网络编程的基础,在 C# 中通过 System.Net.Sockets 命名空间提供了一套完整的 API 来实现网络通信。 网络上的两个程序通过一个双向的通信连接实现数据交换, 这个连接的一端称为一个Socket。 一个Socket包含了进行网络通信必…...
在 Amazon Graviton 上运行大语言模型:CPU 推理性能实测与调优指南
引言 在生成式 AI 浪潮中,GPU 常被视为大模型推理的唯一选择。然而,随着 ARM 架构的崛起和量化技术的成熟,CPU 推理的性价比逐渐凸显。本文基于 Amazon Graviton 系列实例与 llama.cpp 工具链,实测了 Llama 3、DeepSeek 等模型的…...
基于尚硅谷FreeRTOS视频笔记——15—系统配制文件说明与数据规范
目录 配置函数 INCLUDE函数 config函数 数据类型 命名规范 函数与宏 配置函数 官网上可以查找 最核心的就是 config和INCLUDE INCLUDE函数 这些就是裁剪的函数 它们使用一个ifndef。如果定义了,就如果定义了这个宏定义,那么代码就生效。 通过ifn…...
Nacos 使用了什么日志框架?如何配置和查看日志?
Nacos 使用的日志框架 Nacos 主要使用 SLF4j (Simple Logging Facade for Java) 作为日志门面(API),并选择 Logback 作为其底层的日志实现。 SLF4j: 这是一个日志抽象层,允许开发者在代码中使用统一的接口进行日志记录ÿ…...
【基于Fluent+Python耦合的热管理数字孪生系统开发:新能源产品开发的硬核技术实践】
引言:热管理数字孪生的技术革命 在新能源领域(如动力电池、储能系统、光伏逆变器等),热管理是决定产品性能与安全的核心问题。传统热设计依赖实验与仿真割裂的流程,而数字孪生技术通过实时数据驱动与动态建模…...
【工具变量】A股上市公司信息披露质量KV指数测算数据集(含do代码 1991-2024年)
KV指数(Key Value Index)作为评估信息披露质量的关键指标,在证券市场,尤其是A股市场上市公司信息披露监管与评估中占据重要地位。该指数通过系统化、定量化的方法,对企业发布的信息进行全面剖析与打分,精准…...
【ELF2学习板】利用OpenMP采用多核并行技术提升FFTW的性能
目录 引言 OpenMP简介 编译OpenMP支持的FFTW库 部署与测试 测试程序 程序部署 测试结果 结语 引言 在前面已经介绍了在ELF2开发板上运行FFTW计算FFT。今天尝试利用RK3588的多核运算能力来加速FFT运算。FFTW利用多核能力可以考虑使用多线程或者OpenMP。今天介绍一下Ope…...
打靶日记 zico2: 1
一、探测靶机IP(进行信息收集) 主机发现 arp-scan -lnmap -sS -sV -T5 -p- 192.168.10.20 -A二、进行目录枚举 发现dbadmin目录下有个test_db.php 进入后发现是一个登录界面,尝试弱口令,结果是admin,一试就出 得到加…...
【技术派后端篇】 Redis 实现用户活跃度排行榜
在各类互联网应用中,排行榜是一个常见的功能需求,它能够直观地展示用户的表现或贡献情况,提升用户的参与感和竞争意识。在技术派项目中,也引入了用户活跃度排行榜,该排行榜主要基于 Redis 的 ZSET 数据结构来实现。接下…...
MySql Innodb详细解读
参考文档:https://www.cnblogs.com/acatsmiling/p/18424729 一、数据存储:从磁盘到内存的"黑帮走私" 1. 物理结构:表空间与页的江湖规矩 表空间(Tablespace): 所有InnoDB数据存在表空间里&…...
每日两道leetcode
399. 除法求值 - 力扣(LeetCode) 题目 给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。 …...
在RK3588上使用哪个流媒体服务器合适
在RK3588平台上选择合适的流媒体服务器时,需考虑其ARM Cortex-A76/A55架构、硬件编解码能力(如支持H.264/H.265/AV1解码)以及Linux/Android系统支持。以下是推荐的方案: 1. 轻量级方案:GStreamer RTSP 适用场景&…...
分享一个DeepSeek+自建知识库实现人工智能,智能回答高级用法。
这个是我自己搞的DeepSeek大模型自建知识库相结合到一起实现了更强大的回答问题能力还有智能资源推荐等功能。如果感兴趣的小伙伴可以联系进行聊聊,这个成品已经有了实现了,所以可以融入到你的项目,或者毕设什么的还可以去参加比赛等等。 1.项…...
PyTorch 深度学习实战(38):注意力机制全面解析(从Seq2Seq到Transformer)
在上一篇文章中,我们探讨了分布式训练实战。本文将深入解析注意力机制的完整发展历程,从最初的Seq2Seq模型到革命性的Transformer架构。我们将使用PyTorch实现2个关键阶段的注意力机制变体,并在机器翻译任务上进行对比实验。 一、注意力机制演…...
Android Studio 获取配置资源与第三方包信息详解
文章目录 Android Studio 获取配置资源与第三方包信息详解一、获取资源文件中的配置1. 获取颜色值Java 中获取:Kotlin 中获取: 2. 获取字符串Java 中获取:Kotlin 中获取: 3. 获取尺寸值Java 中获取:Kotlin 中获取&…...
【网络初识】从零开始彻底了解网络编程(一)
本篇博客给大家带来的是网络的知识点. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅🚀 要开心要快乐顺便进步 一. 网络…...
Vivado比特流生成、下载及板级验证操作步骤
1. 前期准备 安装Vivado软件:确保Vivado开发环境已正确安装并配置。创建工程: 打开Vivado,点击“Create Project”新建工程。设置工程名称(例如“led_flow”)和路径。选择目标FPGA型号(例如XC7A35TFFG484&…...
【Flutter DevTools】性能优化的瑞士军刀
一、性能分析:帧率与资源监控 1.1 帧率监控(Performance面板) 通过Performance面板可实时捕获应用的渲染流水线数据。开发者点击"Record"按钮后,DevTools会以时间轴形式展示每一帧的构建、布局、绘制耗时。当帧率低于…...
使用Redis实现实时排行榜
为了实现一个实时排行榜系统,我们可以使用Redis的有序集合(ZSet),其底层通常是使用跳跃表实现的。有序集合允许我们按照分数(score)对成员(member)进行排序,因此非常适合…...
HTML5 应用程序缓存:原理、实践与演进
在 Web 技术的发展历程中,HTML5 引入的应用程序缓存(Application Cache)曾是提升 Web 应用离线体验的重要技术。它允许 Web 应用进行缓存,使用户在没有因特网连接时也能访问应用,为 Web 应用带来了显著的优势。然而&am…...
Compose笔记(十七)--AsyncImage
这一节了解一下Compose中的AsyncImage的使用,AsyncImage是由 Coil库提供的一个用于异步加载图片的组件。它支持加载网络图片、本地图片资源,并提供了占位符、错误处理、过渡动画等功能,简单介绍如下: API 1. model 含义:指定要加…...
Python语法系列博客 · 第7期[特殊字符] 列表推导式与字典推导式:更优雅地处理数据结构
上一期小练习解答(第6期回顾) ✅ 练习1:统计文件行数 with open("data.txt", "r", encoding"utf-8") as f:lines f.readlines()print(f"总行数:{len(lines)}")✅ 练习2:反…...
Redis--主从复制
目录 一、配置 1.1 建立复制 1.2 断开复制 1.3 安全性 1.4 只读 1.5 传输延迟 二、拓扑 2.1 一主一从结构 2.2 一主多从结构 2.3 树形主从结构 在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他服务器,满足故障恢 复和负载均衡等需求…...
FPGA练习———DDS波形发生器
简介:使用DDS波形发生器可以在fpga上生成方波、正弦波等波形,其具体方法是计算相位的变化,然后根据数据表的数值进行数模转化改变波形。 DDS的第一步是生成一个相位加法器 相位加法器 在生成一个波,例如正弦波时,我们…...
力扣面试150题-- 存在重复元素 II和最长连续序列
Day 26 题目描述 思路 定义一个map用来存放每个元素以及它对应的序号从前向后遍历数组如果该元素存在于map(说明满足了重复元素的条件),用当前元素的序号值减去map中存放的序号值(因为是从前遍历的所以当前元素序号一定大于存放…...
卸载Anaconda并保留虚拟环境,重装Anaconda并还原之前的虚拟环境
参考 https://blog.csdn.net/qq_63611690/article/details/134560333 该博文是虚拟环境和Anaconda安装路径在一起 我的是虚拟环境早就搞到了别的盘 问题描述 我之前把Anaconda安装到了C盘,随之时间推移,C盘占用空间越来越大。我想把Anaconda卸载重装…...
ArcGIS及其组件抛出 -- “Sorry, this application cannot run under a Virtual Machine.“
产生背景: 使用的是“破解版本”或“被套壳过”的非官方 ArcGIS 版本 破解版本作者为了防止: 被研究破解方式 被自动化抓包/提权/逆向 被企业环境中部署多机使用 通常会加入**“虚拟化环境检测阻断运行”机制** 原因解释: 说明你当前运…...
Ubuntu 25.04 “Plucky Puffin” 正式发布
Ubuntu 25.04 “Plucky Puffin” 于 2025 年 4 月 17 日正式发布。这是一个短期支持版本,只支持到 2026 年 1 月1。以下是该版本的一些主要新变化: 内核与系统:采用 Linux 6.14 内核;systemd v257.4 带来重要上游更新,…...
2. ubuntu20.04 和VS Code实现 ros的输出 (C++,Python)
本节对应赵虚左ROS书籍的1.4.2 1)创建工作空间 mkdir -p catkin_ws/src cd catkin_ws catkin_make 2) 终端进入VS Code code . 3) vscoe 的基本配置 3.1)修改.vscode/tasks.json ,修改内容如下: { // 有关 tasks.json 格式的文档,请参见…...
0801ajax_mock-网络ajax请求1-react-仿低代码平台项目
0 vite配置proxy代理 vite.config.ts代码如下图所示: import { defineConfig } from "vite"; import react from "vitejs/plugin-react";// https://vite.dev/config/ export default defineConfig({plugins: [react()],server: {proxy: {&qu…...
前端vue+后端ssm项目
下载地址: 前端:https://download.csdn.net/download/2401_83418369/90649449 后端: https://download.csdn.net/download/2401_83418369/90649441 一、项目基础环境搭建 1、新建Maven项目 2、创建目录,结构如下: …...
Python实例题:Python获取阴阳师壁纸
目录 Python实例题 题目 实现思路 代码实现 代码解释 get_wallpaper_links 函数: download_wallpapers 函数: 主程序: 运行思路 注意事项 Python实例题 题目 Python获取阴阳师壁纸 实现思路 发送请求获取网页内容:使…...
考研408操作系统文件管理——4.2目录系统详解
考研408操作系统文件管理——目录系统详解 一、目录管理基本概念 1.1 目录的核心功能 目录是文件系统的核心管理组件,主要实现: 按名存取:通过文件名快速定位物理地址路径解析:将逻辑路径转换为物理块地址共享控制:支持多用户共享同一文件命名空间管理:维护全局唯一的…...
国产SMT贴片机自主技术突破解析
内容概要 随着电子信息产业对精密制造需求的持续升级,国产SMT贴片机的技术突破已成为装备自主化进程的关键节点。本文聚焦设备研发的三大核心领域:高动态运动控制系统通过线性电机与数字信号处理技术的融合,将重复定位精度提升至5μm级别&am…...
Ai Agent 在生活领域的深度应用与使用指南
在科技不断革新的时代,Ai Agent 正以前所未有的态势融入生活的各个角落,成为提升生活品质与效率的得力助手。它凭借强大的智能处理能力,解决了传统生活模式中的诸多痛点,在家庭、出行、健康管理等多个场景中展现出巨大的应用价值…...
CPU与GPU之间的交互
命令队列和命令列表 每个GPU都维护着一个命令队列,本质上是一个环形缓冲区,等待着cpu提交到gpu的命令,同时执行命令 在Direct3D中命令队列被抽象为ID3D12CommandQueue接口来表示。通过下面的方式创建命令队列。 ComPtr<ID3D12CommandQue…...
MySQL运维三部曲初级篇:从零开始打造稳定高效的数据库环境
文章目录 一、服务器选型——给数据库一个舒适的家二、系统调优——打造高性能跑道三、MySQL配置——让数据库火力全开四、监控体系——数据库的体检中心五、备份恢复——数据安全的最后防线六、主从复制——数据同步的艺术七、安全加固——守护数据长城 引言:从小白…...
Python制作简易PDF查看工具PDFViewerV1.0查找功能优化
原文说明 为不破坏原文结构,因此功能优化不在原文中维护了。关于这款工具原文请通过下面链接访问。Python制作简易PDF查看工具PDFViewerV1.0 这款小工具基本功能已经可以作为一款文档浏览器使用,但还有一些美中不足的地方,本文将介绍对文本查…...
MOPSO实现无人机多目标路径规划(Matlab完整源码和数据)
一、MOPSO算法核心原理 MOPSO(多目标粒子群优化算法)通过模拟鸟群觅食行为,在搜索空间中寻找满足多个冲突目标的Pareto最优解集。其核心流程包括: 粒子初始化:随机生成粒子群,每个粒子代表一条候选路径&a…...
Python:使用web框架Flask搭建网站
Date: 2025.04.19 20:30:43 author: lijianzhan Flask 是一个轻量级的 Python Web 开发框架,以简洁灵活著称,适合快速构建中小型 Web 应用或 API 服务。以下是 Flask 的核心概念、使用方法和实践指南 Flask 的核心特点: 轻量级 核心代码仅约…...
芝法酱躺平攻略(21)——kafka安装和使用
本节内容比较初级,故接着躺平攻略写 一、官网的下载 1.1 下载解压 首先,去官网下载jar包,放进linux中,解压到对应位置。 我的位置放在/WORK/MIDDLEWARE/kafka/4.0 1.2 常见配置 # 每个topic默认的分片数 num.properties4 # 数…...
C语言知识复习资料
## 第一章 C语言基本知识 ### 【考点1】C程序 - 用C语言编写的程序称为C语言源程序,源程序文件后缀名为".c" - 源程序经编译后生成后缀名为".obj"的目标文件 - 再把目标文件与各种库函数连接起来,生成后缀名为".exe"可执行文件 - C语言有三…...
CMFA在自动驾驶中的应用案例
CMFA在自动驾驶中的典型应用案例 CMFA(Cross-Modal Feature Alignment)方法在自动驾驶领域有多个成功的应用场景,以下是几个典型案例: 1. 多模态3D目标检测 应用场景:车辆、行人、骑行者等交通参与者的精确检测 …...
进程控制(下)【Linux操作系统】
文章目录 进程程序替换进程替换有关函数和指令函数:execl函数:execv函数:execlp函数:execvp函数:execvpe 进程替换的原理为什么进程替换时,原进程的环境变量不会被覆盖? 进程替换具体会造成什么…...
【后端】【python】Python 爬虫常用的框架解析
一、总结 Python 爬虫常用的框架主要分为 三类: 轻量级请求库:如 requests、httpx,用于快速发请求。解析与处理库:如 BeautifulSoup、lxml、pyquery。爬虫框架系统:如 Scrapy、pyspider、Selenium、Playwright 等&am…...
JDBC 数据库连接全解析:从驱动配置到工具类封装
目录 一. 将MySQL对应版本的jar包放入Java项目中 1. 准备工作 2. 复制到Java项目 二. 获取数据库连接 1. 连接Mysql数据库的URL 2. 连接数据库的用户名 3. 连接数据库的密码 4. 通过反射实例化 三. Properties文件用法 1. properties文件介绍 2. Properties工具类 a.…...
【图片识别分类】如何快速识别照片中的水印文字,对图片进行关键字分类,快速整理水印相机拍摄图片,基于WPF和腾讯OCR的技术实现
项目背景 在施工现场,施工人员通常会使用水印相机拍摄照片,这些照片带有时间、地点、施工阶段等水印信息。为了便于管理和归档,需要快速识别照片中的水印文字,并根据关键字对照片进行分类和整理。 界面设计 界面设计简洁直观&…...
第32讲:卫星遥感与深度学习融合 —— 让地球“读懂”算法的语言
目录 🔍 一、讲讲“遥感+深度学习”到底是干啥的? ✅ 能解决什么问题? 🧠 二、基础原理串讲:深度学习如何“看懂”遥感图? 🛰 遥感图像数据类型: 🧠 CNN的基本思路: 🧪 三、实战案例:用CNN对遥感图像做地类分类 📦 所需R包: 🗂️ 步骤一:构建训…...
Java 静态变量、静态方法及工具类介绍
目录 一、静态变量(Static Variables) 1. 基本概念 2. 核心特性 (1)类级别共享 (2)生命周期 (3)内存分配 3. 使用方法 (1)访问方式 (2)初始化时机 4. 典型应用场景 (1)共享常量 (2)计数器功能 (3)配置信息 二、静态方法(Static …...
【win 1】win 右键菜单添加 idea pycharm vscode trae 打开文件夹
编程时经常需要通过 程序 打开文件夹,有时安装时没注意选上添加到右键菜单,又不想重新安装,有什么方法? 之前教程都是改注册表有点繁琐,这里利用开源的 windows 右键管理软件,可以快捷简单的添加。 右键菜…...