unity引擎网络系统架构设计

设计一个高效的网络系统架构对于多人游戏和实时应用至关重要。Unity 提供了多种网络解决方案,包括 Unity 的内置网络系统(UNet,已弃用)、Mirror、Photon 和 Unity Multiplayer(MLAPI)。以下是一个通用的网络系统架构设计指南,适用于 Unity 引擎。

1. 网络系统选择

1.1 Unity Multiplayer(MLAPI)

Unity Multiplayer 是 Unity 官方推荐的网络解决方案,提供高性能和易用的 API,适用于各种类型的多人游戏。

1.2 Mirror

Mirror 是一个开源的高性能网络库,基于 UNet 开发,提供了丰富的功能和良好的社区支持。

1.3 Photon

Photon 是一个商业化的网络解决方案,提供强大的云服务和易用的 API,适用于实时多人游戏和应用。

2. 架构设计

2.1 客户端-服务器模型

客户端-服务器模型是最常见的网络架构,适用于大多数多人游戏和实时应用。

  • 服务器:负责处理游戏逻辑、同步状态和管理客户端连接。
  • 客户端:负责渲染、输入处理和本地逻辑。
2.2 P2P(点对点)模型

P2P 模型适用于小规模的多人游戏和应用,客户端之间直接通信,减少了服务器的负担。

  • 主机客户端:一个客户端充当主机,负责处理游戏逻辑和同步状态。
  • 客户端:其他客户端连接到主机,进行通信和同步。

3. 关键组件

3.1 网络管理器

网络管理器是网络系统的核心组件,负责管理连接、消息传递和状态同步。

  • 连接管理:处理客户端的连接和断开。
  • 消息传递:处理客户端和服务器之间的消息传递。
  • 状态同步:同步游戏状态和数据。
3.2 网络对象

网络对象是需要在客户端和服务器之间同步的游戏对象。

  • NetworkIdentity:标识网络对象的唯一身份。
  • NetworkTransform:同步对象的变换(位置、旋转、缩放)。
  • NetworkBehaviour:自定义的网络行为脚本,处理对象的网络逻辑。
3.3 网络消息

网络消息用于在客户端和服务器之间传递数据和指令。

  • RPC(远程过程调用):客户端和服务器之间的函数调用。
  • Command:客户端向服务器发送指令。
  • ClientRpc:服务器向客户端发送指令。

4. 实现步骤

4.1 初始化网络
  1. 选择网络解决方案:根据项目需求选择合适的网络库(如 MLAPI、Mirror 或 Photon)。
  2. 配置网络管理器:设置网络管理器的参数,如最大连接数、端口号和网络模式(客户端/服务器)。
4.2 创建网络对象
  1. 定义网络对象:创建需要同步的游戏对象,并添加 NetworkIdentity 组件。
  2. 添加网络行为:编写自定义的网络行为脚本,处理对象的网络逻辑。
4.3 处理连接和消息
  1. 管理连接:在网络管理器中处理客户端的连接和断开事件。
  2. 传递消息:使用 RPC、Command 和 ClientRpc 在客户端和服务器之间传递数据和指令。
4.4 同步状态
  1. 同步变换:使用 NetworkTransform 同步对象的变换。
  2. 同步自定义数据:在网络行为脚本中同步自定义的数据和状态。

5. 性能优化

5.1 减少网络流量
  • 压缩数据:使用数据压缩技术减少消息大小。
  • 合并消息:合并小消息,减少消息的发送频率。
  • 使用可靠传输:仅在必要时使用可靠传输,减少网络开销。
5.2 优化同步
  • 插值和外推:使用插值和外推技术平滑对象的运动。
  • 频率控制:根据对象的重要性和变化频率调整同步频率。

6. 安全性

6.1 验证和授权
  • 身份验证:在客户端连接时进行身份验证,确保合法用户访问。
  • 权限控制:在服务器端进行权限控制,防止非法操作和数据篡改。
6.2 数据加密
  • 传输加密:使用 SSL/TLS 加密网络通信,防止数据在传输过程中被窃取或篡改。
  • 敏感数据加密:对敏感数据进行加密存储和传输,确保数据安全。
6.3 防御常见攻击
  • 防止DDoS攻击:使用防火墙和流量限制技术,防止分布式拒绝服务攻击。
  • 防止作弊:在服务器端进行关键逻辑验证,防止客户端作弊行为。
  • 防止重放攻击:使用时间戳和随机数防止消息重放攻击。

