In this article, we will cover the DETAILS of the RFC 6455 WebSocket specification and configure a generic NET 5 applications communicate with SignalR through WebSocket connections.

We’ll dive into the underlying concepts to understand what’s going on at the bottom.

About the WebSocket

WebSocket was introduced to achieve two-way communication between the client and the server. One pain point of HTTP 1.0 is creating and closing connections each time a request is sent to the server. However, in HTTP 1.1, persistent connections were introduced through the use of a keep-alive mechanism (RFC 2616). This way, the connection can be reused by multiple requests — this reduces latency because the server knows the client and they do not need to be started during the handshake for each request.

WebSocket is built on the HTTP 1.1 specification because it allows persistent connections. Therefore, when you first create a WebSocket connection, it is essentially an HTTP 1.1 request (more on that later). This enables real-time communication between the client and the server. In a nutshell, the following figure describes what happens during the initiation (handshake), data transfer, and closing of the WS connection. We will examine these concepts in more depth later.

The protocol consists of two parts: the handshake and the data transfer.

Shake hands

Let’s start with a handshake.

Simply put, WebSocket connections are based on HTTP(and TCP as transport) over a single port. Below is a summary of these steps.

1. The server must listen for incoming TCP socket connections. This can be any port you assign — usually 80 or 443.

2. The client initiates the handshake with an HTTP GET request (otherwise the server doesn’t know who to talk to) — this is the “Web” part of the “WebSockets”. In the message header, the client will ask the server to upgrade the connection to the WebSocket.

3. The server sends a handshake response telling the client that it will change the protocol from HTTP to WebSocket.

4. The client and server negotiate connection details. Either side can walk out.

Here is what a typical opening (client) handshake request looks like.

GET/WS-endpoint HTTP/1.1 Host: example.com:80 Upgrade: websocket Connection: Upgrade sec-websocket-key: L4kHN+1Bx7zKbxsDbqgzHw== Sec-WebSocket-Version: 13Copy the code

Notice how the client sends the Connection: Upgrade and Upgrade: webSocket headers in the request.

And the server responds with a handshake.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: CTPN8jCb3BUjBjBtdjwSQCytuBo=
Copy the code

The data transfer

The next key concept we need to understand is data transfer. Either side can send a message at any given time — because it is a full-duplex communication protocol.

A message consists of one or more frames. The types of frames can be text (UTF-8), binary, and control frames (such as 0x8 (Close), 0x9 (Ping), and 0xA (Pong)).

The installation

Let’s put it into action and see how it works.

Start by creating an ASP.NET 5 WebAPI project.

dotnet new webapi -n WebSocketsTutorial
dotnet new sln
dotnet sln add WebSocketsTutorial
Copy the code

Now add SignalR to the project.

dotnet add WebSocketsTutorial/ package Microsoft.AspNet.SignalR
Copy the code

The sample code

We started by adding WebSockets middleware to our WebAPI application. Open startup. cs and add the following code to the Configure method.

In this tutorial, I like to keep it simple. Therefore, I am not going to discuss SignalR. It will communicate entirely based on WebSocket. You can also implement the same functionality with raw WebSockets, if you want to make things easier, you don’t need to use SignalR.

app.UseWebSockets();
Copy the code

Next, we will delete the default WeatherForecastController, and add a new controller called WebSocketsController. Note that we will only use a controller action instead of intercepting the request pipeline.

The complete code for this controller is shown below.

using System; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace WebSocketsTutorial.Controllers{ [ApiController] [Route("[controller]")] public class WebSocketsController : ControllerBase { private readonly ILogger<WebSocketsController> _logger; public WebSocketsController(ILogger<WebSocketsController> logger) { _logger = logger; } [HttpGet("/ws")] public async Task Get() { if (HttpContext.WebSockets.IsWebSocketRequest) { using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); _logger.Log(LogLevel.Information, "WebSocket connection established"); await Echo(webSocket); } else { HttpContext.Response.StatusCode = 400; } } private async Task Echo(WebSocket webSocket) { var buffer = new byte[1024 * 4]; var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); _logger.Log(LogLevel.Information, "Message received from Client"); while (! result.CloseStatus.HasValue) { var serverMsg = Encoding.UTF8.GetBytes($"Server: Hello. You said: {Encoding.UTF8.GetString(buffer)}"); await webSocket.SendAsync(new ArraySegment<byte>(serverMsg, 0, serverMsg.Length), result.MessageType, result.EndOfMessage, CancellationToken.None); _logger.Log(LogLevel.Information, "Message sent to Client"); result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); _logger.Log(LogLevel.Information, "Message received from Client"); } await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); _logger.Log(LogLevel.Information, "WebSocket connection closed"); }}}Copy the code

This is what we do.

Add a new route named WS /.

Check if the current request is WebSockets, otherwise throw 400.

3. Wait until the client initiates the request.

4. Enter a loop until the client closes the connection.

5. In the loop, we send “Server: Hello. You said: <client’s message>” and send it back to the client.

6. Wait until the client sends another request.

Note that after the initial handshake, the server does not need to wait for the client to send a request to push the message to the client. Let’s run the application and see if it works.

dotnet run --project WebSocketsTutorial
Copy the code

After running the application, please visit: https://localhost:5001/swagger/index.html, you should see a Swagger UI.

Now we’ll see how to get clients and servers to talk to each other. In this demo, I’ll use Chrome’s DevTools(open new TAB → Check or press F12→ Console TAB). However, you can choose any client.

First, we will create a WebSocket connection to the server endpoint.

let webSocket = new WebSocket('wss://localhost:5001/ws');
Copy the code

What it does is initiate a connection between the client and the server. WSS :// is a WebSockets security protocol, because our WebAPI application is served over TLS.

You can then send a message by calling the websocket.send () method. Your console should look like the one below.

Let’s take a closer look at WebSocket connections

If you go to the Network TAB, filter out the request through the WS TAB and click on the last request called WS.

Click the Messages TAB and examine the Messages passing back and forth. In the meantime, if you call the following command, you can see “This was sent from the Client!” . Try it!

webSocket.send("Client: Hello");
Copy the code

As you can see, the server does wait for the client to send a response (that is, after the initial handshake), and the client can send messages without being blocked. This is full duplex communication. We have already discussed the data transfer aspect of WebSocket communication. As an exercise, you can run a loop to push messages to the client to see how it works.

In addition, the server and client can ping pong to see if the client is still alive. This is an actual feature in WebSockets! If you really want to see these packets, you can use a tool like WireShark to learn about them.

How does it shake hands? Well, if you jump to the Headers TAB, you’ll be able to see the request-response header we talked about in the first part of this article.

We can also try websocket.close () so that we can completely override the open-data-close loop.

conclusion

If you are interested in WebSocket RFC, please visit RFC 6455 and read it. This article has just scratched the surface of WebSocket, and there are many other things we could discuss, such as security, load balancing, proxies, and more.

The original link: sahansera dev/understandi…

​​​​​​