C#WebSocket服务端处理多客户端连接

优化 WebSocket 服务端代码

源码

using System.Text; // 用于发送和接收信息时处理文本
using System.Net;   // 用于处理网络连接
using System.Net.WebSockets; // 用于处理WebSocket连接

namespace Test.Program
{
    static class Program
    {
        static HttpListener listener;
        static void Main(string[] args)
        {
            const string HOST = "localhost";     // 监听的主机名
            const int PORT = 3030;              // 监听的端口号

            listener = new HttpListener(); // 实例化HttpListener
            listener.Prefixes.Add($"http://{HOST}:{PORT}/");  // 设置监听的URL
            listener.Start(); // 开始监听
            ListenClientConnections()
                .Wait();
        }

        async static Task ListenClientConnections()
        {
            while (true)
            {
                // 等待客户端的连接
                Console.WriteLine("等待客户端连接");
                HttpListenerContext context = await listener.GetContextAsync();
                // 判断此连接是否为WebSocket的请求
                if (context.Request.IsWebSocketRequest)
                {
                    Console.WriteLine($"接收到客户端连接");
                    // 这时候,我们可以用WebSocket来接受客户端的连接
                    WebSocket webSocket = context.AcceptWebSocketAsync(null).Result.WebSocket;
                    // 这时候我们可以根据需要来处理客户端的消息
                    while (webSocket.State == WebSocketState.Open)
                    {
                        // 等待客户端的消息
                        byte[] buffer = new byte[1024]; // 定义一个缓冲区大小为1KB
                        ArraySegment<byte> segment = new ArraySegment<byte>(buffer);    // 定义一个数组段

                        CancellationToken cancellationToken = CancellationToken.None;   // 定义一个取消标记

                        // 这时候这里一直等待客户端的消息
                        Console.WriteLine("等待客户端消息");
                        WebSocketReceiveResult result = await webSocket.ReceiveAsync(segment, cancellationToken);

                        // 文本类型的消息
                        if (result.MessageType == WebSocketMessageType.Text)
                        {
                            // 这里可以对客户端的消息进行处理
                            string message = Encoding.UTF8.GetString(buffer, 0, result.Count);

                            // 给客户端发送消息
                            string responseMessage = $"接收到客户端的消息: {message}";
                            Console.WriteLine(responseMessage);
                            byte[] responseBuffer = Encoding.UTF8.GetBytes(responseMessage);

                            await webSocket.SendAsync(
                                responseBuffer,     // 发送的消息,字节数组类型
                                WebSocketMessageType.Text,  // 消息类型为文本
                                true,   // 最后一帧,如果为false,则表示后面还有数据
                                CancellationToken.None    // 取消标记
                            );
                        }
                    }
                    Console.WriteLine("客户端连接断开");
                }
            }
        }
    }
}

在这个源码中,我发现,这段代码只支持处理一个客户端的连接,如果有多个客户端连接,那么只有第一个客户端连接会被处理。

为了支持多个客户端连接,我们需要修改代码,使得 WebSocket 服务端可以同时处理多个客户端的连接。

1. 重构 ListenClientConnections 方法

只要将等待客户端连接的部分、等待客户端消息的部分、处理消息的部分、发送消息的部分都提取出来,封装成新的方法即可

  • 创建 ListenClientConnection 方法,用于监听客户端连接,并返回 WebSocket 对象。
  • 创建 WebSocketHandler 方法,用于处理 WebSocket 连接,包括接收消息、处理消息、发送消息。
  • 创建 HandleMessage 方法,用于处理客户端的消息、处理业务逻辑。
  • 修改 ListenClientConnections 方法,用于监听客户端连接,并创建 WebSocketHandler 任务。

为了跟踪客户端连接的数量,我们可以在全局变量中定义一个 clientCount 变量。

static int clientCount = 0;

1. 创建 ListenClientConnection 方法

用于监听客户端连接,并返回 WebSocket 对象。

        private static async Task<WebSocket> ListenClientConnection()
        {
            while (true)
            {
                HttpListenerContext context = await listener.GetContextAsync();
                // 判断此连接是否为WebSocket的请求
                if (context.Request.IsWebSocketRequest)
                {
                    // 这时候,我们可以用WebSocket来接受客户端的连接
                    WebSocket webSocket = context.AcceptWebSocketAsync(null).Result.WebSocket;
                    // 这里我们需要把连接对象返回
                    return webSocket;
                }
            }
        }

2. 创建 WebSocketHandler 方法

用于处理 WebSocket 连接,包括接收消息、处理消息、发送消息。

private static async Task WebSocketHandler(WebSocket webSocket)
{
    while (webSocket.State == WebSocketState.Open)
    {
        // 等待客户端的消息
        byte[] buffer = new byte[1024]; // 定义一个缓冲区大小为1KB
        ArraySegment<byte> segment = new ArraySegment<byte>(buffer);    // 定义一个数组段
        CancellationToken cancellationToken = CancellationToken.None;   // 定义一个取消标记

        // 这时候这里一直等待客户端的消息
        WebSocketReceiveResult result = await webSocket.ReceiveAsync(segment, cancellationToken);

        // 文本类型的消息
        if (result.MessageType == WebSocketMessageType.Text)
        {
            string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
            await HandleMessage(webSocket, message);  // 调用处理消息的方法
        }
    }
    Console.WriteLine("客户端连接断开");
    clientCount--;  // 连接数量减一

    // 输出服务端状态
    Console.WriteLine("接收到客户端连接");
    Console.WriteLine("---服务器状态begin---");
    Console.WriteLine($"连接数量: {clientCount}");
    Console.WriteLine("---服务器状态end---");
}

