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

2025-05-07 Unity 网络基础7——TCP异步通信

文章目录

  • 1 同步与异步
  • 2 常用异步方法
    • 2.1 Beign / End 方法
      • BeginConnect() / EndConnect()
      • BeginAccept() / EndAccept()
      • BeginSend() / EndSend()
      • BeginReceive() / EndReceive()
    • 2.2 Async 方法
      • ConnectAsync()
      • AcceptAsync()
      • SendAsync()
      • ReceiveAsync()
  • 3 实战
    • 3.1 服务端配置
      • 3.1.1 ClientSocket.cs
      • 3.1.2 ServerSocket.cs
      • 3.1.3 Program.cs
    • 3.2 客户端配置
      • 3.2.1 NetAsyncMgr.cs
      • 3.2.2 MainAsync.cs
      • 3.2.3 Lesson13.cs
    • 3.3 代码
  • 4 测试

1 同步与异步

  • 同步方法

    方法中逻辑执行完毕后,再继续执行后面的方法。

  • 异步方法

    方法中逻辑可能还没有执行完毕,就继续执行后面的内容。

    往往异步方法当中都会使用多线程执行某部分逻辑,因为不需要等待方法中逻辑执行完毕就可以继续执行下面的逻辑。

注意

​ Unity 中协同程序中的某些异步方法,有的使用的是多线程,有的使用的是迭代器分步执行。

2 常用异步方法

2.1 Beign / End 方法

IAsyncResult

​ IAsyncResult 接口由包含可异步作的方法的类实现,是启动异步作的方法的返回类型。

​ 当异步调用完成时,将向 WaitHandle 发出信号,可以通过调用 WaitOne 方法来等待它。

image-20250507101504475
  • AsyncState:调用异步方法时传入的参数,需要转换为传入的参数类型。
  • AsyncWaitHandle:用于同步等待。
  • CompletedSynchronously:异步操作是否同步完成。
  • IsCompleted:异步操作是否完成。

BeginConnect() / EndConnect()

BeginConnect()

​ 当调用BeginConnect方法时,该方法立即返回一个IAsyncResult对象,而不会等待连接完成。调用者可以继续执行其他操作,直到连接操作完成,然后通过EndConnect方法获取连接结果。

image-20250507101338850
  • remoteEP:表示远程主机的终结点(EndPoint)。
  • callback:表示异步操作完成时要调用的回调方法。
  • state:表示与异步操作关联的状态对象,可以是任何对象(通常传递 Socket 实例)。
  • address:表示远程主机的 IP 地址。
  • port:表示远程主机的端口号。
  • requestCallback:表示异步操作完成时要调用的回调方法。
  • host:表示远程主机的域名或 IP 地址。

EndConnect()

​ 通常与BeginConnect方法成对使用。

image-20250507102508695
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){var socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socketTcp.BeginAccept(AcceptCallBack, socketTcp);var ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);socketTcp.BeginConnect(ipPoint, result =>{Socket s = result.AsyncState as Socket;try{s.EndConnect(result);print("连接成功!");}catch (SocketException e){print("连接出错:" + e.SocketErrorCode + e.Message);}}, socketTcp);}private void AcceptCallBack(IAsyncResult result){...}
}

BeginAccept() / EndAccept()

BeginAccept()

​ 异步开始接受传入的连接请求,避免阻塞主线程。需传入回调函数和状态对象(通常是原始 Socket 实例)。

image-20250507101413415
  • callback:连接建立后的回调函数。
  • state:状态对象(通常是原始 Socket 实例)。
  • receiveSize:缓冲区大小。
  • acceptSocket:接受套接字。

EndAccept()

​ 在回调函数中调用,用于完成异步连接操作并返回新的客户端 Socket。

​ 会返回一个新的 Socket 实例,用于与客户端通信。

