First open the MSDN document and read it briefly (docs.microsoft.com/zh-cn/aspne…).

WebSocket is a protocol that supports the establishment of persistent two-way channels over TCP connections. It can be used for applications such as chat, stock quotes, and games, as well as for any situation in a Web application that requires real-time functionality.

  

Method of use

  • Install Microsoft. AspNetCore. Web sockets package.
  • Configure the middleware.
  • Accept WebSocket requests.
  • Send and receive messages.

If you are creating an asp.net core project, by default there will be an all package with the default websocket package. So, when you add, take a look

Then there is the middleware for configuring webSockets

app.UseWebSockets();
Copy the code

If more detailed websocket configuration is required, the MSDN documentation also provides an option to configure buffer sizes and ping

Var webSocketOptions = new webSocketOptions () {KeepAliveInterval = timespan.fromseconds (120), ReceiveBufferSize = 4 x 1024 // Size of the buffer used to receive data. Only advanced users need to change it to adjust performance based on data size. }; app.UseWebSockets(webSocketOptions);Copy the code

Accept WebSocket requests

Late in the request lifecycle (such as late in a Configure method or MVC operation), check whether it is a WebSocket request and accept a WebSocket request.

This example comes from late in the Configure method.

app.Use(async (context, next) =>
{
    if (context.Request.Path == "/ws")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
            await Echo(context, webSocket);
        }
        else
        {
            context.Response.StatusCode = 400;
        }
    }
    else
    {
        await next();
    }

});
Copy the code

WebSocket requests can come from any URL, but this sample code only accepts/WS requests

(for example, to test the websocket connection, the address must be: ws:// IP: port /ws.) Finally, the ws of this path can be defined by itself, which can be interpreted as MVC route, or URL address. When websocket connects for the first time, the URL can be used to pass parameters

Send and receive messages

The AcceptWebSocketAsync method upgrades a TCP connection to a WebSocket connection and provides a WebSocket object. Use WebSocket objects to send and receive messages.

The code shown earlier that accepts the WebSocket request passes the WebSocket object to the Echo method; Here is the Echo method. The code receives the message and immediately sends back the same message. Keep doing this in the loop until the client closes the connection

private async Task Echo(HttpContext context, WebSocket webSocket) { var buffer = new byte[1024 * 4]; WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); while (! result.CloseStatus.HasValue) { await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); } await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); }Copy the code

If WebSocket is accepted before the loop begins, the middleware pipeline ends. After the socket is closed, the pipe expands. That is, if WebSocket is accepted, the request stops in the pipe, just like clicking on an MVC action. But when this loop is completed and the socket is closed, the request is backed up in the pipe.

If you want to test whether to connect, you can write your own WS client program, of course, you can also use some off-the-shelf tools

Encapsulate a simple middleware:

What is middleware? MSDN explains this as follows:

Middleware is software that is assembled into an application pipeline to handle requests and responses. Each component:

  • Select whether to pass the request to the next component in the pipeline.
  • Work can be performed before and after the next component in the pipeline is called.

The request delegate is used to generate the request pipeline. The request delegate handles each HTTP request.

Use the Run, Map, and Use extension methods to configure the request delegate. A single request delegate parallelism can be specified as an anonymous method (called parallel middleware) or defined in a reusable class. These reusable classes and parallel anonymous methods are middleware or middleware components. Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the chain where appropriate.

Create a new class for WebSocketExtensions. Cs

 public static class WebSocketExtensions
    {
        public static IApplicationBuilder MapWebSocketManager(this IApplicationBuilder app,PathString path,WebSocketHandler handler)
        {
            return app.Map(path, (_app) => _app.UseMiddleware<WebSocketManagerMiddleware>(handler));
        }
        public static IServiceCollection AddWebSocketManager(this IServiceCollection services)
        {
            services.AddTransient<WebSocketConnectionManager>();

            foreach (var type in Assembly.GetEntryAssembly().ExportedTypes)
            {
                if (type.GetTypeInfo().BaseType == typeof(WebSocketHandler))
                {
                    services.AddSingleton(type);
                }
            }

            return services;
        }
    }
Copy the code

The AddWebSocketManager method deals primarily with dependency injection. Inject all the classes that implement WebSocketHandler through reflection

Manage WebSocket connections

public class WebSocketConnectionManager { private ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>(); public int GetCount() { return _sockets.Count; } public WebSocket GetSocketById(string id) { return _sockets.FirstOrDefault(p => p.Key == id).Value; } public ConcurrentDictionary<string, WebSocket> GetAll() { return _sockets; } public WebSocket GetWebSocket(string key) { WebSocket _socket; _sockets.TryGetValue(key, out _socket); return _socket; } public string GetId(WebSocket socket) { return _sockets.FirstOrDefault(p => p.Value == socket).Key; } public void AddSocket(WebSocket socket,string key) { if (GetWebSocket(key)! =null) { _sockets.TryRemove(key, out WebSocket destoryWebsocket); } _sockets.TryAdd(key, socket); //string sId = CreateConnectionId(); //while (! _sockets.TryAdd(sId, socket)) //{ // sId = CreateConnectionId(); //} } public async Task RemoveSocket(string id) { try { WebSocket socket; _sockets.TryRemove(id, out socket); await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); } catch (Exception) { } } public async Task CloseSocket(WebSocket socket) { await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); } private string CreateConnectionId() { return Guid.NewGuid().ToString(); }}Copy the code

 

Here I put the management of the customer’s connection into a connection class, my idea is

