Asp.net Core中如何使用中間件來(lái)管理websocket
介紹
我喜歡.NET CORE 這個(gè)東西,其實(shí)不僅僅源于它性能很高,可以跨平臺(tái),還因?yàn)樗脑O(shè)計(jì)模式確實(shí)令人著迷。以前沒(méi).NET CORE 的時(shí)候,.NET用websocket必須跑在windows server 2012上,但我一般不會(huì)這么干,都把websocket架在nodejs的服務(wù)器上。這么分出來(lái),麻煩肯定是麻煩的,而且js這東西,寫(xiě)復(fù)雜和幾年后再看都是頭疼的問(wèn)題。那么,如果.NET CORE是以kestrel運(yùn)行的,那么就不再需要考慮服務(wù)器的版本運(yùn)行,任何一個(gè)地方都可以用websocket
ASP.NET Core SignalR是一個(gè)有用的庫(kù),可以簡(jiǎn)化Web應(yīng)用程序中實(shí)時(shí)通信的管理。但是,我寧愿使用WebSockets,因?yàn)槲蚁胍`活,并且與任何WebSocket客戶端兼容。
在Microsoft的文檔中,我找到了一個(gè)很好的WebSockets工作示例。它仍然是管理連接,以便能夠從一個(gè)連接向其他連接廣播消息,這是SignalR開(kāi)箱即用的功能。期望這個(gè)邏輯非常復(fù)雜,我想從Startup類(lèi)中刪除它。
背景
要閱讀ASP.NET Core中的WebSockets支持,可以在此處查看。如果您想了解中間件以及如何在ASP.NET Core中編寫(xiě)它,請(qǐng)閱讀此鏈接。
代碼使用
首先,你必須添加 Microsoft.AspNetCore.WebSockets
包到你的項(xiàng)目。
現(xiàn)在,您可以創(chuàng)建一個(gè)擴(kuò)展方法和類(lèi)來(lái)管理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請(qǐng)求在URL中始終包含“/ ws”。查詢字符串包含用于將WebSocket與登錄用戶相關(guān)聯(lián)的用戶名的參數(shù)u。
CustomWebSocket是一個(gè)包含WebSocket和用戶名的類(lèi):
public class CustomWebSocket { public WebSocket WebSocket { get; set; } public string Username { get; set; } }
我也創(chuàng)建了自定義WebSocket消息:
class CustomWebSocketMessage { public string Text { get; set; } public DateTime MessagDateTime { get; set; } public string Username { get; set; } public WSMessageType Type { get; set; } }
其中Type是您可能擁有的不同類(lèi)型消息的枚舉。
在Startup類(lèi)中,您必須注冊(cè)以下服務(wù):
services.AddSingleton<ICustomWebSocketFactory, CustomWebSocketFactory>(); services.AddSingleton<ICustomWebSocketMessageHandler, CustomWebSocketMessageHandler>();
CustomWebSocketFactory負(fù)責(zé)收集連接的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包含有關(guān)消息的邏輯(即在連接時(shí)需要發(fā)送任何消息以及如何對(duì)傳入消息作出反應(yīng))
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類(lèi)中添加以下內(nèi)容:
var webSocketOptions = new WebSocketOptions() { KeepAliveInterval = TimeSpan.FromSeconds(120), ReceiveBufferSize = 4 * 1024 }; app.UseWebSockets(webSocketOptions); app.UseCustomWebSocketManager();
通過(guò)這種方式,Starup類(lèi)保持干凈,管理WebSockets的邏輯可以擴(kuò)展,使您可以根據(jù)自己的喜好靈活地組織它。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Asp.Net程序目錄下文件夾或文件操作導(dǎo)致Session失效的解決方案
這篇文章主要介紹了Asp.Net程序目錄下文件夾或文件操作導(dǎo)致Session失效的解決方案,需要的朋友可以參考下2017-06-06asp.net+xml+flash實(shí)現(xiàn)的圖片展示效果示例
這篇文章主要介紹了asp.net+xml+flash實(shí)現(xiàn)的圖片展示效果的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了圖片展示效果的相關(guān)操作步驟與flash與xml調(diào)用的相關(guān)技巧,需要的朋友可以參考下2016-08-08解決iis7.5服務(wù)器上.net 獲取不到https頁(yè)面的信息
讓我糾結(jié)了一天多的問(wèn)題,給大家看下,有相同情況的可以不用浪費(fèi)時(shí)間了,本人當(dāng)時(shí)找了好半天都沒(méi)找到什么有用的信息,項(xiàng)目在本地沒(méi)有問(wèn)題,但部署在服務(wù)器后,獲取不到https頁(yè)面的信息,加入下面的代碼就可以了,因?yàn)閕is7.5的安全協(xié)議比較高的原因。2014-06-06基于.Net?Core認(rèn)證授權(quán)方案之JwtBearer認(rèn)證
這篇文章介紹了基于.Net?Core認(rèn)證授權(quán)方案之JwtBearer認(rèn)證,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06調(diào)試ASP.NET2005/2008時(shí),端口不正確的解決三套方案
這篇文章主要介紹了調(diào)試ASP.NET2005/2008時(shí),端口不正確的解決三套方案,小編就特別喜歡收藏這類(lèi)文章,方便以后工作學(xué)習(xí)中遇到這類(lèi)問(wèn)題進(jìn)行解決。2015-09-09模擬HTTP請(qǐng)求實(shí)現(xiàn)網(wǎng)頁(yè)自動(dòng)操作及數(shù)據(jù)采集的方法
下面小編就為大家?guī)?lái)一篇模擬HTTP請(qǐng)求實(shí)現(xiàn)網(wǎng)頁(yè)自動(dòng)操作及數(shù)據(jù)采集的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03