image-20250507101438925
  • buffer:输出参数,用于接收客户端发送的数据。这个缓冲区的大小应该足够大,以容纳客户端发送的数据。
  • asyncResultIAsyncResult对象,表示异步操作的状态。
  • bytesTransferred:输出参数,用于返回实际传输的字节数。
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){var socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);...socketTcp.BeginAccept(AcceptCallBack, socketTcp);}private void AcceptCallBack(IAsyncResult result){try{// 获取传入的参数Socket s = result.AsyncState as Socket;// 通过调用 EndAccept 得到连入的客户端 SocketSocket clientSocket = s.EndAccept(result);// do something with clientSocket// ...}catch (SocketException e){print(e.SocketErrorCode);}}
}

BeginSend() / EndSend()

BeginSend()

image-20250507104222504
  • buffer:接收数据的缓冲区。
  • offset:缓冲区中开始接收数据的偏移量。
  • size:要接收的最大字节数。
  • socketFlags:控制接收操作的标志。
  • errorCode:输出参数,表示操作的结果。
  • callback:异步操作完成时调用的回调方法。
  • state:与异步操作关联的用户状态对象。

EndSend()

image-20250507104457578
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){...var bytes = Encoding.UTF8.GetBytes("1231231231223123123");socketTcp.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, (result) =>{try{socketTcp.EndSend(result);print("发送成功");}catch (SocketException e){print("发送错误" + e.SocketErrorCode + e.Message);}}, socketTcp);}
}

BeginReceive() / EndReceive()

BeginReceive()

image-20250507103211002

EndReceive()

image-20250507103659234
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){...socketTcp.BeginReceive(_resultBytes, 0, _resultBytes.Length, SocketFlags.None, ReceiveCallBack, socketTcp);}private void ReceiveCallBack(IAsyncResult result){try{Socket s = result.AsyncState as Socket;// 这个返回值是你受到了多少个字节int num = s.EndReceive(result);// 进行消息处理Encoding.UTF8.GetString(_resultBytes, 0, num);// 我还要继续接受s.BeginReceive(_resultBytes, 0, _resultBytes.Length, SocketFlags.None, ReceiveCallBack, s);}catch (SocketException e){print("接受消息处问题" + e.SocketErrorCode + e.Message);}}
}

2.2 Async 方法

​ Async 方法相比于 Begin / End 方法,参数相对少一些。主要参数配置集中在 SocketAsyncEventArgs 中,该类封装了异步操作所需的参数和结果。

ConnectAsync()

image-20250507105600083
  • SocketTypeProtocolType参数用于指定 Socket 的类型和协议。
  • SocketAsyncEventArgs对象e包含了连接所需的参数。
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){...SocketAsyncEventArgs e2 = new SocketAsyncEventArgs();e2.Completed += (socket, args) =>{if (args.SocketError == SocketError.Success){//连接成功}else{//连接失败print(args.SocketError);}};socketTcp.ConnectAsync(e2);}
}

AcceptAsync()

image-20250507105022814
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){...var e = new SocketAsyncEventArgs();e.Completed += (socket, args) =>{//首先判断是否成功if (args.SocketError == SocketError.Success){//获取连入的客户端socketSocket clientSocket = args.AcceptSocket;(socket as Socket).AcceptAsync(args);}else{print("连入客户端失败" + args.SocketError);}};socketTcp.AcceptAsync(e);}
}

SendAsync()

​ 发送前需要调用 SocketAsyncEventArgsSetBuffer() 方法设置发送数组。

image-20250507105917639
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){...var e3     = new SocketAsyncEventArgs();var bytes2 = Encoding.UTF8.GetBytes("123123的就是拉法基萨克两地分居");e3.SetBuffer(bytes2, 0, bytes2.Length);e3.Completed += (socket, args) =>{if (args.SocketError == SocketError.Success){print("发送成功");}else{ }};socketTcp.SendAsync(e3);}
}

ReceiveAsync()

​ 接收也需要调用 SocketAsyncEventArgsSetBuffer() 方法设置接收到哪个数组中。

image-20250507110326391
public class Lesson12 : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){...var e4 = new SocketAsyncEventArgs();// 设置接受数据的容器,偏移位置,容量e4.SetBuffer(new byte[1024 * 1024], 0, 1024 * 1024);e4.Completed += (socket, args) =>{if (args.SocketError == SocketError.Success){// 收取存储在容器当中的字节// Buffer 是容器// BytesTransferred 是收取了多少个字节Encoding.UTF8.GetString(args.Buffer, 0, args.BytesTransferred);// 接收完消息 再接收下一条args.SetBuffer(0, args.Buffer.Length);(socket as Socket).ReceiveAsync(args);}else{ }};socketTcp.ReceiveAsync(e4);}
}

3 实战

3.1 服务端配置

​ 依次创建如下 3 个脚本:

  • ClientSocket.cs
  • ServerSocket.cs
  • Program.cs