3. 创建 HandleMessage 方法

用于处理客户端的消息、处理业务逻辑。

private static async Task HandleMessage(WebSocket webSocket, string message)
{
    Console.WriteLine($"接收到客户端的消息: {message}");
    if (message.StartsWith("CLOSE"))
    {
        listener.Close();
    }
    // 这里可以对客户端的消息进行处理
    // 给客户端发送消息
    string responseMessage = $"返回给客户端消息: {message}";
    Console.WriteLine(responseMessage);
    byte[] responseBuffer = Encoding.UTF8.GetBytes(responseMessage);
    await webSocket.SendAsync(
        responseBuffer,     // 发送的消息,字节数组类型
        WebSocketMessageType.Text,  // 消息类型为文本
        true,   // 最后一帧,如果为false,则表示后面还有数据
        CancellationToken.None    // 取消标记
    );
}

4. 修改 ListenClientConnections 方法

用于监听客户端连接,并创建 WebSocketHandler 任务。

async static Task async static Task ListenClientConnections()
{
    while (listener.IsListening)
    {
        Console.WriteLine("等待客户端连接");
        WebSocket webSocket = await ListenClientConnection();
        clientCount++;  // 连接数量加一
        // 输出服务端状态
        Console.WriteLine("接收到客户端连接");
        Console.WriteLine("---服务器状态begin---");
        Console.WriteLine($"连接数量: {clientCount}");
        Console.WriteLine("---服务器状态end---");
        WebSocketHandler(webSocket);
    }
}

2. 完整代码

using System.Text; // 用于发送和接收信息时处理文本
using System.Net;   // 用于处理网络连接
using System.Net.WebSockets; // 用于处理WebSocket连接

namespace Test.Program
{
    static class Program
    {
        static HttpListener listener;
        static int clientCount = 0;
        static void Main(string[] args)
        {
            const string HOST = "localhost";     // 监听的主机名
            const int PORT = 3030;              // 监听的端口号

            listener = new HttpListener(); // 实例化HttpListener
            listener.Prefixes.Add($"http://{HOST}:{PORT}/");  // 设置监听的URL
            listener.Start(); // 开始监听
            ListenClientConnections()
                .Wait();
        }

        private static async Task<WebSocket> ListenClientConnection()
        {
            while (true)
            {
                HttpListenerContext context = await listener.GetContextAsync();
                // 判断此连接是否为WebSocket的请求
                if (context.Request.IsWebSocketRequest)
                {
                    // 这时候,我们可以用WebSocket来接受客户端的连接
                    WebSocket webSocket = context.AcceptWebSocketAsync(null).Result.WebSocket;
                    // 这里我们需要把连接对象返回
                    return webSocket;
                }
            }
        }

        private static async Task WebSocketHandler(WebSocket webSocket)
        {
            while (webSocket.State == WebSocketState.Open)
            {
                // 等待客户端的消息
                byte[] buffer = new byte[1024]; // 定义一个缓冲区大小为1KB
                ArraySegment<byte> segment = new ArraySegment<byte>(buffer);    // 定义一个数组段
                CancellationToken cancellationToken = CancellationToken.None;   // 定义一个取消标记

                // 这时候这里一直等待客户端的消息
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(segment, cancellationToken);

                // 文本类型的消息
                if (result.MessageType == WebSocketMessageType.Text)
                {
                    string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                    await HandleMessage(webSocket, message);  // 调用处理消息的方法
                }
            }
            Console.WriteLine("客户端连接断开");
            clientCount--;

            // 输出服务端状态
            Console.WriteLine("接收到客户端连接");
            Console.WriteLine("---服务器状态begin---");
            Console.WriteLine($"连接数量: {clientCount}");
            Console.WriteLine("---服务器状态end---");
        }

        private static async Task HandleMessage(WebSocket webSocket, string message)
        {
            Console.WriteLine($"接收到客户端的消息: {message}");
            if (message.StartsWith("CLOSE"))
            {
                listener.Close();
            }
            // 这里可以对客户端的消息进行处理
            // 给客户端发送消息
            string responseMessage = $"返回给客户端消息: {message}";
            Console.WriteLine(responseMessage);
            byte[] responseBuffer = Encoding.UTF8.GetBytes(responseMessage);
            await webSocket.SendAsync(
                responseBuffer,     // 发送的消息,字节数组类型
                WebSocketMessageType.Text,  // 消息类型为文本
                true,   // 最后一帧,如果为false,则表示后面还有数据
                CancellationToken.None    // 取消标记
            );
        }


        async static Task ListenClientConnections()
        {
            while (listener.IsListening)
            {
                Console.WriteLine("等待客户端连接");
                WebSocket webSocket = await ListenClientConnection();
                clientCount++;
                // 输出服务端状态
                Console.WriteLine("接收到客户端连接");
                Console.WriteLine("---服务器状态begin---");
                Console.WriteLine($"连接数量: {clientCount}");
                Console.WriteLine("---服务器状态end---");
                WebSocketHandler(webSocket);
            }
        }
    }
}
posted @   慕色寒枝  阅读(240)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示