7. 测试和调试

7.1 单元测试
  • 网络组件测试:编写单元测试,验证网络组件的功能和行为。
  • 模拟网络环境:使用模拟网络环境测试网络系统的性能和稳定性。
7.2 集成测试
  • 多客户端测试:在本地或云环境中进行多客户端测试,验证系统的并发性能和同步效果。
  • 压力测试:进行压力测试,评估系统在高负载下的性能和稳定性。
7.3 调试工具
  • 网络日志:记录网络事件和消息,帮助调试和分析问题。
  • 网络监控:使用网络监控工具,实时监控网络流量和连接状态。

8. 部署和维护

8.1 服务器部署
  • 云服务器:使用云服务提供商(如 AWS、Azure 或 Google Cloud)部署服务器,确保高可用性和可扩展性。
  • 负载均衡:使用负载均衡技术,分配客户端请求,提升系统性能和可靠性。
8.2 版本管理
  • 版本控制:使用版本控制系统(如 Git)管理代码和配置,确保一致性和可追溯性。
  • 自动化部署:使用 CI/CD 工具(如 Jenkins、GitHub Actions)实现自动化构建和部署,提升开发效率和质量。
8.3 监控和维护
  • 系统监控:使用监控工具(如 Prometheus、Grafana)监控服务器性能和健康状态。
  • 日志管理:收集和分析日志,及时发现和解决问题。
  • 定期维护:定期进行系统维护和更新,确保系统的安全性和稳定性。

9. 案例分析

9.1 小型多人游戏
  • 网络解决方案:使用 Mirror 实现 P2P 模型,主机客户端负责游戏逻辑和同步。
  • 关键组件:网络管理器、网络对象、网络消息。
  • 优化措施:减少网络流量、优化同步、确保安全性。
9.2 大型多人在线游戏(MMO)
  • 网络解决方案:使用 Unity Multiplayer(MLAPI)实现客户端-服务器模型,服务器负责游戏逻辑和同步。
  • 关键组件:网络管理器、网络对象、网络消息、数据库。
  • 优化措施:减少网络流量、优化同步、确保安全性、负载均衡、自动化部署。

10. 总结

设计一个高效的网络系统架构需要综合考虑多方面的因素,包括网络模型、关键组件、性能优化、安全性、测试和部署。通过选择合适的网络解决方案,合理设计和实现网络系统,开发者可以创建高质量的多人游戏和实时应用。希望这份指南对你有所帮助,祝你在 Unity 网络开发中取得成功!

unity引擎底层的网络系统代码逻辑实现

在 Unity 中实现底层的网络系统代码逻辑,可以使用低级别的网络 API,如 System.Net.Sockets。这种方法适用于需要高度自定义和控制的网络通信场景。以下是一个简单的示例,展示如何使用 System.Net.Sockets 实现基本的客户端-服务器通信。

1. 服务器端实现

首先,实现一个简单的 TCP 服务器,监听客户端连接并处理消息。

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SimpleTcpServer
{
    private TcpListener server;
    private bool isRunning;

    public SimpleTcpServer(string ipAddress, int port)
    {
        server = new TcpListener(IPAddress.Parse(ipAddress), port);
        server.Start();
        isRunning = true;

        Thread serverThread = new Thread(new ThreadStart(Run));
        serverThread.Start();
    }

    private void Run()
    {
        while (isRunning)
        {
            try
            {
                TcpClient client = server.AcceptTcpClient();
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(client);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Server exception: " + ex.Message);
            }
        }
    }

    private void HandleClient(object obj)
    {
        TcpClient client = (TcpClient)obj;
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];
        int bytesRead;

        while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: " + message);

            // Echo the message back to the client
            byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
            stream.Write(response, 0, response.Length);
        }

        client.Close();
    }

    public void Stop()
    {
        isRunning = false;
        server.Stop();
    }
}

2. 客户端实现