3.1.1 ClientSocket.cs

  1. 异步连接管理:自动为每个客户端连接分配唯一 ID,并立即开始异步接收消息。
  2. 异步消息发送:提供 Send() 方法异步发送字符串消息到服务器。
  3. 异步消息接收:通过回调机制持续接收服务器发送的消息。
  4. 错误处理:捕获并处理套接字操作中的异常。

1)成员变量

private static int _BeginId = 1;  // 静态ID计数器
public Socket Client { get; private set; }  // 底层Socket对象
public int Id { get; set; }  // 客户端唯一标识
private byte[] _buffer = new byte[1024];  // 接收缓冲区
private int _bufferOffset = 0;  // 缓冲区偏移量

2)构造函数

​ 构造函数接收一个已连接的 Socket 对象,为其分配 ID,并立即开始异步接收数据。

public ClientSocket(Socket client)
{Client = client;Id = _BeginId++;  // 分配唯一ID// 立即开始异步接收消息Client.BeginReceive(_buffer, _bufferOffset, _buffer.Length - _bufferOffset, SocketFlags.None, ReceiveCallback, Client);
}

3)消息发送

Send()方法将字符串编码为 UTF-8 字节数组,然后异步发送。

public void Send(string message)
{try{var bytes = Encoding.UTF8.GetBytes(message);Client.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, Client);}catch (SocketException e){Console.WriteLine($"客户端 Id 发送消息时发生错误:e.Message");}
}

4)发送回调

​ 发送完成后调用此回调,处理发送结果。

private void SendCallback(IAsyncResult ar)
{try{var client = (Socket) ar.AsyncState!;client.EndSend(ar);  // 完成异步发送}catch (SocketException e){Console.WriteLine($"客户端 Id 发送消息时发生错误:e.Message");}
}

5)接收回调

​ 这是核心的接收逻辑,处理接收到的数据并保持持续接收状态。

private void ReceiveCallback(IAsyncResult ar)
{try{var client = (Socket) ar.AsyncState!;_bufferOffset = client.EndReceive(ar);  // 完成接收// 处理消息var message = Encoding.UTF8.GetString(_buffer, 0, _bufferOffset);Console.WriteLine($"收到客户端 {Id} 的消息:{message}");_bufferOffset = 0;  // 重置缓冲区if (client.Connected){// 继续接收下一条消息client.BeginReceive(_buffer, _bufferOffset, _buffer.Length - _bufferOffset, SocketFlags.None, ReceiveCallback, client);}else{Console.WriteLine($"客户端 {Id} 断开连接!");}}catch (SocketException e){Console.WriteLine($"客户端 {Id} 接收消息时发生错误:{e.Message}");}
}

3.1.2 ServerSocket.cs

  1. 服务器管理:创建并管理 TCP 服务器套接字,监听指定 IP 和端口
  2. 客户端连接处理:异步接受客户端连接,为每个连接创建ClientSocket实例
  3. 消息广播:向所有已连接客户端发送消息
  4. 客户端管理:使用字典存储所有连接的客户端套接字

1)成员变量

public Socket Server { get; private set; }  // 服务器Socket对象
private Dictionary<int, ClientSocket> _clientSockets = new();  // 存储所有客户端连接

2)启动服务器

Start()方法初始化服务器 Socket,绑定到指定 IP 和端口,并开始监听连接请求。

public void Start(string ip, int port, int num)
{Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);var ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);Server.Bind(ipPoint);Server.Listen(num);  // 设置最大挂起连接数Server.BeginAccept(AcceptCallback, Server);  // 开始异步接受连接
}

3)接受客户端连接

​ 这是核心的异步连接处理逻辑,使用回调机制处理新连接。

private void AcceptCallback(IAsyncResult ar)
{var server = (Socket) ar.AsyncState!;var client = server.EndAccept(ar);  // 完成异步接受var clientSocket = new ClientSocket(client);  // 创建客户端Socket包装_clientSockets.Add(clientSocket.Id, clientSocket);  // 存储客户端Console.WriteLine($"客户端 {clientSocket.Id} 连接成功!");server.BeginAccept(AcceptCallback, server);  // 继续接受新连接
}

4)消息广播

​ 该方法遍历所有已连接客户端,发送相同消息。

