WebSocket In ASP.NET Core(二)
Introduce
? 上篇博文中,介紹了WebSocket的基本原理,以及一個簡單的Demo用來對其有一個大致的認識。這篇博文講的是我們平常在網站上可能會經常遇到的——實時聊天,本文就是來講在.NET-Core使用WebSocket來實現一個“乞丐版”的在線實時聊天Demo。
關鍵詞:Middleware,Real-Time,WebSocket
Before You Read.
?這個和我們上一篇博文中Demo?有何不同的呢?有何共同之處呢?
?相同的點是,都是網頁作為客戶端,使用JavaScript來發送和接收請求,.NET-Core?服務端接收到請求,發送該請求給客戶端。
不同的地方呢,就像我下面這張圖這樣:
一次同時有多個客戶端在,所以應該很清楚,我們只要在上面例子的基礎上,對當前的已存在的Socket進行輪詢,發送回應即可達到我們想要的效果。
Create WebSocket Middleware
?在上個Demo的例子中,我們直接在Startup?的Configure?中直接寫接受WebSocket請求。這次我們換成Middleware形式來處理。在寫Middleware之前,在之前的介紹中,我們知道,需要有多個WebSocket,那么肯定需要一些對WebSocket?的Get/Set?處理。我簡單的寫了一個下面WebScoket Manger Class
//WebSocketManager.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketManage
{
? ? public class WSConnectionManager
? ? {
? ? ? ? private static ConcurrentDictionary<string, WebSocket> _socketConcurrentDictionary = new ConcurrentDictionary<string, WebSocket>();
? ? ? ? public void AddSocket(WebSocket socket)
? ? ? ? {
? ? ? ? ? ? _socketConcurrentDictionary.TryAdd(CreateGuid(), socket);
? ? ? ? }
? ? ? ? public async Task RemoveSocket(WebSocket socket)
? ? ? ? {
? ? ? ? ? ? _socketConcurrentDictionary.TryRemove(GetSocketId(socket), out WebSocket aSocket);
? ? ? ? ? ? await aSocket.CloseAsync(
? ? ? ? ? ? ? ? closeStatus: WebSocketCloseStatus.NormalClosure,
? ? ? ? ? ? ? ? statusDescription: "Close by User",
? ? ? ? ? ? ? ? cancellationToken: CancellationToken.None).ConfigureAwait(false);
? ? ? ? }
? ? ? ? public string GetSocketId(WebSocket socket)
? ? ? ? {
? ? ? ? ? ? return _socketConcurrentDictionary.FirstOrDefault(k => k.Value == socket).Key;
? ? ? ? }
? ? ? ? public ConcurrentDictionary<string, WebSocket> GetAll()
? ? ? ? {
? ? ? ? ? ? return _socketConcurrentDictionary;
? ? ? ? }
? ? ? ? public string CreateGuid()
? ? ? ? {
? ? ? ? ? ? return Guid.NewGuid().ToString();
? ? ? ? }
? ? }
}
上面主要是對WebSocket?進行了簡單的存取操作進行了封裝。下面也把WebSocket?的Send?和Recieve?操作進行了封裝。
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketManage
{
? ? public class WSHandler
? ? {
? ? ? ? protected WSConnectionManager _wsConnectionManager;
? ? ? ? public WSHandler(WSConnectionManager wSConnectionManager)
? ? ? ? {
? ? ? ? ? ? _wsConnectionManager = wSConnectionManager;
? ? ? ? }
? ? ? ? public async Task SendMessageAsync(
? ? ? ? ? ? WebSocket socket,
? ? ? ? ? ? string message,
? ? ? ? ? ? CancellationToken cancellationToken = default(CancellationToken))
? ? ? ? {
? ? ? ? ? ? var buffer = Encoding.UTF8.GetBytes(message);
? ? ? ? ? ? var segment = new ArraySegment<byte>(buffer);
? ? ? ? ? ? await socket.SendAsync(segment, WebSocketMessageType.Text, true, cancellationToken);
? ? ? ? }
? ? ? ? public async Task<string> RecieveAsync(WebSocket webSocket, CancellationToken cancellationToken)
? ? ? ? {
? ? ? ? ? ? var buffer = new ArraySegment<byte>(new byte[1024 * 8]);
? ? ? ? ? ? using (var ms = new MemoryStream())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? WebSocketReceiveResult result;
? ? ? ? ? ? ? ? do
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? cancellationToken.ThrowIfCancellationRequested();
? ? ? ? ? ? ? ? ? ? result = await webSocket.ReceiveAsync(buffer, cancellationToken);
? ? ? ? ? ? ? ? ? ? ms.Write(buffer.Array, buffer.Offset, result.Count);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? while (!result.EndOfMessage);
? ? ? ? ? ? ? ? ms.Seek(0, SeekOrigin.Begin);
? ? ? ? ? ? ? ? if (result.MessageType != WebSocketMessageType.Text)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? using (var reader = new StreamReader(ms, Encoding.UTF8))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? return await reader.ReadToEndAsync();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
有了上面兩個輔助類之后,接下來就可以寫我們自己的RealTimeWebSocketMiddlerware 了,
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocketManage;
namespace Robert.Middleware.WebSockets
{
? ? public class RealTimeWSMiddleware
? ? {
? ? ? ? private readonly RequestDelegate _next;
? ? ? ? private WSConnectionManager _wSConnectionManager { get; set; }
? ? ? ? private WSHandler _wsHanlder { get; set; }
? ? ? ? public RealTimeWSMiddleware(
? ? ? ? ? ? RequestDelegate next,
? ? ? ? ? ? WSConnectionManager wSConnectionManager,
? ? ? ? ? ? WSHandler wsHandler)
? ? ? ? {
? ? ? ? ? ? _next = next;
? ? ? ? ? ? _wSConnectionManager = wSConnectionManager;
? ? ? ? ? ? _wsHanlder = wsHandler;
? ? ? ? }
? ? ? ? public async Task Invoke(HttpContext httpContext)
? ? ? ? {
? ? ? ? ? ? if (httpContext.WebSockets.IsWebSocketRequest)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? var cancellationToken = httpContext.RequestAborted;
? ? ? ? ? ? ? ? var currentWebSocket = await httpContext.WebSockets.AcceptWebSocketAsync();
? ? ? ? ? ? ? ? _wSConnectionManager.AddSocket(currentWebSocket);
? ? ? ? ? ? ? ? while (true)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if (cancellationToken.IsCancellationRequested) break;
? ? ? ? ? ? ? ? ? ? var response = await _wsHanlder.ReceiveAsync(currentWebSocket, cancellationToken);
? ? ? ? ? ? ? ? ? ? if (string.IsNullOrEmpty(response) && currentWebSocket.State != WebSocketState.Open) break;
? ? ? ? ? ? ? ? ? ? foreach (var item in _wSConnectionManager.GetAll())
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? if (item.Value.State == WebSocketState.Open)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? await _wsHanlder.SendMessageAsync(item.Value, response, cancellationToken);
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? await _wSConnectionManager.RemoveSocket(currentWebSocket);
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? await _next(httpContext);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
有了上面兩個輔助類之后,接下來就可以寫我們自己的RealTimeWebSocketMiddlerware?了,
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocketManage;
namespace Robert.Middleware.WebSockets
{
? ? public class RealTimeWSMiddleware
? ? {
? ? ? ? private readonly RequestDelegate _next;
? ? ? ? private WSConnectionManager _wSConnectionManager { get; set; }
? ? ? ? private WSHandler _wsHanlder { get; set; }
? ? ? ? public RealTimeWSMiddleware(
? ? ? ? ? ? RequestDelegate next,
? ? ? ? ? ? WSConnectionManager wSConnectionManager,
? ? ? ? ? ? WSHandler wsHandler)
? ? ? ? {
? ? ? ? ? ? _next = next;
? ? ? ? ? ? _wSConnectionManager = wSConnectionManager;
? ? ? ? ? ? _wsHanlder = wsHandler;
? ? ? ? }
? ? ? ? public async Task Invoke(HttpContext httpContext)
? ? ? ? {
? ? ? ? ? ? if (httpContext.WebSockets.IsWebSocketRequest)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? var cancellationToken = httpContext.RequestAborted;
? ? ? ? ? ? ? ? var currentWebSocket = await httpContext.WebSockets.AcceptWebSocketAsync();
? ? ? ? ? ? ? ? _wSConnectionManager.AddSocket(currentWebSocket);
? ? ? ? ? ? ? ? while (true)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if (cancellationToken.IsCancellationRequested) break;
? ? ? ? ? ? ? ? ? ? var response = await _wsHanlder.ReceiveAsync(currentWebSocket, cancellationToken);
? ? ? ? ? ? ? ? ? ? if (string.IsNullOrEmpty(response) && currentWebSocket.State != WebSocketState.Open) break;
? ? ? ? ? ? ? ? ? ? foreach (var item in _wSConnectionManager.GetAll())
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? if (item.Value.State == WebSocketState.Open)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? await _wsHanlder.SendMessageAsync(item.Value, response, cancellationToken);
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? await _wSConnectionManager.RemoveSocket(currentWebSocket);
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? await _next(httpContext);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
?其實到這里,核心部分已經講完了,接下來就是頁面顯示,發現信息,交互的問題了。在客戶端還是像上篇文章中的一樣,直接使用?JavaScript發送WebScoket請求。
?下面主要演示一下效果,在上篇博文的基礎上,加上了用戶名。
<script>$(function () {var protocol = location.protocol === "https:" ? "wss:" : "ws:";var Uri = protocol + "//" + window.location.host + "/ws";var socket = new WebSocket(Uri);socket.onopen = e => {console.log("socket opened", e);};socket.onclose = function (e) {console.log("socket closed", e);};//function to receive from server.socket.onmessage = function (e) {console.log("Message:" + e.data);$('#msgs').append(e.data + '<br />');};socket.onerror = function (e) {console.error(e.data);};});</script>當寫好了頁面文件后,運行站點。最終運行的效果圖,界面以實用為主,不求美觀。
總結
以上是生活随笔為你收集整理的WebSocket In ASP.NET Core(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: asp.net core Authent
- 下一篇: ASP.NET Core 运行原理剖析