接下来,实现一个简单的 TCP 客户端,连接到服务器并发送消息。

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SimpleTcpClient
{
    private TcpClient client;
    private NetworkStream stream;

    public SimpleTcpClient(string ipAddress, int port)
    {
        client = new TcpClient(ipAddress, port);
        stream = client.GetStream();

        Thread clientThread = new Thread(new ThreadStart(ReceiveMessages));
        clientThread.Start();
    }

    public void SendMessage(string message)
    {
        byte[] data = Encoding.ASCII.GetBytes(message);
        stream.Write(data, 0, data.Length);
    }

    private void ReceiveMessages()
    {
        byte[] buffer = new byte[1024];
        int bytesRead;

        while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: " + message);
        }
    }

    public void Close()
    {
        stream.Close();
        client.Close();
    }
}

3. 在 Unity 中使用

将上述服务器和客户端代码集成到 Unity 项目中。

3.1 服务器端

创建一个新的脚本 ServerManager,并将其添加到一个空的 GameObject 上。

using UnityEngine;

public class ServerManager : MonoBehaviour
{
    private SimpleTcpServer server;

    void Start()
    {
        server = new SimpleTcpServer("127.0.0.1", 8080);
    }

    void OnApplicationQuit()
    {
        server.Stop();
    }
}
3.2 客户端

创建一个新的脚本 ClientManager,并将其添加到另一个空的 GameObject 上。

using UnityEngine;

public class ClientManager : MonoBehaviour
{
    private SimpleTcpClient client;

    void Start()
    {
        client = new SimpleTcpClient("127.0.0.1", 8080);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            client.SendMessage("Hello, Server!");
        }
    }

    void OnApplicationQuit()
    {
        client.Close();
    }
}

4. 运行和测试(续)

  1. 创建一个新的场景,并将 ServerManagerClientManager 脚本分别添加到两个不同的 GameObject 上。
  2. 运行 Unity 项目,确保服务器和客户端都在同一个场景中运行。
  3. 按下空格键,客户端将发送消息到服务器,服务器将接收到消息并回显。

5. 进一步扩展

5.1 处理更多的客户端

当前的服务器实现只能处理一个客户端。为了处理多个客户端,可以在 SimpleTcpServer 中维护一个客户端列表,并在 HandleClient 方法中处理每个客户端的消息。

using System.Collections.Generic;

public class SimpleTcpServer
{
    private TcpListener server;
    private bool isRunning;
    private List<TcpClient> clients = new List<TcpClient>();

    public SimpleTcpServer(string ipAddress, int port)
    {
        server = new TcpListener(IPAddress.Parse(ipAddress), port);
        server.Start();
        isRunning = true;

        Thread serverThread = new Thread(new ThreadStart(Run));
        serverThread.Start();
    }

    private void Run()
    {
        while (isRunning)
        {
            try
            {
                TcpClient client = server.AcceptTcpClient();
                clients.Add(client);
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(client);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Server exception: " + ex.Message);
            }
        }
    }

    private void HandleClient(object obj)
    {
        TcpClient client = (TcpClient)obj;
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];
        int bytesRead;

        while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: " + message);

            // Echo the message back to all clients
            byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
            foreach (var c in clients)
            {
                c.GetStream().Write(response, 0, response.Length);
            }
        }

        clients.Remove(client);
        client.Close();
    }

    public void Stop()
    {
        isRunning = false;
        server.Stop();
    }
}
5.2 处理断开连接

在客户端和服务器中处理断开连接的情况,确保资源正确释放。

public class SimpleTcpClient
{
    private TcpClient client;
    private NetworkStream stream;
    private bool isRunning;

    public SimpleTcpClient(string ipAddress, int port)
    {
        client = new TcpClient(ipAddress, port);
        stream = client.GetStream();
        isRunning = true;

        Thread clientThread = new Thread(new ThreadStart(ReceiveMessages));
        clientThread.Start();
    }

    public void SendMessage(string message)
    {
        if (isRunning)
        {
            byte[] data = Encoding.ASCII.GetBytes(message);
            stream.Write(data, 0, data.Length);
        }
    }

