starx207
starx207

Reputation: 367

WebSockets error on close

I am new to WebSockets and was trying out an example I found online. The example simple has the server parrot back whatever I enter in a text box. There is a button to send the text to the server and a button to close the connection to the server.

Here is the code for the WebSocketHandler:

<%@ WebHandler Language="C#" Class="WebSocketHandler" %>

using System;
using System.Text;
using System.Web;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

public class WebSocketHandler : IHttpHandler {

public void ProcessRequest (HttpContext context) {
    if (context.IsWebSocketRequest) {
        context.AcceptWebSocketRequest(DoTalking);
    }
}

public bool IsReusable {
    get {
        return false;
    }
}

public async Task DoTalking(AspNetWebSocketContext context) {
    WebSocket socket = context.WebSocket;

    while (true) {
        ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
        WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);

        if (socket.State == WebSocketState.Open) {
            string userMessage = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
            userMessage = "You sent: " + userMessage + " at " + DateTime.Now.ToLongTimeString();
            buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMessage));
            await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
        } else {
            break;
        }
    }
}

And here is the JQuery on the client:

var socket;
$(document).ready(function () {
socket = new WebSocket("ws://localhost:61530/WebSocketHandler.ashx");

socket.onopen = function (evt) {
    $("#serverMessage").append("<h3>Connection Opened with the Echo server.</h3>");
};

socket.onmessage = function (evt) {
    $("#serverMessage").append("<h3>" + evt.data + "</h3>");
};

socket.onerror = function (evt) {
    $("#serverMessage").append("<h3>Unexpected Error</h3>");
};

socket.onclose = function (evt) {
    $("#serverMessage").append("<h3>Connection closed</h3>");
}

$("#btnSend").click(function () {
    if (socket.readyState === WebSocket.OPEN) {
        socket.send($("#txtMsg").val());
    } else {
        $("#serverMessage").append("<h3>The underlying connection is closed</h3>");
    }
});

$("#btnStop").click(function () {
    socket.close();
});
});

Everything works fine with sending messages to the server and getting the response. The problem is when I click the button to close the websocket. When clicking that button, it first fires the onerror event then fires the onclose event. Can anyone tell me why that is and/or if I am doing something incorrectly? Thanks in advance.

Note: I've tried this on IE, Edge, Firefox, Chrome, and Opera. The problem only occurs on IE, Edge, and Firefox.

Upvotes: 0

Views: 1277

Answers (1)

starx207
starx207

Reputation: 367

I figured out the problem. In my handler, where I have the line to break out of the while loop, I had to add a CloseAsync() call to ensure the socket closes with a normal closure. Here is the revised DoTalking function that works correctly on all browsers:

public async Task DoTalking(AspNetWebSocketContext context) {
    WebSocket socket = context.WebSocket;

    while (true) {
        ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
        WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);

        if (socket.State == WebSocketState.Open) {
            string userMessage = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
            userMessage = "You sent: " + userMessage + " at " + DateTime.Now.ToLongTimeString();
            buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMessage));
            await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
        } else {
            // The following line is the line I had to add to correct the problem
            await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Socket closed", CancellationToken.None);
            break;
        }
    }
}

EDIT: Since this was just an example that I was working though, I just left it like that, but in a production environment, I would probably need to add different code to handle the different closure reasons. That way, if it was closing due to an error I could handle it differently than a normal closure.

Upvotes: 2

Related Questions