deniszykov / WebSocketListener

A lightweight and highly scalable asynchronous WebSocket listener
http://vtortola.github.io/WebSocketListener
81 stars 17 forks source link

Never Sees disconnect from client #55

Open netvillage opened 4 years ago

netvillage commented 4 years ago

If I do a javascript ws.close(); from the client, the server never seems to see it. The earlier version of vtortola 2.2.0.3 seems to work. A .close() on the client, it will trigger a msg = null after the line String msg = await ws.ReadStringAsync(cancellation).ConfigureAwait(false); and set the ws.IsConnected to false, taking it to the finally in the try/catch. The 4.2.9 version just sits there. I used the simple echoserver code example and it does the same thing. here's my client side code (which I used to test both old and new):

` var noSupportMessage = "Your browser cannot support WebSocket!"; var ws;

        function appendMessage(message) {
            $('body').append(message);
        }

        function connectSocketServer() {
            var support = "MozWebSocket" in window ? 'MozWebSocket' : ("WebSocket" in window ? 'WebSocket' : null);

            if (support == null) {
                appendMessage("* " + noSupportMessage + "<br/>");
                return;
            }

            appendMessage("* Connecting to server ..<br/>");
            // create a new websocket and connect
            ws = new window[support]("ws://192.168.1.207:8080/");

            // when data is comming from the server, this metod is called
            ws.onmessage = function (evt) {
                appendMessage("# " + evt.data + "<br />");
            };

            // when the connection is established, this method is called
            ws.onopen = function () {
                appendMessage('* Connection open<br/>');
                $('#messageInput').attr("disabled", "");
                $('#sendButton').attr("disabled", "");
                $('#connectButton').attr("disabled", "disabled");
                $('#disconnectButton').attr("disabled", "");
            };

            // when the connection is closed, this method is called
            ws.onclose = function () {
                appendMessage('* Connection closed<br/>');
                $('#messageInput').attr("disabled", "disabled");
                $('#sendButton').attr("disabled", "disabled");
                $('#connectButton').attr("disabled", "");
                $('#disconnectButton').attr("disabled", "disabled");
            }
            ws.onerror = function (event) {
                console.log(event);
                // Sadly, for security reasons you can't get good information on the error
                // If you see "ERR_CERT_DATE_INVALID" from developers console, the server's SSL cert has expired
            }
        }

        function sendMessage() {
            if (ws) {
                var messageBox = document.getElementById('messageInput');
                //ws.send(messageBox.value);
                ws.send(messageBox.value+"\r");
                messageBox.value = "";
            }
        }

        function disconnectWebSocket() {
            if (ws) {
                ws.close();
            }
        }

        function connectWebSocket() {
            connectSocketServer();
        }

        window.onload = function () {
            $('#messageInput').attr("disabled", "disabled");
            $('#sendButton').attr("disabled", "disabled");
            $('#disconnectButton').attr("disabled", "disabled");
        }
<p>To test just an echo back from the server</p>
<input type="button" id="connectButton" value="Connect" onclick="connectWebSocket()" /> 
<input type="button" id="disconnectButton" value="Disconnect" onclick="disconnectWebSocket()" /> 
<input type="text" id="messageInput"/> <input type="button" id="sendButton" value="Send" onclick="sendMessage()" /> 

`

and my C# code here

