简体   繁体   中英

Server-side C# Websocket, how run a loop while listening to the cancellation token?

In my Vue/.NET Core 2.3 project I have started replacing standard AJAX calls with Websockets where I'm implementing a Stock pricing streaming service to update the front end as when stock prices get updated in real time. The previous price update used to send a interval request from the FE every 5 seconds to get the new prices from method StockProcess().GetStockPricing(symbol)

The new Websocket implementation below is now using the same backend GetStockPricing() code however the check loop now is in the back end, so while client is connected the method will keep sending prices back.

The implementation works fine perfectly as a principle, if there is a price change, the method will send the update to the client, perfect.

APIController.cs

[HttpGet("GetStockPricingAsync", Name = "GetStockPricingAsync")]
public async Task GetStockPricingAsync(string symbol)
{
    var isSocketRequest = ControllerContext.HttpContext.WebSockets.IsWebSocketRequest;
    if (isSocketRequest)
    {
        WebSocket webSocket = await ControllerContext.HttpContext.WebSockets.AcceptWebSocketAsync();
        await _mainController.GetStockPricingAsync(ControllerContext.HttpContext, webSocket, symbol);
    }
    else
    {
        ControllerContext.HttpContext.Response.StatusCode = 400;
    }
}

Implementation.cs

public async Task GetStockPricingAsync(HttpContext context, WebSocket webSocket, string symbol)
{
    var lastUpdated = DateTime.MinValue;
    bool isNotCancelled = true;
    byte[] requestBuffer = new byte[4194304];

    while (webSocket.State == WebSocketState.Open || webSocket.State == WebSocketState.CloseSent)
    {
        while (isNotCancelled)
        {
            var price = new StockProcess().GetStockPricing(symbol);

            if (lastUpdated != price.LastPriceDate)
            {
                lastUpdated = price.LastPriceDate;
                var json = JsonConvert.SerializeObject(new ServerData(price, _eventMessage), _jsonSerializerSettings);
                requestBuffer = Encoding.ASCII.GetBytes(json);
                var arraySegment = new ArraySegment<byte>(requestBuffer);
                await webSocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
            }
            Thread.Sleep(300);
        }
        WebSocketReceiveResult result = await webSocket.ReceiveAsync(requestBuffer, CancellationToken.None);

        if (result.MessageType == WebSocketMessageType.Close)
        {
            isNotCancelled = false
            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
            break;
        }
    }
}

The issue is I cannot cancel the running GetStockPricing loop:

The problem is with line WebSocketReceiveResult result = await webSocket.ReceiveAsync(requestBuffer, CancellationToken.None); does not get executed since the loop is always running, if I move this line to be executed before the loop then the loop does not get triggered as well, so I'm unable to get both loop running and ReceiveAsync listening for a cancellation token. Is there a way to achieve this? running the loop while listening for cancellation tokens?

try listening for it using a loop inside System.Threading.Tasks.Task Run

Task.Run(() => CancellationTokenFunc(...), ...);

static void CancellationTokenFunc(){
  while(true){
             //check for cancel
    }

}

do this before the webSocket.State loop

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM