"接收設備端傳送過來的數據並顯示在頁面上"
例如空調系統設備傳送當前溫度
後端使用 ASP.NET Core 2.0 webapi 開發
以前有在 .Net Framework 上使用過 ASP.NET SignalR
查資料得知目前(寫這篇筆記的時候) .Net Core 版本的 SingalR 還在開發階段
2.1 版本才會發行正式版本,有些文章建議不適合在正式產品中使用開發版本
於是就查了如何在 .Net Core 使用 WebSocket
#1 [ASP.NET Core: Building chat room using WebSocket]
http://gunnarpeipman.com/aspnet/aspnet-core-websocket-chat/
上面這篇文章使用網頁發送和接收訊息
但是目前專案需求是要由設備發送訊息
#2 [Real-time chart using ASP.NET Core and WebSocket]
http://gunnarpeipman.com/aspnet/aspnet-core-websocket-chart/
上面這篇文章使用api當端點給設備端呼叫,符合專案的需求
以上兩種發送訊息的方式都想要使用,於是整合兩篇文章
使用 #1 的 SocketMiddleware 和 # 2 的 SocketManager
#2 的 SocketManager 不需修改, #1 產生 socketId 和保存當前 socket 的程式改使用 # 2 的 SocketManaer
public class ACSystemSocketMiddleware { private readonly RequestDelegate _next; private readonly ACSystemSocketManager _socketManager; public ChatWebSocketMiddleware( RequestDelegate next, ACSystemSocketManager socketManager) { _next = next; _socketManager = socketManager; } public async Task Invoke(HttpContext context) { if (!context.WebSockets.IsWebSocketRequest) { await _next.Invoke(context); return; } CancellationToken ct = context.RequestAborted; WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync(); var socketId = _socketManager.AddSocket(currentSocket); while (true) { if (ct.IsCancellationRequested) { break; } var response = await ReceiveStringAsync(currentSocket, ct); if (string.IsNullOrEmpty(response)) { if (currentSocket.State != WebSocketState.Open) { break; } continue; } foreach (var socket in _socketManager.GetAll()) { if (socket.Value.State != WebSocketState.Open) { continue; } await SendStringAsync(socket.Value, response, ct); } } await _socketManager.RemoveSocket(socketId); 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(); } } } }
後續 :
整合過的程式執行起來正常,但是接收到的中文字串變問號,檢查後發現問題出在 #2 的 SocketManager,發送訊息(SendMessageAsync)使用的 Encoding 是ASCII,改為UTF8或是Default以後可以正常顯示中文,但是序列化成JSON格式的字串卻少了最後一個大括號,調整成下面這段程式碼以後就正常了
private async Task SendMessageAsync(WebSocket socket, string message) { if (socket.State != WebSocketState.Open) return; var buffer = Encoding.UTF8.GetBytes(message); var segment = new ArraySegment<byte>(buffer); await socket.SendAsync( segment, WebSocketMessageType.Text, true, CancellationToken.None); }
沒有留言:
張貼留言