最佳化 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);
}
}
}
}