Using Websockets with ASP.NET Web API

Using WebSockets with ASP.NET Web API

UPDATE: After a bit more research by myself and a coworker, we came to the conclusion that the WebSocketHandler class does not appear to be intended to be used outside of the internal processes of SignalR. As there is no obvious means to leverage WebSocketHandler isolated from SignalR. This is unfortunate as I find its interfaces slightly more high-level than the System.Web/System.Net interfaces. Moreover, the method described below makes use of HttpContext which I believe should be avoided.

As such we plan to take an approach similar to the one shown by Mrchief, but with a bit more Web API flavor. Like this...(NOTE: our socket is write-only, but I discovered you MUST perform read operations of you want WebSocket.State to get updated properly.

class MyServiceController : ApiController
{
public HttpResponseMessage Get (string param)
{
HttpContext currentContext = HttpContext.Current;
if (currentContext.IsWebSocketRequest ||
currentContext.IsWebSocketRequestUpgrading)
{
currentContext.AcceptWebSocketRequest(ProcessWebsocketSession);
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
}

private async Task ProcessWebsocketSession(AspNetWebSocketContext context)
{
var ws = context.WebSocket;

new Task(() =>
{
var inputSegment = new ArraySegment<byte>(new byte[1024]);

while (true)
{
// MUST read if we want the state to get updated...
var result = await ws.ReceiveAsync(inputSegment, CancellationToken.None);

if (ws.State != WebSocketState.Open)
{
break;
}
}
}).Start();

while (true)
{
if (ws.State != WebSocketState.Open)
{
break;
}
else
{
byte[] binaryData = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe };
var segment = new ArraySegment<byte>(binaryData);
await ws.SendAsync(segment, WebSocketMessageType.Binary,
true, CancellationToken.None);
}
}
}
}

NOTE: Obviously error checking and proper usage of a CancellationToken is left as an exercise for the reader.

ASP.NET Web API 2 with Websockets

If "pure" WebSockets aren't a requirement I can recommend using SignalR together with WebAPI 2 (they really really make a tremendous web stack!). There are several good tutorials as this cast: http://channel9.msdn.com/Events/TechDays/Belgium-2013/25

One critical point to understand are the different life-times of controllers / hubs etc...


Edit:
One aspect you may want to read about is on how to call SignalR Hub methods from your Controller: And it's rather easy to interact with Hubs from Controllers, here's some reading about that: http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server#callfromoutsidehub

How to use websockets in asp.net core

can anyone make it more clear for me how the i will build the websocket for the server and how i will initialize the websocket on the client?

As the example that you referenced demonstrated, making use of WebSocket in ASP.NET Core, we can add the WebSockets middleware in the Configure method, then add/configure request delegate to check and handle incoming WebSocket requests.

And after transitioned a request to a WebSocket connection with AcceptWebSocketAsync() method, we can use the returned WebSocket object to send and receive messages.

In Echo method, we can also perform custom code logic to generate and send reply message/notification based on received message(s).

//received message
var mes = Encoding.UTF8.GetString(buffer, 0, result.Count);

//code logic here
//...

//create reply message
var reply_mes = $"You sent {mes}.";

byte[] reply_mes_buffer = Encoding.UTF8.GetBytes(reply_mes);

await webSocket.SendAsync(new ArraySegment<byte>(reply_mes_buffer, 0, reply_mes.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);

Besides, ASP.NET Core SignalR is an open-source library that simplifies implementing real-time communication functionality. And it does support WebSockets transport and we can easily achieving push messages/notifications to all connected clients or specified subsets of connected clients.

For more information about ASP.NET Core SignalR, you can check this doc: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1

Using websockets in classic asp.net forms application

It should be possible to handle a web socket connect in a classic ASP.NET Forms application. The tools are available on HttpContext and you basically have the correct idea.

I think the issue is that you are trying to invoke it from an aspx page and not just using a generic IHttpHandler. IIS runs specific processing pipelines based upon the file extension. Aspx is rather specialized to trying to render an HTML page, so while it might be possible to reconfigure IIS to allow websockets to interact with ASPX pages, it is much easier to just use a generic ashx handler.

You can create a handler by going to the "Add Item" dialog in Visual Studio
Visual Studio's 'Add Item' dialog with the entry for 'Generic Handler' highlighted

At this point, you would just implement your websocket logic in your handler code. I'm putting in a simple echo from this aspnet sample just to demonstrate.

public class MyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(EchoWebSocket);
}
}

