WebAPI-WebSocket
WebAPI-WebSocket
HTTP是短链接
WebSocket是长连接
HTTP通勤是单向的,基于请求响应模式
WebSocket支持双向通信
HTTP和WebSocket底层都是TCP链接
处理请求
在ASP.NETCore中,我们甚至可以直接使用控制器来处理WS请求,但这并不符合设计规范,所以我们使用第二种方法——中间件处理Ws请求
// 使用 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
// 开启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工具类
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);
}
}
}