
1.創建簡易通訊協議
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Dw.RegApi.Models { /// <summary> /// 通訊協議 /// </summary> public class MsgTemplate { /// <summary> /// 接收人 /// </summary> public string to_id { get; set; } /// <summary> /// 發送人 /// </summary> public string from_id { get; set; } /// <summary> /// 發送人昵稱 /// </summary> public string from_username { get; set; } /// <summary> /// 發送人頭像 /// </summary> public string from_userpic { get; set; } /// <summary> /// 發送類型 text,voice等等 /// </summary> public string type { get; set; } /// <summary> /// 發送內容 /// </summary> public string data { get; set; } /// <summary> /// 接收到的時間 /// </summary> public long time { get; set; } } }
2.添加中間件ChatWebSocketMiddleware
using Dw.Util.Helper; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Dw.RegApi.Models { /// <summary> /// WebSocket中間件ChatWebSocketMiddleware /// </summary> public class ChatWebSocketMiddleware { private static ConcurrentDictionary<string,WebSocket> _sockets = new ConcurrentDictionary<string,WebSocket>(); private readonly RequestDelegate _next; /// <summary> /// WebSocket中間件 /// </summary> /// <param name="next"></param> public ChatWebSocketMiddleware(RequestDelegate next) { _next = next; } /// <summary> /// 執行 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task Invoke(HttpContext context) { if (!context.WebSockets.IsWebSocketRequest) { await _next.Invoke(context); return; } CancellationToken ct = context.RequestAborted; var currentSocket = await context.WebSockets.AcceptWebSocketAsync();
string socketId_Expand = Guid.NewGuid().ToString();
string socketId = context.Request.Query["sid"].ToString()+ socketId_Expand;
if (!_sockets.ContainsKey(socketId)) { _sockets.TryAdd(socketId, currentSocket); } //_sockets.TryRemove(socketId, out dummy); //_sockets.TryAdd(socketId, currentSocket); while (true) { if (ct.IsCancellationRequested) { break; } string response = await ReceiveStringAsync(currentSocket, ct); NLogHelper.WriteInfo("WebSocket發送消息:" + response); MsgTemplate msg = JsonConvert.DeserializeObject<MsgTemplate>(response); if (string.IsNullOrEmpty(response)) { if (currentSocket.State != WebSocketState.Open) { break; } continue; } foreach (var socket in _sockets) { if (socket.Value.State != WebSocketState.Open) { continue; } //控制只有接收者才能收到消息,並且用戶所有的客戶端都可以同步收到 if (socket.Key.Contains(msg.to_id)) { await SendStringAsync(socket.Value, JsonConvert.SerializeObject(msg), ct); } } } //_sockets.TryRemove(socketId, out dummy); await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct); currentSocket.Dispose(); } private static Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default(CancellationToken)) { var buffer = Encoding.UTF8.GetBytes(data); var segment = new ArraySegment<byte>(buffer); return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct); } private static async Task<string> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default(CancellationToken)) { var buffer = new ArraySegment<byte>(new byte[8192]); using (var ms = new MemoryStream()) { WebSocketReceiveResult result; do { ct.ThrowIfCancellationRequested(); result = await socket.ReceiveAsync(buffer, ct); 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(); } } } } }
3.在Startup.cs中使用中間件
//WebSocket中間件 var webSocketOptions = new WebSocketOptions() { //KeepAliveInterval - 向客戶端發送“ping”幀的頻率,以確保代理保持連接處於打開狀態。 默認值為 2 分鍾。 KeepAliveInterval = TimeSpan.FromSeconds(120), //ReceiveBufferSize - 用於接收數據的緩沖區的大小。 高級用戶可能需要對其進行更改,以便根據數據大小調整性能。 默認值為 4 KB。 ReceiveBufferSize = 4 * 1024 }; app.UseWebSockets(webSocketOptions); app.UseMiddleware<ChatWebSocketMiddleware>();
相關文檔:
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1
