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

网络开发基础(游戏)之 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) 和 AddressFamily.InterNetwork address 系列。

Dgram:支持数据报,即最大长度固定(通常很小)的无连接、不可靠消息。 消息可能会丢失或重复并可能在到达时不按顺序排列。 Socket 类型的 Dgram 在发送和接收数据之前不需要任何连接,并且可以与多个对方主机进行通信。 Dgram 使用 Datagram 协议 (Udp) 和 AddressFamily.InterNetwork address 系列。

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}");}}
}

到底了~

相关文章:

  • REC: 引爆全球万亿级市场!Web3+消费革命重塑全球-东南亚-跨境商业未来
  • [HCIP] OSPF 综合实验
  • 高速系统设计简介
  • 背包 DP 详解
  • PyTorch 深度学习实战(38):注意力机制全面解析(从Seq2Seq到Transformer)
  • 将 DeepSeek 集成到 Spring Boot 项目实现通过 AI 对话方式操作后台数据
  • 为什么 waitress 不支持 WebSocket?
  • python文件类操作:json/ini配置文件、logging日志统计、excel表格数据读写、os操作库
  • 多模态融合(十一): SwinFusion——武汉大学马佳义团队(二)
  • Java中包装类和泛型
  • 导出excel文件并在页面自动下载
  • TCP/IP、UDP、HTTP、HTTPS、WebSocket 一文讲解
  • 从零开始搭建CLIP模型实现基于文本的图像检索
  • elementUI中MessageBox.confirm()默认不聚焦问题处理
  • UML-共享汽车系统通信图深度解析
  • 蓝桥杯练习题2
  • Codeforces Educational Round 177 Div. 2 【B题,C待补
  • Unity:获取组件对象(GetComponent<T>())
  • MinnowBoard MAX单板UEFI BIOS代码编译教程
  • Spring 学习笔记之 @Transactional详解
  • 深一度|中国花样滑冰因何大滑坡
  • 竹笋食用不当,小心“鲜”变“险”
  • 王东杰:重审康有为的“大同世界”
  • 经济参考报:安全是汽车智能化的终极目标
  • 特朗普就防卫负担施压日本,石破茂:防卫费应由我们自主决定
  • “云南舞蹈大家跳”暨牟定“三月会”下周举行,城际公交免票