public static void OnReceiveAsync(HttpContext context, WebSocket webSocket, WebSocketReceiveResult?wsResult, string bufferStr) { DashboardClient?client = null; lock (DashboardClient.g_clients) // find client from the same IP, assuming connection in the last 1000ms { client = DashboardClient.g_clients.Find(r => r.WsWebSocket == webSocket); } if (client != null) { var semicolonInd = bufferStr.IndexOf(':'); string msgCode = bufferStr.Substring(0, semicolonInd); string msgObjStr = bufferStr.Substring(semicolonInd + 1); bool isHandled = client.OnReceiveWsAsync_MktHealth(wsResult, msgCode, msgObjStr); if (!isHandled) { isHandled = client.OnReceiveWsAsync_BrAccViewer(wsResult, msgCode, msgObjStr); } if (!isHandled) { isHandled = client.OnReceiveWsAsync_QckflNews(wsResult, msgCode, msgObjStr); } if (!isHandled) { // throw new Exception($"Unexpected websocket received msgCode '{msgCode}'"); Utils.Logger.Error($"Unexpected websocket received msgCode '{msgCode}'"); } } }
public bool OnReceiveWsAsync_BrAccViewer(WebSocketReceiveResult?wsResult, string msgCode, string msgObjStr) { switch (msgCode) { case "BrAccViewer.ChangeLookback": Utils.Logger.Info("OnReceiveWsAsync_BrAccViewer(): changeLookback"); // m_lastLookbackPeriodStr = msgObjStr; // SendHistoricalWs(); return(true); case "BrAccViewer.ChangeNav": Utils.Logger.Info($"OnReceiveWsAsync_BrAccViewer(): changeNav to '{msgObjStr}'"); // DC.IM string sqTicker = "N/" + msgObjStr; // turn DC.IM to N/DC.IM m_braccSelectedNavAsset = MemDb.gMemDb.AssetsCache.GetAsset(sqTicker) as BrokerNav; BrAccViewerSendSnapshotAndHist(); // var navAsset = MemDb.gMemDb.AssetsCache.GetAsset(sqTicker); // RtMktSummaryStock? navStock = m_mktSummaryStocks.FirstOrDefault(r => r.SqTicker == g_brNavVirtualTicker); // if (navStock != null) // navStock.AssetId = navAsset!.AssetId; // SendHistoricalWs(); // SendRealtimeWs(); return(true); default: return(false); } }
public static void OnReceiveAsync(HttpContext context, WebSocket webSocket, WebSocketReceiveResult?wsResult, string bufferStr) { var semicolonInd = bufferStr.IndexOf(':'); string msgCode = bufferStr.Substring(0, semicolonInd); string msgObjStr = bufferStr.Substring(semicolonInd + 1); if (msgCode == "startStreamingCode") { _ = Task.Run(async() => // do CPU-bound work on a ThreadPool background thread. '_' is a discard variable in C# 7.0 { for (int i = 0; i < 15; i++) { string msg = "priceQuoteFromServerCode:" + "TSLA price is: $" + (new Random().NextDouble() * 1000.0).ToString("0.00"); var encodedMsg = Encoding.UTF8.GetBytes(msg); if (webSocket !.State == WebSocketState.Open) { await webSocket.SendAsync(new ArraySegment <Byte>(encodedMsg, 0, encodedMsg.Length), WebSocketMessageType.Text, true, CancellationToken.None); } Thread.Sleep(2000); } }); } else { string msg = "msgCode1:" + $"simple messageReceived:'{bufferStr}'"; byte[] encodedMsg = Encoding.UTF8.GetBytes(msg); if (webSocket !.State == WebSocketState.Open) { webSocket.SendAsync(new ArraySegment <Byte>(encodedMsg, 0, encodedMsg.Length), WebSocketMessageType.Text, true, CancellationToken.None); } } }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, AppDbContext appDbContext) { appDbContext.Database.EnsureCreated(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseWebSockets(new WebSocketOptions { ReceiveBufferSize = 4 * 1024 }); app.Use(async(context, next) => { // 如果Request為WebSocket且路徑爲/watchDog if (context.WebSockets.IsWebSocketRequest && context.Request.Path == "/watchDog") { // 容許WebSocket連線並取得WebSocket實例 var webSocket = await context.WebSockets.AcceptWebSocketAsync(); while (webSocket.State == WebSocketState.Open) { WebSocketReceiveResult?receivedData = null; // 接收一次訊息中的所有段落 do { // 接收緩衝區 ArraySegment <byte> buffer = new ArraySegment <byte>(new byte[4 * 1024]); // 接收 receivedData = await webSocket.ReceiveAsync(buffer, CancellationToken.None); } while (!receivedData.EndOfMessage); // 是否為最後的段落 } } else { await next(); } }); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); }
public bool OnReceiveWsAsync_QckflNews(WebSocketReceiveResult? wsResult, string msgCode, string msgObjStr) { switch (msgCode) { case "ReloadQuickfolio": Utils.Logger.Info("OnReceiveWsAsync_QckflNews(): ReloadQuickfolio"); ReloadQuickfolioMsgArrived(); return true; default: return false; } }
public async Task ReceiveAsync( PipeWriter writer, CancellationToken cancellationToken) { WebSocket?webSocket = _webSocket; if (_disposed || webSocket == null) { return; } try { WebSocketReceiveResult?socketResult = null; do { Memory <byte> memory = writer.GetMemory(_maxMessageSize); bool success = MemoryMarshal.TryGetArray(memory, out ArraySegment <byte> buffer); if (success) { try { socketResult = await webSocket.ReceiveAsync(buffer, cancellationToken); if (socketResult.Count == 0) { break; } writer.Advance(socketResult.Count); } catch { break; } FlushResult result = await writer.FlushAsync(cancellationToken); if (result.IsCompleted) { break; } } } while (socketResult == null || !socketResult.EndOfMessage); } catch (ObjectDisposedException) { // we will just stop receiving } }
public async Task ReceiveAsync( PipeWriter writer, CancellationToken cancellationToken = default) { if (IsClosed) { return; } try { WebSocketReceiveResult?socketResult = null; do { Memory <byte> memory = writer.GetMemory(_maxMessageSize); if (MemoryMarshal.TryGetArray(memory, out ArraySegment <byte> buffer)) { try { socketResult = await _socketClient.Socket .ReceiveAsync(buffer, cancellationToken) .ConfigureAwait(false); if (socketResult.Count == 0) { break; } writer.Advance(socketResult.Count); } catch { break; } FlushResult result = await writer .FlushAsync(cancellationToken) .ConfigureAwait(false); if (result.IsCompleted) { break; } } } while (socketResult == null || !socketResult.EndOfMessage); } catch (ObjectDisposedException) { // we will just stop receiving } }
public async Task HandleRequestAsync(HttpContext context, WebSocketHandler webSocketHandler) { if (!context.WebSockets.IsWebSocketRequest) { context.Response.StatusCode = 400; } var socket = await context.WebSockets.AcceptWebSocketAsync(); var socketFinishedTcs = new TaskCompletionSource <object?>(); var socketId = await webSocketHandler.AddConnectionAsync(socket, socketFinishedTcs, context.RequestAborted); WebSocketReceiveResult?result = null; _ = Task.Run(async() => { while (socket.State == WebSocketState.Open) { try { using var ms = new MemoryStream(); result = await this.ReadMessageAsync(socket, ms, context.RequestAborted); if (result.MessageType == WebSocketMessageType.Binary || result.MessageType == WebSocketMessageType.Text) { await webSocketHandler.ReceiveAsync(socketId, result, ms, context.RequestAborted); } else if (result.MessageType == WebSocketMessageType.Close) { await webSocketHandler.CloseConnectionAsync(socket, "Connection Closed"); } } catch (WebSocketException ex) when(ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) { await webSocketHandler.CloseConnectionAsync(socket, "Connection Closed Prematurely"); return; } catch (TaskCanceledException) when(socket.State == WebSocketState.Aborted) { await webSocketHandler.CloseConnectionAsync(socket, "WebSocket Aborted"); return; } } }); await socketFinishedTcs.Task; }
public static async Task ListenAsync(this WebSocket webSocket, HandleMessage onReceived, CancellationToken token) { WebSocketReceiveResult?result = null; do { var buffer = ArrayPool <byte> .Shared.Rent(Constants.MaxMessageSize); try { Array.Clear(buffer); result = await webSocket.ReceiveAsync(buffer, token); if (result.MessageType == WebSocketMessageType.Close) { return; } if (!result.EndOfMessage) { await webSocket.SendAckAsync(Ack.ExpectedEndOfMessage, token); }
public bool OnReceiveWsAsync_MktHealth(WebSocketReceiveResult?wsResult, string msgCode, string msgObjStr) { switch (msgCode) { case "MktHlth.ChangeLookback": Utils.Logger.Info("OnReceiveWsAsync_MktHealth(): changeLookback"); m_lastLookbackPeriodStr = msgObjStr; SendHistoricalWs(); return(true); case "MktHlth.ChangeNav": Utils.Logger.Info($"OnReceiveWsAsync_MktHealth(): changeNav to '{msgObjStr}'"); // DC.IM string sqTicker = "N/" + msgObjStr; // turn DC.IM to N/DC.IM var navAsset = MemDb.gMemDb.AssetsCache.GetAsset(sqTicker); this.m_mkthSelectedNavAsset = navAsset as BrokerNav; SendHistoricalWs(); SendRealtimeWs(); return(true); default: return(false); } }
// When using a WebSocket, you must keep this middleware pipeline running for the duration of the connection. // The code receives a message and immediately sends back the same message. Messages are sent and received in a loop until the client closes the connection. private async Task WebSocketLoopKeptAlive(HttpContext context, string p_requestRemainigPath) { WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); // this accept immediately send back the client that connection is accepted. switch (p_requestRemainigPath) { case "/dashboard": await DashboardWs.OnConnectedAsync(context, webSocket); break; case "/example-ws1": // client sets this URL: connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ws/example-ws1" ; await ExampleWs.OnConnectedAsync(context, webSocket); break; case "/ExSvPush": await ExSvPushWs.OnConnectedAsync(context, webSocket); break; default: throw new Exception($"Unexpected websocket connection '{p_requestRemainigPath}' in WebSocketLoopKeptAlive()"); } ArraySegment <Byte> buffer = new ArraySegment <byte>(new Byte[8192]); string bufferStr = string.Empty; WebSocketReceiveResult?result = null; // loop until the client closes the connection. The server receives a disconnect message only if the client sends it, which can't be done if the internet connection is lost. // If the client isn't always sending messages and you don't want to timeout just because the connection goes idle, have the client use a timer to send a ping message every X seconds. // On the server, if a message hasn't arrived within 2*X seconds after the previous one, terminate the connection and report that the client disconnected. while (webSocket.State == WebSocketState.Open && (result?.CloseStatus == null || !result.CloseStatus.HasValue)) { try { // convert binary array to string message: https://stackoverflow.com/questions/24450109/how-to-send-receive-messages-through-a-web-socket-on-windows-phone-8-using-the-c bufferStr = string.Empty; using (var ms = new MemoryStream()) { do { result = await webSocket.ReceiveAsync(buffer, CancellationToken.None); // client can send CloseStatus = NormalClosure for initiating close ms.Write(buffer.Array !, buffer.Offset, result.Count); } while (!result.EndOfMessage); ms.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(ms, Encoding.UTF8)) bufferStr = reader.ReadToEnd(); } // gLogger.Trace($"WebSocketLoopKeptAlive(). received msg: '{bufferStr}'"); // logging takes 1ms // if result.CloseStatus = NormalClosure message received, or any other fault, don't pass msg to listeners. We manage complexity in this class. bool isGoodNonClientClosedConnection = webSocket.State == WebSocketState.Open && result != null && (result.CloseStatus == null || !result.CloseStatus.HasValue); if (isGoodNonClientClosedConnection && result != null) { switch (p_requestRemainigPath) { case "/dashboard": DashboardWs.OnReceiveAsync(context, webSocket, result, bufferStr); // no await. There is no need to Wait until all of its async inner methods are completed break; case "/example-ws1": ExampleWs.OnReceiveAsync(context, webSocket, result, bufferStr); break; case "/ExSvPush": ExSvPushWs.OnReceiveAsync(context, webSocket, result, bufferStr); break; default: throw new Exception($"Unexpected websocket connection '{p_requestRemainigPath}' in WebSocketLoopKeptAlive()"); } } // If Client never sent any proper data, and closes browser tabpage, ReceiveAsync() returns without Exception and result.CloseStatus = EndpointUnavailable // If Client sent any proper data, and closes browser tabpage, ReceiveAsync() returns with Exception WebSocketError.ConnectionClosedPrematurely } catch (System.Net.WebSockets.WebSocketException e) { if (e.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) // 'The remote party closed the WebSocket connection without completing the close handshake.' { gLogger.Trace($"WebSocketLoopKeptAlive(). Expected exception: '{e.Message}'"); } else { throw; } } } if (result?.CloseStatus != null) // if client sends Close request then result.CloseStatus = NormalClosure and websocket.State == CloseReceived. In that case, we just answer back that we are closing. { await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); } }
internal static void OnReceiveAsync(HttpContext context, WebSocket webSocket, WebSocketReceiveResult?lastResult, string bufferStr) { // if it is not a Close-message from client, send it back temporarily var encoded = Encoding.UTF8.GetBytes(bufferStr); var buffer = new ArraySegment <Byte>(encoded, 0, encoded.Length); webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); }
protected async Task <string> ReceiveTillEnd(CancellationToken ct) { ArraySegment <Byte> buffer = new ArraySegment <byte>(new Byte[8192]); using (var ms = new MemoryStream()) { WebSocketReceiveResult?result = null; do { try { result = await wsAmiVoice.ReceiveAsync(buffer, CancellationToken.None); ms.Write(buffer.ToArray(), 0, result.Count); if (result.CloseStatus != null) { Debug.WriteLine(result.CloseStatusDescription); } if (result.MessageType == WebSocketMessageType.Close) { Debug.WriteLine("WebSocket Close Recieved"); break; } if (ct.IsCancellationRequested) { break; } } catch (WebSocketException ex) { var errStr = String.Format("WebSocketError: {0} - {1}", ex.WebSocketErrorCode, ex.Message); ErrorOccured?.Invoke(this, errStr); Debug.WriteLine(ex.Message); Debug.WriteLine(ex.StackTrace); if (wsAmiVoice.CloseStatus != null) { Debug.WriteLine(wsAmiVoice.CloseStatusDescription); } Debug.WriteLine(ex.WebSocketErrorCode); string temp; try { temp = Encoding.UTF8.GetString(ms.ToArray()); Debug.WriteLine(temp); } catch (Exception encex) when(encex is ArgumentException || encex is ArgumentNullException) { } return(""); } } while ((result == null || result.EndOfMessage != true) && (wsAmiVoice.State == WebSocketState.Open || wsAmiVoice.State == WebSocketState.CloseSent)); var payload = ms.ToArray(); try { var encodedData = System.Text.Encoding.UTF8.GetString(payload); Debug.WriteLine(encodedData); return(encodedData); } catch (Exception ex) when(ex is ArgumentException || ex is ArgumentNullException || ex is DecoderFallbackException) { return(""); } } }