Nowadays, web applications need more real-time communications. For instance, if you create a chat application, you want the user to get messages as soon as the other users write them. So, in this kind of application, the server must send information to the client as soon as they are available. If you have already done this a few years ago, you may have used tricky methods to simulate a full-duplex channel in a browser such as long polling, streaming, or using Flash. To replace all those hacks, W3C has defined a new standard: Web Sockets
. Web sockets provide a full-duplex, bidirectional communications channel. This is now the standard way to do real-time communication between the server and the client.
How to use Web Sockets?
In this post, we’ll create a simple web application that uses web sockets. The client opens the web socket, then the server sends “ping” every second to the client, and the client replies “pong”.
First, we need to configure the server to accept web sockets:
1. Create an ASP.NET Core Project
2. Add the NuGet package Microsoft.AspNetCore.WebSockets
(NuGet, GitHub)
3. Edit the Startup.cs
file
public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // ... // Accept web socket requests app.UseWebSockets(); // handle web socket requests app.UseMiddleware<SampleWebSocketMiddleware>(); } }
4. Create the Middleware that handles the web socket requests
The code is very simple. The Invoke method get the socket if available and use it to send and receive data. We also create 2 helpers to send and receive text data using the socket. The receive method is more complex because the data send by the client can be chunked, this means we need to get all packets before building the string.
public class SampleWebSocketMiddleware { private readonly RequestDelegate _next; public SampleWebSocketMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { if (!context.WebSockets.IsWebSocketRequest) { // Not a web socket request await _next.Invoke(context); return; } var ct = context.RequestAborted; using (var socket = await context.WebSockets.AcceptWebSocketAsync()) { for (var i = 0; i < 10; i++) { await SendStringAsync(socket, "ping", ct); var response = await ReceiveStringAsync(socket, ct); if (response != "pong") { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Expected 'pong'", ct); return; } await Task.Delay(1000, ct); } } } private static Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default) { var buffer = Encoding.UTF8.GetBytes(data); var segment = new ArraySegment<byte>(buffer); return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct); } private static async Task<string> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default) { // Message can be sent by chunk. // We must read all chunks before decoding the content var buffer = new ArraySegment<byte>(new byte[8192]); using (var ms = new MemoryStream()) { WebSocketReceiveResult result; do { ct.ThrowIfCancellationRequested(); result = await socket.ReceiveAsync(buffer, ct); ms.Write(buffer.Array, buffer.Offset, result.Count); } while (!result.EndOfMessage); ms.Seek(0, SeekOrigin.Begin); if (result.MessageType != WebSocketMessageType.Text) throw new Exception("Unexpected message"); // Encoding UTF8: https://tools.ietf.org/html/rfc6455#section-5.6 using (var reader = new StreamReader(ms, Encoding.UTF8)) { return await reader.ReadToEndAsync(); } } } }
That’s all for the server part. Now we can create the client using a few lines of JavaScript. First, we create the WebSocket object. The URL looks like ws://localhost:8080
or wss://localhost:8080
if you are using https (get a free certificate with let’s encrypt). Then we can send data to the server using the method send
. To get the data from the server we need to use the onmessage
callback. Here’s the full code:
<button id="BtnStart">Start</button> <script> var btnStart = document.getElementById("BtnStart"); btnStart.addEventListener("click", function (e) { e.preventDefault(); var protocol = location.protocol === "https:" ? "wss:" : "ws:"; var wsUri = protocol + "//" + window.location.host; var socket = new WebSocket(wsUri); socket.onopen = e => { console.log("socket opened", e); }; socket.onclose = function (e) { console.log("socket closed", e); }; socket.onmessage = function (e) { console.log(e); socket.send("pong"); }; socket.onerror = function (e) { console.error(e.data); }; }); </script>
You can see the result by opening Chrome developer tools:
Conclusion
If you are hosting your website with us, you can always contact our support team to enable WebSocket for you on the server.
Andriy Kravets is writer and experience .NET developer and like .NET for regular development. He likes to build cross-platform libraries/software with .NET.