private async Task EchoWebSocket(AspNetWebSocketContext socketContext)
{
var buffer = new byte[1024 * 4];
var result = await socketContext.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

while (!result.CloseStatus.HasValue)
{
await socketContext.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await socketContext.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}

await socketContext.WebSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

public bool IsReusable
{
get
{
return false;
}
}
}

You should now be able to connect to the websocket. Note the usage of wss rather than ws. The setup I tested with requires the secure version (wss). Use whatever is relevant for the application you are working on.

// Replace with port/path to your handler.
var ws = new WebSocket("wss://localhost:44336/MyHandler.ashx");

ws.onmessage = function(evt) {
console.log(evt);
};

ws.send("Hello, World!");

And you should see output similar to this:
Chrome developer console with output from the sample javascript above.

Note: I generally wouldn't recommend trying to access Session data from the websocket handler. Your session may expire outside the timeframe of the websocket's lifespan. You would probably want to use some other mechanism to persist state between the socket and the rest of the pages (potentially a database or the application-level cache, which you can access from socketContext.Cache in the example above.

Edit: Added an example of using session value on socket initialization.
Per your comment, if you want to use session values during the initialization of a websocket (which should be a safe operation), then your handler just needs to implement IRequiresSessionState and needs to add a little extra syntax to pass the session value into the accept handler.

public class MyHandler : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
{
var sessionValue = context.Session["session_key"] as string;

context.AcceptWebSocketRequest(async (socketContext) =>
{
await SetupWebSocket(socketContext, sessionValue);
});
}
}

private async Task SetupWebSocket(AspNetWebSocketContext socketContext, string sessionValue)
{
// Handle socket as before, but 'sessionValue' is now available for use.
}

public bool IsReusable
{
get
{
return false;
}
}
}

ASP.NET MVC Websocket server (WebAPI) handshake completing, but OnOpen and other event functions are not getting hit server side

Well I finally figured this out, and of course it was something stupid :)

Apparently, I made the mistake of referencing the WebSocketHandler class in SignalR - it does not look like that was meant to be used outside of SignalR's internal operations.

My new code is extremely similar, really the only difference is the websockethandler class inherited is in the Microsoft.Web.Websockets namespace.

One other difference, is this line:

HttpContext.Current.AcceptWebSocketRequest(Function() Tasks.Task.FromResult(New ChatWebSocketHandler()))

Now is just:

HttpContext.Current.AcceptWebSocketRequest(New ChatWebSocketHandler)

Took 2 days to get there and now I'm in the dog house with the PM team since we were in a hurry, but hey, got there finally. Hopefully if someone else hits this they wont be stuck on it as long as I was!

What is the simplest way to create a Websocket server in c#?

SignalR is great for cases where:

1) You need a fallback in case websockets aren't available

AND

2) You have control over the implementation of the client (there's a specific protocol that has to be followed by the client)

The easiest way I can think of, from what you tell us about the project (limited control of the implementation of the client, websocket server implementation only, Web API) is Microsoft.WebSockets.

You can install Microsoft.WebSockets from NuGet and be up and running with a websocket server in minutes. There a few tutorials out there (ex.: https://dejanstojanovic.net/aspnet/2014/june/database-change-notifications-in-aspnet-using-websocket/), but essentially:

1) make sure your IIS has websockets enabled

2) create a Web API controller that handles websocket requests. Ex.:

using System;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using Microsoft.Web.WebSockets;

public class SomeController : ApiController
{
// Has to be called something starting with "Get"
public HttpResponseMessage Get()
{
HttpContext.Current.AcceptWebSocketRequest(new SomeWebSocketHandler());
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}

class SomeWebSocketHandler : WebSocketHandler
{
public SomeWebSocketHandler() { SetupNotifier(); }
protected void SetupNotifier()
{
// Call a method to handle whichever change you want to broadcast
var messageToBroadcast = "Hello world";
broadcast(messageToBroadcast);
}

private static WebSocketCollection _someClients = new WebSocketCollection();

public override void OnOpen()
{
_someClients.Add(this);
}

public override void OnMessage(string message)
{

}

private void broadcast(string message)
{
_someClients.Broadcast(msg);
SetupNotifier();
}
}

}

The SetupNotifier() method should contain logic that catches the change you want to react upon. broadcast(string message) (can be renamed of course) contains the logic that "returns" data to the client(s) - this example sends the same message to all clients.

Make sure to test this with a proper websocket client (there are Chrome extenstions for this, if you want the ease of use) - you can't do ws:// requests in a browser as-is.



Related Topics



Leave a reply



Submit