WebAPI-WebSocket

WebAPI-WebSocket

HTTP是短链接

WebSocket是长连接

HTTP通勤是单向的,基于请求响应模式

WebSocket支持双向通信

HTTP和WebSocket底层都是TCP链接


处理请求

在ASP.NETCore中,我们甚至可以直接使用控制器来处理WS请求,但这并不符合设计规范,所以我们使用第二种方法——中间件处理Ws请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 使用 WebSocket 中间件
app.UseWebSockets();

app.MapControllers(); // 映射控制器


// 处理 WebSocket 请求
// 定义一个路由 `/ws`,并处理 WebSocket 请求
app.Map("/ws", async context =>
{
// 检查当前请求是否是 WebSocket 请求
if (context.WebSockets.IsWebSocketRequest)
{
// 接受 WebSocket 连接
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();

// 处理 WebSocket 连接
await HandleWebSocketConnection(webSocket);
}
else
{
// 如果请求不是 WebSocket 请求,返回 400 Bad Request 状态码
context.Response.StatusCode = 400;
}
});

app.Run();

// 处理 WebSocket 连接的方法
async Task HandleWebSocketConnection(WebSocket webSocket)
{
// 创建一个缓冲区来接收消息
var buffer = new byte[1024 * 4];

// 只要 WebSocket 连接处于打开状态,就继续接收消息
while (webSocket.State == WebSocketState.Open)
{
// 接收消息
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

// 如果收到的消息是文本类型
if (result.MessageType == WebSocketMessageType.Text)
{
// 将字节转换为字符串
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);

// 打印收到的消息
Console.WriteLine($"Received: {message}");

// 将收到的消息发送回客户端
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
}
// 如果收到的消息是关闭连接的类型
else if (result.MessageType == WebSocketMessageType.Close)
{
// 关闭 WebSocket 连接
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
}
}

广播消息

在项目中,如果有将客户端发来的消息推送给服务端,广播方式也是一种很好的选择

Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 开启ws中间件支持
app.UseWebSockets();

app.MapControllers();

#region ws

// 使用中间件管道过滤ws请求
app.Map("/ws/{sid}", async (context) =>
{
// 如果为ws请求
if (context.WebSockets.IsWebSocketRequest)
{
// 从路径中获取客户端id
var sid = context.Request.RouteValues["sid"] as string;

// 从容器中获取到WebSocketService 工具类
var webSocketService = context.RequestServices.GetRequiredService<WebSocketService>();

// 允许与客户端建立ws链接
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();

// 调用工具类中的HandleWebSocketConnection执行ws请求的处理逻辑
await webSocketService.HandleWebSocketConnection(webSocket, sid, context.RequestAborted);
}
else
{
// 返回请求失败
context.Response.StatusCode = 400; // Bad Request
}
});

#endregion

app.Run();
webSocketService工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class WebSocketService
{
/// <summary>
/// 存放会话对象
/// </summary>
private static ConcurrentDictionary<string, WebSocket> sessionMap = new ConcurrentDictionary<string, WebSocket>();

/// <summary>
/// 连接建立成功调用的方法
/// </summary>
/// <param name="webSocket"></param>
/// <param name="sid"></param>
public async Task OnOpen(WebSocket webSocket, string sid)
{
Console.WriteLine($"客户端:{sid} 建立连接");
sessionMap.TryAdd(sid, webSocket); // 将客户端和请求保存到集合中
}

/// <summary>
/// 收到客户端消息后调用的方法
/// </summary>
/// <param name="webSocket"></param>
/// <param name="message"></param>
/// <param name="sid"></param>
public async Task OnMessage(WebSocket webSocket, string message, string sid)
{
Console.WriteLine($"收到来自客户端:{sid} 的信息: {message}");
}

/// <summary>
/// 连接关闭调用的方法
/// </summary>
/// <param name="sid"></param>
public void OnClose(string sid)
{
// 如果成功的将客户端从集合中移除
if (sessionMap.TryRemove(sid, out _))
{
Console.WriteLine($"连接断开: {sid}");
}
}

/// <summary>
/// 群发消息
/// </summary>
/// <param name="message"></param>
public async Task SendToAllClients(string message)
{
var buffer = Encoding.UTF8.GetBytes(message);
foreach (var webSocket in sessionMap.Values)
{
if (webSocket.State == WebSocketState.Open)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true,
CancellationToken.None);
}
}
}


/// <summary>
/// 处理 WebSocket 连接
/// </summary>
/// <param name="webSocket"></param>
/// <param name="sid"></param>
/// <param name="cancellationToken"></param>
public async Task HandleWebSocketConnection(WebSocket webSocket, string sid, CancellationToken cancellationToken)
{
await OnOpen(webSocket, sid);

var buffer = new byte[1024 * 4];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
if (result.MessageType == WebSocketMessageType.Text)
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
await OnMessage(webSocket, message, sid);
}
else if (result.MessageType == WebSocketMessageType.Close)
{
OnClose(sid);
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
}
}
}


WebAPI-WebSocket
http://blog.170827.xyz/2024/11/29/WebAPI-WebSocket/
作者
XIAOBAI
发布于
2024年11月29日
许可协议