public void BroadCast(string msg)
{foreach (var clientSocket in _clientSockets.Values){clientSocket.Send(msg);  // 向每个客户端发送消息}
}

3.1.3 Program.cs

  1. 服务器启动:初始化并启动 TCP 服务器。
  2. 交互式控制:通过控制台输入管理服务器。
  3. 消息广播:向所有连接客户端发送消息。
  4. 退出机制:提供优雅关闭服务器的选项。

1)服务器初始化

  • 创建ServerSocket实例。
  • 启动服务器监听本地回环地址(127.0.0.1)的 8080 端口。
  • 设置最大挂起连接数为 10。
  • 输出启动信息。
var serverSocket = new ServerSocket();
serverSocket.Start("127.0.0.1", 8080, 10);
Console.WriteLine("服务器已启动,等待客户端连接...");

2)主控制循环

​ 这是一个无限循环,等待控制台输入并处理命令:

  1. exit 命令:退出循环,结束程序。
  2. B:前缀消息:广播消息给所有客户端(去除"B:"前缀)。
  3. 其他输入:当前版本忽略。
while (true)
{var input = Console.ReadLine();if (input == "exit"){break;}else if (input?[..2] == "B:"){serverSocket.BroadCast(input[2..]);}
}

3)使用说明

  1. 启动服务器:运行程序即启动服务器。
  2. 广播消息:输入"B:消息内容"广播给所有客户端。
  3. 退出服务器:输入"exit"关闭服务器。

3.2 客户端配置

​ 依次创建如下 3 个脚本:

  • NetAsyncMgr.cs
  • MainAsync.cs
  • Lesson13.cs

3.2.1 NetAsyncMgr.cs

​ 实现 Unity 异步 Socket 网络管理器,用于处理与服务器的 TCP 连接、消息发送和接收。

  1. 单例模式:通过Instance属性确保全局唯一访问点。
  2. 异步连接:使用ConnectAsync实现非阻塞连接。
  3. 消息收发:支持异步发送和接收 UTF-8 编码的字符串消息。
  4. 连接管理:提供连接状态检查和关闭方法。

1)初始化与连接

​ 使用SocketAsyncEventArgs实现异步连接,连接完成后触发ConnectCallback回调。

public void Connect(string ip, int port)
{var ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);var args = new SocketAsyncEventArgs();args.RemoteEndPoint = ipPoint;args.Completed += ConnectCallback;_socket.ConnectAsync(args);
}

2)消息发送

​ 将字符串编码为 UTF-8 字节数组后异步发送,发送完成后触发SendCallback

public void Send(string msg)
{var bytes = Encoding.UTF8.GetBytes(msg);var args = new SocketAsyncEventArgs();args.SetBuffer(bytes, 0, bytes.Length);args.Completed += SendCallback;_socket.SendAsync(args);
}

3)消息接收

​ 使用循环接收模式,每次接收完成后立即开始下一次接收。

private void ReceiveCallback(object sender, SocketAsyncEventArgs e)
{print(Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred));socket.ReceiveAsync(e); // 继续接收下一条消息
}

3.2.2 MainAsync.cs

​ MainAsync.cs 用于初始化并连接网络管理器 NetAsyncMgr。

  1. 网络管理器初始化:确保NetAsyncMgr单例实例存在。
  2. 自动连接服务器:启动时自动连接本地 8080 端口。

1)网络管理器初始化

​ 这段代码实现了"懒加载"模式:

  • 检查NetAsyncMgr单例是否存在。
  • 如不存在则创建新的 GameObject 并附加NetAsyncMgr组件。
if (NetAsyncMgr.Instance == null)
{var go = new GameObject("NetAsyncMgr");go.AddComponent<NetAsyncMgr>();
}

2)服务器连接

​ 使用单例实例连接本地服务器 (127.0.0.1) 的 8080 端口。

NetAsyncMgr.Instance.Connect("127.0.0.1", 8080);

3.2.3 Lesson13.cs

  1. UI 组件绑定
    • BtnSend:发送按钮。
    • TMP_InputField:文本输入框(使用 TextMeshPro 实现)。
  2. 消息发送逻辑
    • Start()中注册按钮点击事件。
    • 点击按钮时调用NetAsyncMgr.Instance.Send()发送输入框内容。

3.3 代码

服务端

