Unity中的简易TCP服务器/客户端
在本文中,我将向你介绍一个在Unity中实现的简单TCP服务器脚本,和一个简单的客户端脚本.
脚本 MyTcpServer
允许Unity应用创建一个TCP服务器,监听客户端的连接、异步处理客户端消息,并通过事件与Unity应用中的其他模块进行通信。
MyTcpServer
类是使用 C# 的 TcpListener
实现的自定义 TCP 服务器,该服务器监听指定端口的传入 TCP 连接,并以异步、非阻塞的方式处理与客户端的通信.
AcceptClientsAsync
方法是一个异步方法,使用 TcpListener.AcceptTcpClientAsync()
来接受客户端的连接。每当一个新客户端连接时,服务器会将其添加到 connectedClients
列表中,并为该客户端创建一个新的任务 (Task.Run()
) 来处理客户端的消息。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using System.IO;
using System.Collections.Generic;
public class MyTcpServer
{
private static MyTcpServer instance;
private TcpListener tcpListener;
private CancellationTokenSource cts = new CancellationTokenSource();
private List<TcpClient> connectedClients = new List<TcpClient>(); // 管理客户端连接
public static bool isOnce = false;
public static event Action<string> sendEvent;
public static MyTcpServer Instance
{
get
{
if (instance == null)
{
instance = new MyTcpServer();
}
return instance;
}
}
public void StartServer()
{
try
{
var p = Path.Combine(Application.streamingAssetsPath, "MyPort.txt");
if (!File.Exists(p)) return;
var port = File.ReadAllText(p, Encoding.UTF8);
Debug.Log($"Starting server on port {port}");
tcpListener = new TcpListener(IPAddress.Any, int.Parse(port));
tcpListener.Start();
isOnce = true;
Task.Run(() => AcceptClientsAsync(cts.Token));
}
catch (Exception ex)
{
Debug.LogError($"Error starting server: {ex.Message}");
}
}
private async Task AcceptClientsAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
TcpClient client = await tcpListener.AcceptTcpClientAsync();
Debug.Log($"Client connected: {client.Client.RemoteEndPoint}");
connectedClients.Add(client);
_ = Task.Run(() => HandleClientAsync(client, token));
}
catch (Exception ex)
{
Debug.LogError($"Error accepting client: {ex.Message}");
}
}
}
private async Task HandleClientAsync(TcpClient client, CancellationToken token)
{
using (client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[4096];
while (!token.IsCancellationRequested)
{
try
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (bytesRead == 0) break;
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Debug.Log($"Received: {message}");
sendEvent?.Invoke(message);
}
catch (Exception ex)
{
Debug.LogError($"Error reading from client: {ex.Message}");
break;
}
await Task.Delay(10, token);
}
}
// Cleanup on client disconnect
connectedClients.Remove(client);
}
public void StopServer()
{
if (cts != null)
{
cts.Cancel();
if (tcpListener != null)
{
tcpListener.Stop();
tcpListener = null;
}
cts.Dispose();
cts = null;
}
// Ensure that all connected clients are closed properly
foreach (var client in connectedClients)
{
client.Close();
}
connectedClients.Clear();
}
}
MyClient
类是一个简单的客户端实现,能够与 TCP 服务器进行通信。它提供了连接服务器、发送命令以及关闭连接等基本功能。
using System.Collections;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
public class MyClient
{
private TcpClient client;
private NetworkStream stream;
private static MyClient _ins; // 单例实例
private static bool connected = false;
public static MyClient ins
{
get
{
if (_ins == null)
{
_ins = new MyClient();
}
return _ins;
}
}
// 私有构造函数,防止外部实例化
private MyClient()
{
}
public void Init()
{
if (!connected)
{
connected = true;
var path = Path.Combine(Application.streamingAssetsPath, "IpFile.txt");
var p = Path.Combine(Application.streamingAssetsPath, "MyPort.txt");
if (!File.Exists(p) || !File.Exists(path)) return;
Debug.Log($"Reading IP configuration from: {path}");
Debug.Log($"Reading port from: {p}");
var ipAddress = File.ReadAllText(path, Encoding.UTF8);
var port = File.ReadAllText(p, Encoding.UTF8);
Debug.Log(ipAddress);
Debug.Log(port);
ConnectToServer(ipAddress, int.Parse(port));
}
}
private async void ConnectToServer(string ip, int port)
{
int maxRetryAttempts = 5; // 最大重试次数
int retryDelayMilliseconds = 1500; // 重试间隔,单位为毫秒
for (int attempt = 1; attempt <= maxRetryAttempts; attempt++)
{
try
{
client = new TcpClient();
await client.ConnectAsync(ip, port); // 异步连接服务器
stream = client.GetStream();
Debug.Log("Connected to server.");
return; // 成功连接则退出方法
}
catch (SocketException e)
{
Debug.LogError($"Attempt {attempt} failed to connect: {e.Message}");
if (attempt < maxRetryAttempts)
{
Debug.Log($"Retrying in {retryDelayMilliseconds / 1000} seconds...");
await Task.Delay(retryDelayMilliseconds); // 等待重试
}
else
{
Debug.LogError("Max retry attempts reached. Unable to connect to server.");
}
}
}
}
public async Task SendCommand(string command)
{
if (stream != null && client.Connected)
{
try
{
byte[] data = Encoding.UTF8.GetBytes(command);
await stream.WriteAsync(data, 0, data.Length); // 发送数据到服务器
Debug.Log($"Command sent: {command}");
}
catch (SocketException e)
{
Debug.LogError($"Error sending command: {e.Message}");
}
}
else
{
Debug.LogWarning("Not connected to server.");
}
}
public void CloseConnection()
{
if (stream != null)
{
stream.Close();
stream = null;
}
if (client != null)
{
client.Close();
client = null;
}
Debug.Log("Connection closed.");
}
}