Asp.net Core中如何使用中间件来管理websocket

介绍

我喜欢.NET CORE 这个东西,其实不仅仅源于它性能很高,可以跨平台,还因为它的设计模式确实令人着迷。以前没.NET CORE 的时候,.NET用websocket必须跑在windows server 2012上,但我一般不会这么干,都把websocket架在nodejs的服务器上。这么分出来,麻烦肯定是麻烦的,而且js这东西,写复杂和几年后再看都是头疼的问题。那么,如果.NET CORE是以kestrel运行的,那么就不再需要考虑服务器的版本运行,任何一个地方都可以用websocket

ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理。但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容。

在Microsoft的文档中,我找到了一个很好的WebSockets工作示例。它仍然是管理连接,以便能够从一个连接向其他连接广播消息,这是SignalR开箱即用的功能。期望这个逻辑非常复杂,我想从Startup类中删除它。

背景

要阅读ASP.NET Core中的WebSockets支持,可以在此处查看。如果您想了解中间件以及如何在ASP.NET Core中编写它,请阅读此链接。

代码使用

首先,你必须添加 Microsoft.AspNetCore.WebSockets 包到你的项目。

现在,您可以创建一个扩展方法和类来管理WebSockets:

public static class WebSocketExtensions

{

public static IApplicationBuilder UseCustomWebSocketManager(this IApplicationBuilder app)

{

return app.UseMiddleware<CustomWebSocketManager>();

}

}

public class CustomWebSocketManager

{

private readonly RequestDelegate _next;

public CustomWebSocketManager(RequestDelegate next)

{

_next = next;

}

public async Task Invoke(HttpContext context, ICustomWebSocketFactory wsFactory, ICustomWebSocketMessageHandler wsmHandler)

{

if (context.Request.Path == "/ws")

{

if (context.WebSockets.IsWebSocketRequest)

{

string username = context.Request.Query["u"];

if (!string.IsNullOrEmpty(username))

{

WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();

CustomWebSocket userWebSocket = new CustomWebSocket()

{

WebSocket = webSocket,

Username = username

};

wsFactory.Add(userWebSocket);

await wsmHandler.SendInitialMessages(userWebSocket);

await Listen(context, userWebSocket, wsFactory, wsmHandler);

}

}

else

{

context.Response.StatusCode = 400;

}

}

await _next(context);

}

private async Task Listen(HttpContext context, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory, ICustomWebSocketMessageHandler wsmHandler)

{

WebSocket webSocket = userWebSocket.WebSocket;

var buffer = new byte[1024 * 4];

WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

while (!result.CloseStatus.HasValue)

{

await wsmHandler.HandleMessage(result, buffer, userWebSocket, wsFactory);

buffer = new byte[1024 * 4];

result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

}

wsFactory.Remove(userWebSocket.Username);

await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);

}

}

在这种情况下,WebSockets请求在URL中始终包含“/ ws”。查询字符串包含用于将WebSocket与登录用户相关联的用户名的参数u。

CustomWebSocket是一个包含WebSocket和用户名的类:

public class CustomWebSocket

{

public WebSocket WebSocket { get; set; }

public string Username { get; set; }

}

我也创建了自定义WebSocket消息:

class CustomWebSocketMessage

{

public string Text { get; set; }

public DateTime MessagDateTime { get; set; }

public string Username { get; set; }

public WSMessageType Type { get; set; }

}

其中Type是您可能拥有的不同类型消息的枚举。

在Startup类中,您必须注册以下服务:

services.AddSingleton<ICustomWebSocketFactory, CustomWebSocketFactory>();

services.AddSingleton<ICustomWebSocketMessageHandler, CustomWebSocketMessageHandler>();

CustomWebSocketFactory负责收集连接的WebSockets列表:

public interface ICustomWebSocketFactory

{

void Add(CustomWebSocket uws);

void Remove(string username);

List<CustomWebSocket> All();

List<CustomWebSocket> Others(CustomWebSocket client);

CustomWebSocket Client(string username);

}

public class CustomWebSocketFactory : ICustomWebSocketFactory

{

List<CustomWebSocket> List;

public CustomWebSocketFactory()

{

List = new List<CustomWebSocket>();

}

public void Add(CustomWebSocket uws)

{

List.Add(uws);

}

//when disconnect

public void Remove(string username)

{

List.Remove(Client(username));

}

public List<CustomWebSocket> All()

{

return List;

}

public List<CustomWebSocket> Others(CustomWebSocket client)

{

return List.Where(c => c.Username != client.Username).ToList();

}

public CustomWebSocket Client(string username)

{

return List.First(c=>c.Username == username);

}

}

CustomWebSocketMessageHandler包含有关消息的逻辑(即在连接时需要发送任何消息以及如何对传入消息作出反应)

public interface ICustomWebSocketMessageHandler

{

Task SendInitialMessages(CustomWebSocket userWebSocket);

Task HandleMessage(WebSocketReceiveResult result, byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);

Task BroadcastOthers(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);

Task BroadcastAll(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);

}

public class CustomWebSocketMessageHandler : ICustomWebSocketMessageHandler

{

public async Task SendInitialMessages(CustomWebSocket userWebSocket)

{

WebSocket webSocket = userWebSocket.WebSocket;

var msg = new CustomWebSocketMessage

{

MessagDateTime = DateTime.Now,

Type = WSMessageType.anyType,

Text = anyText,

Username = "system"

};

string serialisedMessage = JsonConvert.SerializeObject(msg);

byte[] bytes = Encoding.ASCII.GetBytes(serialisedMessage);

await webSocket.SendAsync(new ArraySegment<byte>(bytes, 0, bytes.Length), WebSocketMessageType.Text, true, CancellationToken.None);

}

public async Task HandleMessage(WebSocketReceiveResult result, byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)

{

string msg = Encoding.ASCII.GetString(buffer);

try

{

var message = JsonConvert.DeserializeObject<CustomWebSocketMessage>(msg);

if (message.Type == WSMessageType.anyType)

{

await BroadcastOthers(buffer, userWebSocket, wsFactory);

}

}

catch (Exception e)

{

await userWebSocket.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

}

}

public async Task BroadcastOthers(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)

{

var others = wsFactory.Others(userWebSocket);

foreach (var uws in others)

{

await uws.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);

}

}

public async Task BroadcastAll(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)

{

var all = wsFactory.All();

foreach (var uws in all)

{

await uws.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);

}

}

}

最后,在Configure方法的Startup类中添加以下内容:

var webSocketOptions = new WebSocketOptions()

{

KeepAliveInterval = TimeSpan.FromSeconds(120),

ReceiveBufferSize = 4 * 1024

};

app.UseWebSockets(webSocketOptions);

app.UseCustomWebSocketManager();

通过这种方式,Starup类保持干净,管理WebSockets的逻辑可以扩展,使您可以根据自己的喜好灵活地组织它。

总结

以上是 Asp.net Core中如何使用中间件来管理websocket 的全部内容, 来源链接: utcz.com/z/328874.html

回到顶部