Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net open websocket as a client

In my application I use SignalR for messaging through my clients. The application however has a websocket connection to another server for notifications like this:

var wsURL = (isSecure() ? "wss://" : "ws://") + wsEndpointURI + "?token=" + token + "&lang=" + language
         + "&uid=" + wsUid,
    notifierWs = new WebSocket(wsURL);

middlewareNotifierWs.onmessage = function (event) {
   var notification = JSON.parse(event.data),
       msg = notification.message;

       // Show the notification
};

What I want to do is to make this connection from my Asp.Net application and handle all the incoming messages myself by pushing them to the correct client. I am not finding however an example of such an implementation. All examples I find are about setting a websocket connection from my server to the client. (I have to mention that I am not very familiar with asynchronous functions so possibly I have some errors related to this fact)

Update

From what I have found the best and (possibly) simplest way to go is by ClientWebSocket.

Trying to find some examples I found this: Websocket - Server Using HttpListener and Client With ClientWebSocket

From this example it seems that I could create a Websocket connection to the endpoint I want. So that is what I did

public static class Program
{
    private static UTF8Encoding encoding = new UTF8Encoding();

    public static async Task Connect()
    {

        ClientWebSocket webSocket = null;
        try
        {
            var url = serverWebSocketConnectionUrl;

            webSocket = new ClientWebSocket();
            await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
            await Task.WhenAll(OnMessage(webSocket));
        }
        catch (Exception ex)
        {
            // Log it
        }
        finally
        {
            if (webSocket != null)
            {
                webSocket.Dispose();
            }
        }
    }

    public static async Task OnMessage(ClientWebSocket webSocket)
    {
        byte[] buffer = new byte[1024];
        while (webSocket.State == WebSocketState.Open)
        {
            var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            if (result.MessageType == WebSocketMessageType.Close)
            {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty,
                    CancellationToken.None);

            }
            else
            {
                WebSocketNotification notification = Newtonsoft.Json.JsonConvert.DeserializeObject<WebSocketNotification>(Encoding.UTF8.GetString(buffer));

                // Here I want to get to the signalR context and send my message to the correct user.
                var hubContext = GlobalHost.ConnectionManager.GetHubContext<ReportingHub>();
                List<string> userIds = new List<string>();
                userIds.Add(notification.Id);
                hubContext.Clients.Users(userIds).OnMessage(notification);
            }
        }
    }
}

Inside my javascript file handling the signalR methods I have inserted the onMessage method which should be triggered from signalR. (The same way I have inserted all methods handled by signalR and they work just fine.)

repHub.client.onMessage = function (notification) {
    // Show the message
}

From what I have done the current results are:

  1. My websocket connection opens correctly
  2. From there my debugger gets into the onMessage of the await Task.WhenAll(OnMessage(webSocket));
  3. After that my debugger awaits in the row var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
  4. When a message is sent from the server my debugging continues correctly with the result.

My problems and questions are:

  1. At some points is triggered the finally of my Connect method where the websocket gets disposed and my listener is released. (Any messages sent aren't caught anymore from await webSocket.ReceiveAsync). Under what circumstances is this suppose to happen and what should I look for?

  2. When the message is received I deserialize the json result to the WebSocketNotification correctly but my javascript function onMessage never triggers. What am I missing?

Update 2 About the second question I made it work by changing

repHub.client.onMessage => repHub.client.onmessage

I find it king of weird though since my reporting hub which uses signalR had all its methods on the server side as camelCase with first letter capital and all its methods on the client side with simple camelCase. Why this case is different? I would appreciate an answer to that.

Update 3

I inserted a try catch in my OnMessage method like that

    public static async Task OnMessage(ClientWebSocket webSocket)
    {
        byte[] buffer = new byte[1024];
        while (webSocket.State == WebSocketState.Open)
        {
            try
            {
                // Receive data on ClientWebSocket as an asynchronous operation
                var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Close)
                {
                    // If the server sends a message with message type for closure close the websocket connection
                    // Could use the CloseOutputAsync : https://stackoverflow.com/questions/26744420/net-websocket-closeoutputasync-vs-closeasync
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty,
                        CancellationToken.None);
                }
                else
                {

                    WebSocketNotification notification =
                        Newtonsoft.Json.JsonConvert.DeserializeObject<WebSocketNotification>(
                            Encoding.UTF8.GetString(buffer));
                    var hubContext = GlobalHost.ConnectionManager.GetHubContext<ReportingHub>();
                    List<string> userIds = new List<string>();
                    userIds.Add(notification.Id);
                    hubContext.Clients.Users(userIds).OnMessage(notification);

                }
            }
            catch (Exception ex)
            {
                var a = ex;
            }

        }
    }

From the error I get right before my Connect goes to finally and gets disposed I get the following error and stack trace

An internal WebSocket error occurred. Please see the innerException, if present, for more details.

at System.Net.WebSockets.WebSocketBase.ThrowIfConvertibleException(String methodName, Exception exception, CancellationToken cancellationToken, Boolean aborted)
at System.Net.WebSockets.WebSocketBase.<ReceiveAsyncCore>d__45.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at AthenaWeb.Infrastructure.NotificationWebSocket.<OnMessage>d__3.MoveNext() in C:\Branches\Notifications\trunk\AthenaWeb\Infrastructure\NotificationWebSocket.cs:line 69

I guess I could should just handle it and probably open the connection again. Now pretty much this is my only question (if there isn't anything else to look for):

Is that the best way to go? Should I check for something more on why my connection closes?

like image 235
Anastasios Selmani Avatar asked Oct 23 '17 14:10

Anastasios Selmani


People also ask

How do I open WebSocket in browser?

To open a websocket connection, we need to create new WebSocket using the special protocol ws in the url: let socket = new WebSocket("ws://javascript.info"); There's also encrypted wss:// protocol. It's like HTTPS for websockets.

Is gRPC faster than WebSocket?

gRPC is preferred over WebSockets if the application demands various requests processing at one time. gRPC supports multiplexing that arranges different requests seamlessly. But, multiplexing isn't offered by WebSocket. Hence, one connection is useful only for one request.

Does .NET support WebSockets?

. NET 7 introduced Websockets over HTTP/2 support for Kestrel, the SignalR JavaScript client, and SignalR with Blazor WebAssembly. HTTP/2 WebSockets use CONNECT requests rather than GET, so your own routes and controllers may need updating.

How do I connect to a WebSocket?

All you have to do is call the WebSocket constructor and pass in the URL of your server. // Create a new WebSocket. var socket = new WebSocket('ws://echo.websocket.org'); Once the connection has been established, the open event will be fired on your Web Socket instance.


1 Answers

About your last question you could get some insight from this post.

It seems that WebSockets can close for many reasons so the best way to go is handling the WebSocket closure and probably reopening it.

However, it seems from the answers that you should also check the I/O Completion Port of the IIS server.

One way to handle your case that is provided from @user4624881, could be to use the IAsyncResult.AsyncWaitHandle.WaitOne() for the websocket abortion handling.

Like this:

Stream s = this.GetStream();
IAsyncResult ar = s.BeginWrite(data, 0, data.Length, SendAsync, state);
if (!ar.IsCompleted)
    ar.AsyncWaitHandle.WaitOne();
like image 138
ibraimsap Avatar answered Oct 03 '22 19:10

ibraimsap