    private void ReceiveMessages()
    {
        byte[] buffer = new byte[1024];
        int bytesRead;

        while (isRunning && (bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: " + message);
        }
    }

    public void Close()
    {
        isRunning = false;
        stream.Close();
        client.Close();
    }
}
5.3 处理更多的消息类型

可以扩展客户端和服务器的消息处理逻辑,支持更多的消息类型。例如,可以定义一个简单的协议,使用不同的消息前缀来区分消息类型。

private void HandleClient(object obj)
{
    TcpClient client = (TcpClient)obj;
    NetworkStream stream = client.GetStream();
    byte[] buffer = new byte[1024];
    int bytesRead;

    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
    {
        string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine("Received: " + message);

        if (message.StartsWith("CHAT:"))
        {
            // Handle chat message
            string chatMessage = message.Substring(5);
            Console.WriteLine("Chat message: " + chatMessage);
        }
        else if (message.StartsWith("MOVE:"))
        {
            // Handle move message
            string move消息 = message.Substring(5);
            Console.WriteLine("Move message: " + moveMessage);
        }
        else
        {
            // Handle unknown message
            Console.WriteLine("Unknown message type: " + message);
        }

        // Echo the message back to all clients
        byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
        foreach (var c in clients)
        {
            c.GetStream().Write(response, 0, response.Length);
        }
    }

    clients.Remove(client);
    client.Close();
}

在客户端中,可以根据需要发送不同类型的消息:

public void SendMessage(string messageType, string message)
{
    if (isRunning)
    {
        string fullMessage = messageType + ":" + message;
        byte[] data = Encoding.ASCII.GetBytes(fullMessage);
        stream.Write(data, 0, data.Length);
    }
}

ClientManager 中,可以根据用户输入发送不同类型的消息:

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        client.SendMessage("CHAT", "Hello, Server!");
    }
    if (Input.GetKeyDown(KeyCode.M))
    {
        client.SendMessage("MOVE", "Player moved to position (x, y)");
    }
}

6. 处理并发和线程安全

在处理多个客户端时,需要注意并发和线程安全问题。可以使用锁(lock)来确保对共享资源的访问是线程安全的。

private readonly object clientListLock = new object();

private void HandleClient(object obj)
{
    TcpClient client = (TcpClient)obj;
    NetworkStream stream = client.GetStream();
    byte[] buffer = new byte[1024];
    int bytesRead;

    lock (clientListLock)
    {
        clients.Add(client);
    }

    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
    {
        string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine("Received: " + message);

        if (message.StartsWith("CHAT:"))
        {
            string chatMessage = message.Substring(5);
            Console.WriteLine("Chat message: " + chatMessage);
        }
        else if (message.StartsWith("MOVE:"))
        {
            string moveMessage = message.Substring(5);
            Console.WriteLine("Move message: " + moveMessage);
        }
        else
        {
            Console.WriteLine("Unknown message type: " + message);
        }

        byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
        lock (clientListLock)
        {
            foreach (var c in clients)
            {
                c.GetStream().Write(response, 0, response.Length);
            }
        }
    }

    lock (clientListLock)
    {
        clients.Remove(client);
    }
    client.Close();
}

7. 处理心跳和超时

为了确保客户端和服务器之间的连接是活跃的,可以实现心跳机制。客户端定期发送心跳消息,服务器检查心跳消息的接收情况,如果超时未收到心跳消息,则认为客户端已断开。

在客户端中实现心跳消息:

public void StartHeartbeat()
{
    Thread heartbeatThread = new Thread(new ThreadStart(SendHeartbeat));
    heartbeatThread.Start();
}

private void SendHeartbeat()
{
    while (isRunning)
    {
        SendMessage("HEARTBEAT", "ping");
        Thread.Sleep(5000); // 每5秒发送一次心跳消息
    }
}

在服务器中处理心跳消息和超时:

private Dictionary<TcpClient, DateTime> clientLastHeartbeat = new Dictionary<TcpClient, DateTime>();

private void HandleClient(object obj)
{
    TcpClient client = (TcpClient)obj;
    NetworkStream stream = client.GetStream();
    byte[] buffer = new byte[1024];
    int bytesRead;

    lock (clientListLock)
    {
        clients.Add(client);
        clientLastHeartbeat[client] = DateTime.Now;
    }

    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
    {
        string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine("Received: " + message);

        if (message.StartsWith("HEARTBEAT:"))
        {
            lock (clientListLock)
            {
                clientLastHeartbeat[client] = DateTime.Now;
            }
        }
        else if (message.StartsWith("CHAT:"))
        {
            string chatMessage = message.Substring(5);
            Console.WriteLine("Chat message: " + chatMessage```csharp
        }
        else if (message.StartsWith("MOVE:"))
        {
            string moveMessage = message.Substring(5);
            Console.WriteLine("Move message: " + moveMessage);
        }
        else
        {
            Console.WriteLine("Unknown message type: " + message);
        }

        byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
        lock (clientListLock)
        {
            foreach (var c in clients)
            {
                c.GetStream().Write(response, 0, response.Length);
            }
        }
    }

    lock (clientListLock)
    {
        clients.Remove(client);
        clientLastHeartbeat.Remove(client);
    }
    client.Close();
}

private void CheckHeartbeat()
{
    while (isRunning)
    {
        lock (clientListLock)
        {
            DateTime now = DateTime.Now;
            List<TcpClient> clientsToRemove = new List<TcpClient>();

            foreach (var kvp in clientLastHeartbeat)
            {
                if ((now - kvp.Value).TotalSeconds > 10) // 超过10秒未收到心跳消息
                {
                    clientsToRemove.Add(kvp.Key);
                }
            }

            foreach (var client in clientsToRemove)
            {
                clients.Remove(client);
                clientLastHeartbeat.Remove(client);
                client.Close();
                Console.WriteLine("Client disconnected due to timeout.");
            }
        }

        Thread.Sleep(5000); // 每5秒检查一次心跳
    }
}

在服务器的构造函数中启动心跳检查线程:

public SimpleTcpServer(string ipAddress, int port)
{
    server = new TcpListener(IPAddress.Parse(ipAddress), port);
    server.Start();
    isRunning = true;

    Thread serverThread = new Thread(new ThreadStart(Run));
    serverThread.Start();

    Thread heartbeatThread = new Thread(new ThreadStart(CheckHeartbeat));
    heartbeatThread.Start();
}

8. 处理异常和错误

在实际应用中,网络通信可能会遇到各种异常和错误。需要在代码中添加适当的异常处理,以确保系统的稳定性。

在客户端和服务器的网络操作中添加异常处理:

private void HandleClient(object obj)
{
    TcpClient client = (TcpClient)obj;
    NetworkStream stream = client.GetStream();
    byte[] buffer = new byte[1024];
    int bytesRead;

    lock (clientListLock)
    {
        clients.Add(client);
        clientLastHeartbeat[client] = DateTime.Now;
    }

    try
    {
        while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: " + message);

            if (message.StartsWith("HEARTBEAT:"))
            {
                lock (clientListLock)
                {
                    clientLastHeartbeat[client] = DateTime.Now;
                }
            }
            else if (message.StartsWith("CHAT:"))
            {
                string chatMessage = message.Substring(5);
                Console.WriteLine("Chat message: " + chatMessage);
            }
            else if (message.StartsWith("MOVE:"))
            {
                string moveMessage = message.Substring(5);
                Console.WriteLine("Move message: " + moveMessage);
            }
            else
            {
                Console.WriteLine("Unknown message type: " + message);
            }

            byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
            lock (clientListLock)
            {
                foreach (var c in clients)
                {
                    c.GetStream().Write(response, 0, response.Length);
                }
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Client handling exception: " + ex.Message);
    }
    finally
    {
        lock (clientListLock)
        {
            clients.Remove(client);
            clientLastHeartbeat.Remove(client);
        }
        client.Close();
    }
}

在客户端中添加异常处理:

private void ReceiveMessages()
{
    byte[] buffer = new byte[1024];
    int bytesRead;

    try
    {
        while (isRunning && (bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: " + message);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Client receive exception: " + ex.Message);
    }
}

9. 总结(续)

通过上述步骤,我们实现了一个基本的客户端-服务器通信系统,支持多客户端连接、消息处理、心跳检测和异常处理。以下是我们实现的主要功能:

  1. 基本通信:客户端和服务器可以通过 TCP 进行基本的消息传递。
  2. 多客户端支持:服务器可以处理多个客户端的连接和消息。
  3. 消息类型处理:通过简单的协议区分不同类型的消息(如聊天消息和移动消息)。
  4. 心跳检测:客户端定期发送心跳消息,服务器检测心跳消息以判断客户端是否仍然在线。
  5. 异常处理:在网络操作中添加异常处理,确保系统的稳定性。

10. 进一步扩展

虽然我们已经实现了一个基本的通信系统,但在实际应用中,可能还需要更多的功能和优化。以下是一些可以进一步扩展的方向:

10.1 加密和安全

在实际应用中,网络通信需要考虑安全性。可以使用 SSL/TLS 加密通信,确保数据在传输过程中不被窃听或篡改。

10.2 数据序列化

为了传输复杂的数据结构,可以使用数据序列化技术(如 JSON、XML 或 Protobuf)。这可以使得消息传递更加灵活和强大。

10.3 高效的消息处理

对于高并发的应用,可以使用更高效的消息处理机制,如异步 I/O 或消息队列。这样可以提高系统的性能和响应速度。

10.4 负载均衡

在大规模应用中,可以使用负载均衡技术,将客户端请求分发到多个服务器上,确保系统的高可用性和扩展性。

10.5 日志和监控

为了更好地维护和调试系统,可以添加日志和监控功能,记录系统的运行状态和异常情况。

11. 示例代码

以下是完整的示例代码,包括客户端和服务器的实现:

服务器代码
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SimpleTcpServer
{
    private TcpListener server;
    private bool isRunning;
    private List<TcpClient> clients = new List<TcpClient>();
    private Dictionary<TcpClient, DateTime> clientLastHeartbeat = new Dictionary<TcpClient, DateTime>();
    private readonly object clientListLock = new object();

    public SimpleTcpServer(string ipAddress, int port)
    {
        server = new TcpListener(IPAddress.Parse(ipAddress), port);
        server.Start();
        isRunning = true;

        Thread serverThread = new Thread(new ThreadStart(Run));
        serverThread.Start();

        Thread heartbeatThread = new Thread(new ThreadStart(CheckHeartbeat));
        heartbeatThread.Start();
    }

    private void Run()
    {
        while (isRunning)
        {
            try
            {
                TcpClient client = server.AcceptTcpClient();
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(client);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Server exception: " + ex.Message);
            }
        }
    }

    private void HandleClient(object obj)
    {
        TcpClient client = (TcpClient)obj;
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];
        int bytesRead;

        lock (clientListLock)
        {
            clients.Add(client);
            clientLastHeartbeat[client] = DateTime.Now;
        }

        try
        {
            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine("Received: " + message);

                if (message.StartsWith("HEARTBEAT:"))
                {
                    lock (clientListLock)
                    {
                        clientLastHeartbeat[client] = DateTime.Now;
                    }
                }
                else if (message.StartsWith("CHAT:"))
                {
                    string chatMessage = message.Substring(5);
                    Console.WriteLine("Chat message: " + chatMessage);
                }
                else if (message.StartsWith("MOVE:"))
                {
                    string moveMessage = message.Substring(5);
                    Console.WriteLine("Move message: " + moveMessage);
                }
                else
                {
                    Console.WriteLine("Unknown message type: " + message);
                }

                byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
                lock (clientListLock)
                {
                    foreach```csharp
                    (var c in clients)
                    {
                        c.GetStream().Write(response, 0, response.Length);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Client handling exception: " + ex.Message);
        }
        finally
        {
            lock (clientListLock)
            {
                clients.Remove(client);
                clientLastHeartbeat.Remove(client);
            }
            client.Close();
        }
    }

    private void CheckHeartbeat()
    {
        while (isRunning)
        {
            lock (clientListLock)
            {
                DateTime now = DateTime.Now;
                List<TcpClient> clientsToRemove = new List<TcpClient>();

                foreach (var kvp in clientLastHeartbeat)
                {
                    if ((now - kvp.Value).TotalSeconds > 10) // 超过10秒未收到心跳消息
                    {
                        clientsToRemove.Add(kvp.Key);
                    }
                }

                foreach (var client in clientsToRemove)
                {
                    clients.Remove(client);
                    clientLastHeartbeat.Remove(client);
                    client.Close();
                    Console.WriteLine("Client disconnected due to timeout.");
                }
            }

            Thread.Sleep(5000); // 每5秒检查一次心跳
        }
    }
}
客户端代码
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SimpleTcpClient
{
    private TcpClient client;
    private NetworkStream stream;
    private bool isRunning;

    public SimpleTcpClient(string ipAddress, int port)
    {
        client = new TcpClient(ipAddress, port);
        stream = client.GetStream();
        isRunning = true;

        Thread receiveThread = new Thread(new ThreadStart(ReceiveMessages));
        receiveThread.Start();

        StartHeartbeat();
    }

    public void SendMessage(string messageType, string message)
    {
        if (isRunning)
        {
            string fullMessage = messageType + ":" + message;
            byte[] data = Encoding.ASCII.GetBytes(fullMessage);
            stream.Write(data, 0, data.Length);
        }
    }

    private void ReceiveMessages()
    {
        byte[] buffer = new byte[1024];
        int bytesRead;

        try
        {
            while (isRunning && (bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine("Received: " + message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Client receive exception: " + ex.Message);
        }
    }

    public void StartHeartbeat()
    {
        Thread heartbeatThread = new Thread(new ThreadStart(SendHeartbeat));
        heartbeatThread.Start();
    }

    private void SendHeartbeat()
    {
        while (isRunning)
        {
            SendMessage("HEARTBEAT", "ping");
            Thread.Sleep(5000); // 每5秒发送一次心跳消息
        }
    }

    public void Close()
    {
        isRunning = false;
        stream.Close();
        client.Close();
    }
}
使用示例

在主程序中,可以创建服务器和客户端实例,并进行简单的交互:

class Program
{
    static void Main(string[] args)
    {
        // 启动服务器
        SimpleTcpServer server = new SimpleTcpServer("127.0.0.1", 8888);

        // 启动客户端
        SimpleTcpClient client = new SimpleTcpClient("127.0.0.1", 8888);

        // 发送聊天消息
        client.SendMessage("CHAT", "Hello, Server!");

        // 发送移动消息
        client.SendMessage("MOVE", "Player moved to position (x, y)");

        // 等待一段时间以便观察消息传递
        Thread.Sleep(20000);

        // 关闭客户端
        client.Close();
    }
}

12. 结语(续)

通过上述代码,我们实现了一个基本的客户端-服务器通信系统,支持多客户端连接、消息处理、心跳检测和异常处理。这个系统可以作为更复杂应用的基础,进一步扩展和优化以满足实际需求。希望这个示例对你有所帮助!

13. 进一步优化和改进

虽然我们已经实现了一个基本的通信系统,但在实际应用中,可能还需要更多的功能和优化。以下是一些可以进一步优化和改进的方向:

13.1 异步编程

使用异步编程可以提高系统的性能和响应速度。C# 提供了 asyncawait 关键字,可以方便地实现异步操作。

13.2 使用高级协议

可以使用更高级的协议(如 WebSocket)来实现实时通信。WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,适用于需要低延迟和高频率消息传递的应用。

13.3 负载均衡和高可用性

在大规模应用中,可以使用负载均衡技术,将客户端请求分发到多个服务器上,确保系统的高可用性和扩展性。可以使用反向代理服务器(如 Nginx)或云服务提供的负载均衡器。

13.4 日志和监控

为了更好地维护和调试系统,可以添加日志和监控功能,记录系统的运行状态和异常情况。可以使用日志库(如 NLog 或 log4net)和监控工具(如 Prometheus 和 Grafana)。

13.5 单元测试和集成测试

为了确保系统的稳定性和可靠性,可以编写单元测试和集成测试。可以使用测试框架(如 NUnit 或 xUnit)来编写和运行测试。

14. 完整示例代码

以下是完整的示例代码,包括服务器和客户端的实现,以及主程序的使用示例。

服务器代码
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SimpleTcpServer
{
    private TcpListener server;
    private bool isRunning;
    private List<TcpClient> clients = new List<TcpClient>();
    private Dictionary<TcpClient, DateTime> clientLastHeartbeat = new Dictionary<TcpClient, DateTime>();
    private readonly object clientListLock = new object();

    public SimpleTcpServer(string ipAddress, int port)
    {
        server = new TcpListener(IPAddress.Parse(ipAddress), port);
        server.Start();
        isRunning = true;

        Thread serverThread = new Thread(new ThreadStart(Run));
        serverThread.Start();

        Thread heartbeatThread = new Thread(new ThreadStart(CheckHeartbeat));
        heartbeatThread.Start();
    }

    private void Run()
    {
        while (isRunning)
        {
            try
            {
                TcpClient client = server.AcceptTcpClient();
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(client);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Server exception: " + ex.Message);
            }
        }
    }

    private void HandleClient(object obj)
    {
        TcpClient client = (TcpClient)obj;
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];
        int bytesRead;

        lock (clientListLock)
        {
            clients.Add(client);
            clientLastHeartbeat[client] = DateTime.Now;
        }

        try
        {
            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine("Received: " + message);

                if (message.StartsWith("HEARTBEAT:"))
                {
                    lock (clientListLock)
                    {
                        clientLastHeartbeat[client] = DateTime.Now;
                    }
                }
                else if (message.StartsWith("CHAT:"))
                {
                    string chatMessage = message.Substring(5);
                    Console.WriteLine("Chat message: " + chatMessage);
                }
                else if (message.StartsWith("MOVE:"))
                {
                    string moveMessage = message.Substring(5);
                    Console.WriteLine("Move message: " + moveMessage);
                }
                else
                {
                    Console.WriteLine("Unknown message type: " + message);
                }

                byte[] response = Encoding.ASCII.GetBytes("Echo: " + message);
                lock (clientListLock)
                {
                    foreach (var c in clients```csharp
                    {
                        c.GetStream().Write(response, 0, response.Length);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Client handling exception: " + ex.Message);
        }
        finally
        {
            lock (clientListLock)
            {
                clients.Remove(client);
                clientLastHeartbeat.Remove(client);
            }
            client.Close();
        }
    }

    private void CheckHeartbeat()
    {
        while (isRunning)
        {
            lock (clientListLock)
            {
                DateTime now = DateTime.Now;
                List<TcpClient> clientsToRemove = new List<TcpClient>();

                foreach (var kvp in clientLastHeartbeat)
                {
                    if ((now - kvp.Value).TotalSeconds > 10) // 超过10秒未收到心跳消息
                    {
                        clientsToRemove.Add(kvp.Key);
                    }
                }

                foreach (var client in clientsToRemove)
                {
                    clients.Remove(client);
                    clientLastHeartbeat.Remove(client);
                    client.Close();
                    Console.WriteLine("Client disconnected due to timeout.");
                }
            }

            Thread.Sleep(5000); // 每5秒检查一次心跳
        }
    }
}
客户端代码
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SimpleTcpClient
{
    private TcpClient client;
    private NetworkStream stream;
    private bool isRunning;

    public SimpleTcpClient(string ipAddress, int port)
    {
        client = new TcpClient(ipAddress, port);
        stream = client.GetStream();
        isRunning = true;

        Thread receiveThread = new Thread(new ThreadStart(ReceiveMessages));
        receiveThread.Start();

        StartHeartbeat();
    }

    public void SendMessage(string messageType, string message)
    {
        if (isRunning)
        {
            string fullMessage = messageType + ":" + message;
            byte[] data = Encoding.ASCII.GetBytes(fullMessage);
            stream.Write(data, 0, data.Length);
        }
    }

    private void ReceiveMessages()
    {
        byte[] buffer = new byte[1024];
        int bytesRead;

        try
        {
            while (isRunning && (bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine("Received: " + message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Client receive exception: " + ex.Message);
        }
    }

    public void StartHeartbeat()
    {
        Thread heartbeatThread = new Thread(new ThreadStart(SendHeartbeat));
        heartbeatThread.Start();
    }

    private void SendHeartbeat()
    {
        while (isRunning)
        {
            SendMessage("HEARTBEAT", "ping");
            Thread.Sleep(5000); // 每5秒发送一次心跳消息
        }
    }

    public void Close()
    {
        isRunning = false;
        stream.Close();
        client.Close();
    }
}
使用示例

在主程序中,可以创建服务器和客户端实例,并进行简单的交互:

class Program
{
    static void Main(string[] args)
    {
        // 启动服务器
        SimpleTcpServer server = new SimpleTcpServer("127.0.0.1", 8888);

        // 启动客户端
        SimpleTcpClient client = new SimpleTcpClient("127.0.0.1", 8888);

        // 发送聊天消息
        client.SendMessage("CHAT", "Hello, Server!");

        // 发送移动消息
        client.SendMessage("MOVE", "Player moved to position (x, y)");

        // 等待一段时间以便观察消息传递
        Thread.Sleep(20000);

        // 关闭客户端
        client.Close();
    }
}

15. 结语

通过上述代码,我们实现了一个基本的客户端-服务器通信系统,支持多客户端连接、消息处理、心跳检测和异常处理。这个系统可以作为更复杂应用的基础,进一步扩展和优化以满足实际需求。希望这个示例对你有所帮助!

在实际应用中,可能还需要根据具体需求进行更多的优化和改进,例如使用异步编程、加密通信、负载均衡、日志和监控等。希望你能根据自己的需求,进一步完善和优化这个系统。祝你编程愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值