  • I use WebAPI for authentication, using the HTTP protocol interface
  • After the authentication succeeds, the server returns a token to the client
  • When the client connects to the server through websocket, it needs to carry the token returned last time, so THAT I can identify the duplicate socket in the connection dictionary (since I tried in websocket). In Net Core, if you connect multiple times, the server does not disconnect events, but constantly appear multiple socket objects.

The encapsulation of WebSocketManagerMiddleware

public class WebSocketManagerMiddleware { private readonly RequestDelegate _next; private WebSocketHandler _webSocketHandler { get; set; } public WebSocketManagerMiddleware(RequestDelegate next, WebSocketHandler webSocketHandler) { _next = next; _webSocketHandler = webSocketHandler; } public async Task Invoke(HttpContext context) { if (! context.WebSockets.IsWebSocketRequest) return; var socket = await context.WebSockets.AcceptWebSocketAsync(); string Key = context.Request.Query["Key"]; Console.WriteLine(" Connect person: "+Key); _webSocketHandler.OnConnected(socket,Key); await Receive(socket, async (result, buffer) => { if (result.MessageType == WebSocketMessageType.Text) { await _webSocketHandler.ReceiveAsync(socket, result, buffer); return; } else if (result.MessageType == WebSocketMessageType.Close) { await _webSocketHandler.OnDisconnected(socket); return; }}); //TODO - investigate the Kestrel exception thrown when this is the last middleware //await _next.Invoke(context); } private async Task Receive(WebSocket socket, Action<WebSocketReceiveResult, byte[]> handleMessage) { try { var buffer = new byte[1024 * 4]; while (socket.State == WebSocketState.Open) { var result = await socket.ReceiveAsync(buffer: new ArraySegment<byte>(buffer), cancellationToken: CancellationToken.None); handleMessage(result, buffer); } } catch (Exception ex) { GsLog.E(ex.StackTrace); }}}Copy the code

 

When invoked, the key parameter is passed to the client after authentication :(ws:// IP: port /ws? Key = XXX) to connect to webSocket

In this class, we deal with three main things

  • If the client connects, we place the connection in the connection management dictionary
  • If the client disconnects, we remove the connection from the connection management dictionary
  • If we are sending data, we call the ReceiveAsync method and pass in the connection object and data

Encapsulation of the WebSocketHandler class

This class is mainly associated with the game logic module and a link to websocket, the middleware we package, through the WebSockethandler to pass data to the subclass that implements this class

public abstract class WebSocketHandler { public WebSocketConnectionManager WebSocketConnectionManager { get; set; } public WebSocketHandler(WebSocketConnectionManager webSocketConnectionManager) { WebSocketConnectionManager = webSocketConnectionManager; } public virtual void OnConnected(WebSocket socket, string key) { //var ServerSocket = WebSocketConnectionManager.GetWebSocket(key); //if (ServerSocket ! = null) //{ // WebSocketConnectionManager.AddSocket(); // console. WriteLine(" Current connection already exists, disconnection." ); //} WebSocketConnectionManager.AddSocket(socket, key); } public Virtual async Task OnDisconnected(WebSocket socket) {console. WriteLine("Socket disconnected "); await WebSocketConnectionManager.RemoveSocket(WebSocketConnectionManager.GetId(socket)); } public async Task SendMessageAsync(WebSocket socket, string message) { if (socket.State ! = WebSocketState.Open) return; var bytes = Encoding.UTF8.GetBytes(message); await socket.SendAsync(buffer: new ArraySegment<byte>(array: bytes, offset: 0, count: bytes.Length), messageType: WebSocketMessageType.Text, endOfMessage: true, cancellationToken: CancellationToken.None); } public async Task SendMessageAsync(string socketId, string message) { try { await SendMessageAsync(WebSocketConnectionManager.GetSocketById(socketId), message); } catch (Exception) { } } public async Task SendMessageToAllAsync(string message) { foreach (var pair in WebSocketConnectionManager.GetAll()) { if (pair.Value.State == WebSocketState.Open) await SendMessageAsync(pair.Value, message); }} / / / < summary > / / / / / / get some connection < summary > / / / < param name = "keys" > < param > / / / < returns > < / returns > public IEnumerable<WebSocket> GetSomeWebsocket(string[] keys) { foreach (var key in keys) { yield return WebSocketConnectionManager.GetWebSocket(key); }} / / / < summary > / / / send a message to a bunch of people / / / < summary > / / / < param name = "web sockets" > < param > / / / < param name = "message" > < param > /// <returns></returns> public async Task SendMessageToSome(WebSocket[] webSockets, string message) { webSockets.ToList().ForEach(async a => { await SendMessageAsync(a, message); }); } public abstract Task ReceiveAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer); }Copy the code

Methods that must be overridden need to be defined as abstract to be overridden by subclasses

Use webSocket management middleware that we have written

  public void ConfigureServices(IServiceCollection services)
        {
            services.AddWebSocketManager();
        }
Copy the code
 var webSocketOptions = new WebSocketOptions()
            {
                KeepAliveInterval = TimeSpan.FromSeconds(20),
                ReceiveBufferSize = 4 * 1024
            };
            app.UseWebSockets(webSocketOptions);
            app.MapWebSocketManager("/zhajinhua", serviceProvider.GetService<ZjhGame>());
Copy the code

The ZjhGame class has to implement a WebSocketHandler so that we can handle the game logic in the ZjhGame class

Well, that’s about it. After all, I have no experience in developing games. I hope you can point out some mistakes

 

Still have the big guy of the last blog say add note, but I did not receive add note of money, do you add in the end? I’m gonna go back on my word if I don’t

www.cnblogs.com/boxrice/p/8…


 

 

If you want to invite me to drink water, you are welcome to give a reward, the rich hold a money field, the poor hold a personal field. Tips, likes, jokes