自学内容网 自学内容网

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


原文地址:https://blog.csdn.net/R3333355726856/article/details/144063938

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!