` public partial class MainWindow : Window { public IPEndPoint endpoint; // The IP/Port websocket service is listening on public WebSocketListener server; CancellationTokenSource cancellation;

    public MainWindow() {
        InitializeComponent();

    }

    private void Window_Loaded(object sender, RoutedEventArgs e) {
        cancellation = new CancellationTokenSource();
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

        var bufferSize = 1024 * 8; // 8KiB
        var bufferPoolSize = 100 * bufferSize; // 800KiB pool

        var options = new WebSocketListenerOptions
        {
            SubProtocols = new[] { "text" },
            PingTimeout = TimeSpan.FromSeconds(5),
            NegotiationTimeout = TimeSpan.FromSeconds(5),
            PingMode = PingMode.Manual,
            ParallelNegotiations = 16,
            NegotiationQueueCapacity = 256,
            BufferManager = BufferManager.CreateBufferManager(bufferPoolSize, bufferSize)
        };
        options.Standards.RegisterRfc6455(factory =>
        {
            factory.MessageExtensions.RegisterDeflateCompression();
        });
        // configure tcp transport

        options.Transports.ConfigureTcp(tcp =>
        {
            tcp.BacklogSize = 100; // max pending connections waiting to be accepted
            tcp.ReceiveBufferSize = bufferSize;
            tcp.SendBufferSize = bufferSize;
        });

        IPAddress serverIPAddress;
        serverIPAddress = System.Net.IPAddress.Parse("192.168.1.207");
        endpoint = new IPEndPoint(serverIPAddress, 8080);

        // starting the server
        server = new WebSocketListener(endpoint, options);
        server.StartAsync().Wait();

        Console.WriteLine("Echo Server listening on " + endpoint.ToString());

        Console.WriteLine("You can test echo server at http://www.websocket.org/echo.html.");

        var acceptingTask = AcceptWebSocketsAsync(server, cancellation.Token);

    }

    private static async Task AcceptWebSocketsAsync(WebSocketListener server, CancellationToken cancellation) {
        await Task.Yield();

        while(!cancellation.IsCancellationRequested) {
            try {
                var webSocket = await server.AcceptWebSocketAsync(cancellation).ConfigureAwait(false);
                // This will wait here until we get an incoming connection
                if(webSocket == null) {
                    if(cancellation.IsCancellationRequested || !server.IsStarted)
                        break; // stopped
                    continue; // retry
                }
                EchoAllIncomingMessagesAsync(webSocket, cancellation);
            }
            catch(OperationCanceledException) {
                /* server is stopped */
                break;
            }
            catch(Exception acceptError) {
                Console.WriteLine("An error occurred while accepting client.", acceptError);
            }

        }
        // goes here when: cancellation.Cancel();
        Console.WriteLine("Server has stopped accepting new clients.");
    }

    private static async Task EchoAllIncomingMessagesAsync(WebSocket webSocket, CancellationToken cancellation) {
        Console.WriteLine("Client '" + webSocket.RemoteEndpoint + "' connected.");
        try {
            while(webSocket.IsConnected && !cancellation.IsCancellationRequested) {
                try {
                    var messageText = await webSocket.ReadStringAsync(cancellation).ConfigureAwait(false);
                    if(messageText == null)
                        break; // webSocket is disconnected

                    await webSocket.WriteStringAsync(messageText, cancellation).ConfigureAwait(false);

                    Console.WriteLine("Client '" + webSocket.RemoteEndpoint + "' sent: " + messageText + ".");
                }
                catch(TaskCanceledException) {
                    break;
                }
                catch(Exception readWriteError) {
                    Console.WriteLine("An error occurred while reading/writing echo message.", readWriteError);
                    await webSocket.CloseAsync().ConfigureAwait(false);
                }
            }
        }
        finally {
            webSocket.Dispose();
            Console.WriteLine("Client '" + webSocket.RemoteEndpoint + "' disconnected.");
        }
    }
    private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) {
        Console.WriteLine("Unobserved Exception: ", e.Exception);
    }
    private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine("Unhandled Exception: ", e.ExceptionObject as Exception);
    }
    private void Window_Closed(object sender, EventArgs e) {
        if(server.IsStarted) {
            cancellation.Cancel();
            server.StopAsync().Wait();
            ///acceptingTask.Wait();
        }

    }
}

`

As I mentioned, it's pretty much the echo code, and the older version seems to work

netvillage commented 4 years ago

well. I think this might be an issue with 4.2.9. because if I change the nuGet package to 4.2.4 and below it seems to be working.

GusevVasily commented 3 years ago

@deniszykov, +1 I also ran into this problem (v4.2.10). and also the rollback to version 4.2.4 helped to solve the disconnection from the server

deniszykov commented 3 years ago

Hi everyone. This one is fixed in 4.2.14

Also don't forget to call webSocket.CloseAsync() after you are done with WebSocket and want to dispose it. I see in your example you close socket only on errors. Current examples from repository contains valid close routine.

GusevVasily commented 3 years ago

Hi everyone. This one is fixed in 4.2.14

Also don't forget to call webSocket.CloseAsync() after you are done with WebSocket and want to dispose it. I see in your example you close socket only on errors. Current examples from repository contains valid close routine.

Super! Updated to 4.2.14 and everything worked, thank you. Just yesterday, nuget did not have this version.

I think the issue can be closed.