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:
await Task.WhenAll(OnMessage(webSocket));
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
My problems and questions are:
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?
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?
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.
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.
. 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.
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.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With