Websocket.ReceiveAsyn c вызывает cra sh, когда клиент отключается без отмены соединения - PullRequest
0 голосов
/ 07 апреля 2020

Do tNet Core 3.1 (под управлением Kestral или IIS)

У меня есть следующее l oop, выполняемое в задании для каждого подключенного клиента

 using (var ms = new MemoryStream())
                {
                        do

                            webSocketReceiveResult = await socket.ReceiveAsync(buffer, CancellationToken.None);
                            ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count);

                        }
                        while (!webSocketReceiveResult.EndOfMessage);

Когда клиент неожиданно завершает работу, происходит сбой сети, cra sh или что-либо еще, и у него нет шанса закрыть свое соединение, он разрушает весь класс и все задачи выполняются. Веб-сервер все еще работает и будет принимать все новые соединения, но все существующие соединения будут прерваны.

Ошибка: «Удаленная сторона закрыла соединение WebSocket, не завершив рукопожатие». что ожидается, но я не могу попытаться поймать его, чтобы сохранить остальные соединения?

Полный метод здесь:

    private static async Task SocketProcessingLoopAsync(ConnectedClient client)
    {
        _ = Task.Run(() => client.BroadcastLoopAsync().ConfigureAwait(false));

        var socket = client.Socket;
        var loopToken = SocketLoopTokenSource.Token;
        var broadcastTokenSource = client.BroadcastLoopTokenSource; // store a copy for use in finally block
        string sessionName = "";
        string command = "";
        int commandCounter = 0;
        WebSocketReceiveResult webSocketReceiveResult = null;
        try
        {



            var buffer = WebSocket.CreateServerBuffer(4096);

            while (socket.State != WebSocketState.Closed && socket.State != WebSocketState.Aborted && !loopToken.IsCancellationRequested)
            {

                // collect all the bytes incoming

                using (var ms = new MemoryStream())
                {
                        do
                        {
                            webSocketReceiveResult = await socket.ReceiveAsync(buffer, CancellationToken.None);
                            ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count);

                        }
                        while (!webSocketReceiveResult.EndOfMessage);

                    //var receiveResult = await client.Socket.ReceiveAsync(buffer, loopToken);

                    // if the token is cancelled while ReceiveAsync is blocking, the socket state changes to aborted and it can't be used
                    if (!loopToken.IsCancellationRequested)
                    {
                        // the client is notifying us that the connection will close; send acknowledgement
                        if (client.Socket.State == WebSocketState.CloseReceived && webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
                        {
                            Console.WriteLine($"Socket {client.SocketId}: Acknowledging Close frame received from client");
                            broadcastTokenSource.Cancel();
                            await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None);
                            // the socket state changes to closed at this point
                        }


                        if (client.Socket.State == WebSocketState.Open)
                        {
                            if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text)
                            {
                                // here we receive text instructioons from the clients

                                using (StreamReader reader = new StreamReader(ms, Encoding.UTF8))
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    command = reader.ReadToEnd().ToLower();
                                }

                                //   var command = Encoding.UTF8.GetString(buffer.Array, 0, webSocketReceiveResult.Count).ToLower();
                                if (command.Contains("session:"))
                                {
                                    // assign the client to a session, and inform them of their position in it
                                    sessionName = command.Replace("session:", "");
                                    if (!ClientControl.Sessions.ContainsKey(sessionName))
                                    {
                                        ClientControl.Sessions.TryAdd(sessionName, new BlockingCollection<ConnectedClient>());
                                    }
                                    ClientControl.Sessions[sessionName].Add(client);
                                    // broadcast the collection count
                                    Broadcast(ClientControl.Sessions[sessionName].Count.ToString(), client, true, sessionName);
                                    Broadcast("Number of clients " + ClientControl.Sessions[sessionName].Count.ToString(), client, false, sessionName);
                                    Broadcast("Number of clients " + ClientControl.Sessions[sessionName].Count.ToString(), client, true, sessionName);
                                }
                                else if (command.Contains("status:"))
                                {
                                    string output = "<br/><h1>Sessions:</h1><br/><br/>";
                                    foreach (var session in ClientControl.Sessions)
                                    {
                                        output += session.Key + " Connected Clients: " + session.Value.Count.ToString() + "<br/>";

                                    }

                                    Broadcast(output, client, true, "");
                                }
                                else if (command.Contains("ping"))
                                {
                                    Console.WriteLine(command + " " + DateTime.Now.ToString() + " " + commandCounter);
                                }

                            }
                            else
                            {
                                // we just mirror what is sent out to the connected clients, depending on the session

                                Broadcast(ms.ToArray(), client, sessionName);
                                commandCounter++;

                            }

                        }

                    }

                }// end memory stream 

            }
        }
        catch (OperationCanceledException)
        {
            // normal upon task/token cancellation, disregard
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Socket {client.SocketId}:");
            Program.ReportException(ex);
        }
        finally
        {
            broadcastTokenSource.Cancel();

            Console.WriteLine($"Socket {client.SocketId}: Ended processing loop in state {socket.State}");

            // don't leave the socket in any potentially connected state
            if (client.Socket.State != WebSocketState.Closed)
                client.Socket.Abort();

            // by this point the socket is closed or aborted, the ConnectedClient object is useless
            if (ClientControl.Sessions[sessionName].TryTake(out client))
                socket.Dispose();

            // signal to the middleware pipeline that this task has completed
            client.TaskCompletion.SetResult(true);
        }
    }

1 Ответ

0 голосов
/ 09 апреля 2020

Хорошо, не удалось заставить это работать, я переписал l oop, чтобы он выглядел так, он чисто завершает работу, когда клиент зависает

  do
                    {
                        cancellationTokenSource = new CancellationTokenSource(10000);

                        task = socket.ReceiveAsync(buffer, loopToken);
                        while (!task.IsCompleted && !SocketLoopTokenSource.IsCancellationRequested)
                        {
                            await Task.Delay(2).ConfigureAwait(false);
                        }
                        if (socket.State != WebSocketState.Open || task.Status != TaskStatus.RanToCompletion)
                        {
                            if (socket.State == WebSocketState.CloseReceived)
                            {
                                Console.WriteLine($"Socket {client.SocketId}: Acknowledging Close frame received from client");
                                broadcastTokenSource.Cancel();
                                await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None);
                                ClientControl.Sessions[sessionName].TryTake(out client);
                            }

                            Console.WriteLine("Client Left " + client.SocketId);
                            break;
                        }
                        ms.Write(buffer.Array, buffer.Offset, task.Result.Count);

                    }
                    while (!task.Result.EndOfMessage);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...