首页 新能源汽车

Unity Socket 网络编程实战:从入门到精通(二)

字数: (4032)
阅读: (4667)
内容摘要:Unity Socket 网络编程实战:从入门到精通(二),

在上一篇 Unity Socket 学习笔记(一)中,我们已经掌握了使用 Socket 进行简单通信的基础。但实际项目中的网络需求远不止于此。今天我们来深入探讨 Unity Socket 的进阶用法,解决更复杂的问题。

问题场景:高并发服务器性能瓶颈

设想一个多人在线游戏,需要同时处理数千甚至上万玩家的连接请求。如果仍然采用简单的单线程 Socket 监听模式,服务器很快就会不堪重负。这涉及到高并发场景下,TCP连接的建立、维护以及数据的快速收发。这时候,我们必须考虑多线程、异步 Socket、以及连接池等技术。

Unity Socket 网络编程实战:从入门到精通(二)

底层原理:异步 Socket 与线程池

传统的同步 Socket 在 Accept() 方法处会阻塞线程,直到有新的连接到来。在高并发场景下,这会严重影响服务器的响应速度。而异步 Socket 则允许我们非阻塞地监听连接请求,并在连接建立后,将数据收发任务交给线程池中的线程处理。这可以有效提高服务器的并发能力。

Unity Socket 网络编程实战:从入门到精通(二)

线程池,顾名思义,是一组预先创建好的线程。当有新的任务需要执行时,线程池会从线程池中取出一个空闲线程来处理任务,而不是每次都创建新的线程。这可以减少线程创建和销毁的开销,提高服务器的性能。类似 Nginx 通过 worker 进程池处理客户端请求,避免频繁的进程创建和销毁,提升并发连接数。

Unity Socket 网络编程实战:从入门到精通(二)

代码解决方案:异步 Socket + 线程池实现高并发服务器

以下是一个简单的异步 Socket + 线程池服务器的示例代码:

Unity Socket 网络编程实战:从入门到精通(二)
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

public class AsyncSocketServer : MonoBehaviour
{
    private Socket listener;
    private ThreadPool threadPool;
    private int maxThreads = 100; // 最大线程数
    private int port = 8888;

    void Start()
    {
        // 初始化线程池
        threadPool = new ThreadPool(maxThreads);

        // 创建 Socket
        listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        // 绑定 IP 地址和端口
        IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);
        listener.Bind(localEndPoint);

        // 开始监听
        listener.Listen(100); // 最大连接数

        Debug.Log("服务器已启动,监听端口:" + port);

        // 开始异步接受连接
        StartAccept(null);
    }

    private void StartAccept(SocketAsyncEventArgs acceptEventArg)
    {
        if (acceptEventArg == null)
        {
            acceptEventArg = new SocketAsyncEventArgs();
            acceptEventArg.Completed += AcceptEventArg_Completed;
        }
        else
        {
            acceptEventArg.AcceptSocket = null;
        }

        if (!listener.AcceptAsync(acceptEventArg))
        {
            ProcessAccept(acceptEventArg);
        }
    }

    private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
    {
        ProcessAccept(e);
    }

    private void ProcessAccept(SocketAsyncEventArgs e)
    {
        Socket clientSocket = e.AcceptSocket;
        if (clientSocket != null)
        {
            Debug.Log("客户端连接:" + clientSocket.RemoteEndPoint);

            // 将客户端连接交给线程池处理
            threadPool.QueueUserWorkItem(HandleClient, clientSocket);

            // 继续接受新的连接
            StartAccept(e);
        }
    }

    private void HandleClient(object obj)
    {
        Socket clientSocket = (Socket)obj;
        byte[] buffer = new byte[1024];
        int bytesReceived;

        try
        {
            while ((bytesReceived = clientSocket.Receive(buffer)) > 0)
            {
                string data = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesReceived);
                Debug.Log("收到客户端数据:" + data);

                // 发送响应数据
                string response = "服务器已收到:" + data;
                byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes(response);
                clientSocket.Send(responseBytes);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError("客户端连接异常:" + ex.Message);
        }
        finally
        {
            // 关闭连接
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
            Debug.Log("客户端连接已关闭:" + clientSocket.RemoteEndPoint);
        }
    }

    void OnApplicationQuit()
    {
        // 关闭 Socket
        if (listener != null)
        {
            listener.Close();
        }
    }
}

这个示例代码使用了 SocketAsyncEventArgs 类来实现异步 Socket,并使用 ThreadPool.QueueUserWorkItem() 方法将客户端连接交给线程池处理。当然,实际项目中需要考虑更完善的线程池实现,例如使用 Semaphore 来控制并发数量,防止线程池耗尽系统资源。

实战避坑:Nagle 算法与延迟

在使用 Socket 进行网络编程时,一个常见的坑是 Nagle 算法导致的延迟。Nagle 算法会延迟小数据包的发送,以减少网络拥塞。但在某些实时性要求高的应用中,例如 FPS 游戏,这种延迟是无法接受的。可以通过设置 socket.NoDelay = true 来禁用 Nagle 算法。

另外,对于高并发服务器,需要关注操作系统的连接数限制(例如 Linux 下的 ulimit -n),合理调整 TCP 参数,如 tcp_tw_recycletcp_tw_reuse,并在必要时使用负载均衡技术,如 Nginx 反向代理,将请求分发到多台服务器上,进一步提高服务器的承载能力。

在后续的 Unity Socket 学习笔记中,我们将继续探讨更高级的网络编程技术,例如 UDP 协议、KCP 协议、以及更复杂的网络架构设计。

Unity Socket 网络编程实战:从入门到精通(二)

转载请注明出处: 键盘上的咸鱼

本文的链接地址: http://m.acea4.store/article/03088.html

本文最后 发布于2026-04-21 10:34:20,已经过了6天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 广东肠粉 1 天前
    博主能不能出一期关于KCP协议的教程,感觉那个协议在Unity里挺火的。
  • 格子衫青年 15 小时前
    博主能不能出一期关于KCP协议的教程,感觉那个协议在Unity里挺火的。
  • 冬天里的一把火 3 天前
    ThreadPool的实现方式有很多,博主用的这个是最简单的,可以考虑使用更高级的线程池,性能会更好。
  • 奶茶续命 2 天前
    ThreadPool的实现方式有很多,博主用的这个是最简单的,可以考虑使用更高级的线程池,性能会更好。
  • 肝帝 2 天前
    感谢分享,写的很详细,受益匪浅。正在学习Socket,这篇文章帮助很大。