// ------------------------------------------------------------
// @file       ClientSocket.cs
// ------------------------------------------------------------namespace NetLearningTcpServerAsync;using System.Net.Sockets;
using System.Text;public class ClientSocket
{private static int _BeginId = 1;public Socket Client { get; private set; }public int Id { get; set; }private byte[] _buffer       = new byte[1024];private int    _bufferOffset = 0;public ClientSocket(Socket client){Client = client;Id     = _BeginId++;// 开始收消息Client.BeginReceive(_buffer, _bufferOffset, _buffer.Length - _bufferOffset, SocketFlags.None, ReceiveCallback, Client);}public void Send(string message){try{var bytes = Encoding.UTF8.GetBytes(message);Client.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, Client);}catch (SocketException e){Console.WriteLine($"客户端 Id 发送消息时发生错误:e.Message");}}private void SendCallback(IAsyncResult ar){try{var client = (Socket) ar.AsyncState!;client.EndSend(ar);}catch (SocketException e){Console.WriteLine($"客户端 Id 发送消息时发生错误:e.Message");}}private void ReceiveCallback(IAsyncResult ar){try{var client = (Socket) ar.AsyncState!;_bufferOffset = client.EndReceive(ar);// 处理收到的消息var message = Encoding.UTF8.GetString(_buffer, 0, _bufferOffset);Console.WriteLine($"收到客户端 {Id} 的消息:{message}");// 清空缓冲区_bufferOffset = 0;if (client.Connected){// 继续接收消息client.BeginReceive(_buffer, _bufferOffset, _buffer.Length - _bufferOffset, SocketFlags.None, ReceiveCallback, client);}else{Console.WriteLine($"客户端 {Id} 断开连接!");}}catch (SocketException e){Console.WriteLine($"客户端 {Id} 接收消息时发生错误:{e.Message}");}}
}
// ------------------------------------------------------------
// @file       ServerSocket.cs
// ------------------------------------------------------------namespace NetLearningTcpServerAsync;using System.Net;
using System.Net.Sockets;public class ServerSocket
{public Socket Server { get; private set; }private Dictionary<int, ClientSocket> _clientSockets = new();public void Start(string ip, int port, int num){Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);var ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);try{Server.Bind(ipPoint);Server.Listen(num);Server.BeginAccept(AcceptCallback, Server);}catch (SocketException e){Console.WriteLine(e);throw;}}public void BroadCast(string msg){foreach (var clientSocket in _clientSockets.Values){clientSocket.Send(msg);}}private void AcceptCallback(IAsyncResult ar){try{var server = (Socket) ar.AsyncState!;var client = server.EndAccept(ar);var clientSocket = new ClientSocket(client);_clientSockets.Add(clientSocket.Id, clientSocket);Console.WriteLine($"客户端 {clientSocket.Id} 连接成功!");// 继续让别的客户端连入server.BeginAccept(AcceptCallback, server);}catch (SocketException e){Console.WriteLine(e);throw;}}
}
// ------------------------------------------------------------
// @file       Program.cs
// ------------------------------------------------------------using NetLearningTcpServerAsync;var serverSocket = new ServerSocket();
serverSocket.Start("127.0.0.1", 8080, 10);
Console.WriteLine("服务器已启动,等待客户端连接...");while (true)
{var input = Console.ReadLine();if (input == "exit"){break;}else if (input?[..2] == "B:"){serverSocket.BroadCast(input[2..]);}
}

客户端

// ------------------------------------------------------------
// @file       NetAsyncMgr.cs
// ------------------------------------------------------------using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;public class NetAsyncMgr : MonoBehaviour
{public static NetAsyncMgr Instance { get; private set; }private Socket _socket; // 连接服务器的 Socketprivate byte[] _buffer       = new byte[1024 * 1024];private int    _bufferOffset = 0;private void Awake(){if (Instance == null){Instance = this;}}public void Connect(string ip, int port){if (_socket != null && _socket.Connected){return;}var ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);var args = new SocketAsyncEventArgs();args.RemoteEndPoint =  ipPoint;args.Completed      += ConnectCallback;_socket.ConnectAsync(args);}public void Send(string msg){if (_socket == null || !_socket.Connected){return;}var bytes = Encoding.UTF8.GetBytes(msg);var args = new SocketAsyncEventArgs();args.SetBuffer(bytes, 0, bytes.Length);args.Completed += SendCallback;_socket.SendAsync(args);}public void Close(){if (_socket != null){_socket.Shutdown(SocketShutdown.Both);_socket.Disconnect(false);_socket.Close();_socket = null;}}private void SendCallback(object sender, SocketAsyncEventArgs e){if (e.SocketError == SocketError.Success){Debug.Log("Send Success");}else{Debug.Log("Send Failed: " + e.SocketError);Close();}}private void ConnectCallback(object sender, SocketAsyncEventArgs eventArgs){if (eventArgs.SocketError == SocketError.Success){Debug.Log("Connect Success");var socket = sender as Socket;if (socket == null || !socket.Connected){return;}// TODO: 连接成功后,开始接收数据var args2 = new SocketAsyncEventArgs();args2.SetBuffer(_buffer, 0, _buffer.Length);args2.Completed += ReceiveCallback;socket.ReceiveAsync(args2);}else{Debug.Log("Connect Failed: " + eventArgs.SocketError);}}private void ReceiveCallback(object sender, SocketAsyncEventArgs e){if (e.SocketError == SocketError.Success){print(Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred));// TODO: 处理接收到的数据var socket = sender as Socket;if (socket == null || !socket.Connected){return;}// 继续接收数据socket.ReceiveAsync(e);}else{print("接受消息出错:" + e.SocketError);Close();}}
}
// ------------------------------------------------------------
// @file       MainAsync.cs
// ------------------------------------------------------------using UnityEngine;public class MainAsync : MonoBehaviour
{// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){if (NetAsyncMgr.Instance == null){var go = new GameObject("NetAsyncMgr");go.AddComponent<NetAsyncMgr>();}NetAsyncMgr.Instance.Connect("127.0.0.1", 8080);}
}
// ------------------------------------------------------------
// @file       Lesson13.cs
// ------------------------------------------------------------using TMPro;
using UnityEngine;
using UnityEngine.UI;public class Lesson13 : MonoBehaviour
{public Button         BtnSend;public TMP_InputField TxtMessage;// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){BtnSend.onClick.AddListener(() =>{NetAsyncMgr.Instance.Send(TxtMessage.text);});}
}

4 测试

  1. Unity 中创建新场景,新建空物体 Main,将 MainAsync.cs 脚本挂载上去。
image-20250507194100660
  1. 创建 Canvas 画布,添加 InputField 和 Button 并关联至 Lesson13.cs。
image-20250507194236535
  1. 首先运行服务器。
image-20250507194349409
  1. 服务器启动后,运行 Unity。服务器显示连接成功。

    输入命令“B:Hello!”,回车。

image-20250507194613365
  1. Unity 中接收到消息并输出。
image-20250507194801832
  1. Unity 中在 InputField 中输入文字,点击发送。
image-20250507194851682
  1. 服务器中接收到了消息并打印输出。
image-20250507194918709

​ 到此,完成了服务器和客户端的双向通信。

相关文章:

  • 什么是声明式UI什么是命令式UI?鸿蒙ArkTS为什么是声明式UI-优雅草卓伊凡
  • 智算中心基础设施0-1建设全流程及投产后的运维
  • 融合静态图与动态智能:重构下一代智能系统架构
  • CPU-GPU-NPU-TPU 概念
  • 【HarmonyOS 5】鸿蒙Web组件和内嵌网页双向通信DEMO示例
  • Feign 重试策略调整:优化微服务通信的稳定性
  • PAT(最近)
  • 商汤科技前端面试题及参考答案
  • 如何避免项目结束后知识流失
  • 【基础知识】常见公式计算(三)
  • 【论文阅读】Harnessing the Power of LLM to Support Binary Taint Analysis
  • 汽车服务小程序功能点开发
  • 密码学基石:哈希、对称/非对称加密与HTTPS实践详解
  • 世界无人机大会将至,大势智慧以“AI+实景三维”赋能低空经济
  • TypeScript类型挑战-刷题
  • 适合java程序员的Kafka消息中间件实战
  • Kafka生产者send方法详解
  • phpstudy升级新版apache
  • 加速项目落地(Trae编辑器)
  • 《Overlapping Experiment Infrastructure: More, Better, Faster》论文阅读笔记
  • 社恐也能嗨起来,《孤独摇滚》千人观影齐舞荧光棒
  • 中国社科院:网文市场超430亿元,作者破3000万人
  • 云南多地突查公职人员违规饮酒:公安局门口开展酒精吹气测试
  • 乘客被地铁厕所门砸伤,南京地铁:突然坏的,已和乘客沟通处理
  • “降息潮”延续!存款利率全面迈向“1时代”
  • 美乌基金协议:美国搞了一套可在资源富集地区复刻的商业模式