1. 程式人生 > 其它 >SuperSocket 2.0應用2:基於固定頭協議的WebSocket伺服器

SuperSocket 2.0應用2:基於固定頭協議的WebSocket伺服器

SuperSocket:GitHubSuperSocket 2.0 中文文件官方WebSocket Server Demo

本文開發環境:Win10 + VS2019 + .NET 5.0 + SuperSocket 2.0.0-beta.10。

Gitee:SuperSocketV2FixedHeaderSample

續接“SuperSocket 2.0應用1:基於固定頭協議的Socket伺服器”,本文使用SuperSocket 2.0建立基於固定頭協議的WebSocket伺服器,通過PackageInfo、PackageMapper、IAsyncCommand、WebSocketSession、MiddlewareBase來說明如何構建一個完整的WebSocket伺服器。

1、PackageInfo

同SocketServer,略。

2、PackageMapper

SuperSocke使用PackageMapper將WebSocketServer中的WebSocketPackage轉換為自己感興趣的包型別。通過WebSocketPackage的Map方法,可以將位元組陣列(package.Data)或字串(package.Message)轉換為目標PackageInfo。

示例程式碼如下:

public class MyPackageConverter : IPackageMapper<WebSocketPackage, MyPackageInfo>
{
    
/// +-------+---+----------------------+ /// |request| l | | /// | type | e | request body | /// | (2) | n | | /// | |(2)| | /// +-------+---+----------------------+ private const int HeaderSize = 4; //Header總長度
// ReSharper disable once UnusedMember.Local private const int HeaderLenOffset = 2; //長度offset public MyPackageInfo Map(WebSocketPackage package) { var reader = new SequenceReader<byte>(package.Data); reader.TryReadBigEndian(out short packageKey); var body = package.Data.Slice(HeaderSize).ToArray(); return new MyPackageInfo { Key = packageKey, Body = body }; } }

3、IAsyncCommand

同SocketServer,略。

4、WebSocketSession

這是連線WebSocketServer的會話類,如同AppSession,能夠實現向客戶端傳送訊息等功能。

5、MiddlewareBase

此為WebSocketServer在.Net Core中的中介軟體,類似SocketServer的SuperSocketService。通過注入SessionContainer(即使用UseInProcSessionContainer方法,在SuperSocket.SessionContainer包中宣告)可以獲取已連線的客戶端Session列表,一般用於訊息推送。

示例程式碼如下:

public class MyMiddleware : MiddlewareBase
{
    private ISessionContainer _sessionContainer;
    private Task _sendTask;
    private bool _stopped;

    public override void Start(IServer server)
    {
        _sessionContainer = server.GetSessionContainer();
        _sendTask = RunAsync(); //模擬訊息推送
    }

    private async Task RunAsync()
    {
        while (!_stopped)
        {
            var sent = await Push();

            if (sent == 0 && !_stopped)
            {
                await Task.Delay(1000 * 5);
            }
            else
            {
                await Task.Delay(1000 * 2);
            }
        }
    }

    private async ValueTask<int> Push()
    {
        if (_sessionContainer == null)
        {
            return 0;
        }

        // about 300 characters
        var line = string.Join("-", Enumerable.Range(0, 10).Select(x => Guid.NewGuid().ToString()));
        var count = 0;

        foreach (var s in _sessionContainer.GetSessions<MyWebSocketSession>())
        {
            await s.SendAsync(line);
            count++;

            if (_stopped)
                break;
        }

        return count;
    }

    public override void Shutdown(IServer server)
    {
        _stopped = true;
        _sendTask.Wait();

        foreach (var s in _sessionContainer.GetSessions<MyWebSocketSession>())
        {
            s.PrintStats();
        }
    }
}

6、Main示例

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FixedHeaderSample.WebSocketServer.Commands;
using FixedHeaderSample.WebSocketServer.Server;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SuperSocket;
using SuperSocket.Command;
using SuperSocket.WebSocket.Server;

namespace FixedHeaderSample.WebSocketServer
{
    class Program
    {
        static async Task Main()
        {
            var host = WebSocketHostBuilder.Create()
                //註冊WebSocket訊息處理器(使用UseCommand註冊命令之後該處理器不起作用)
                .UseWebSocketMessageHandler(async (session, package) =>
                {
                    Console.WriteLine($@"{DateTime.Now:yyyy-MM-dd HH:mm:ss fff} Receive message: {package.Message}.");

                    //Send message back
                    var message =
                        $@"{DateTime.Now:yyyy-MM-dd HH:mm:ss fff} Hello from WebSocket Server: {package.Message}.";
                    await session.SendAsync(message);
                })
                .UseCommand<MyPackageInfo, MyPackageConverter>(commandOptions =>
                {
                    //註冊命令
                    commandOptions.AddCommand<MyCommand>();
                })
                .UseSession<MyWebSocketSession>()
                .UseInProcSessionContainer()
                .UseMiddleware<MyMiddleware>()
                .ConfigureAppConfiguration((hostCtx, configApp) =>
                {
                    configApp.AddInMemoryCollection(new Dictionary<string, string>
                    {
                        {"serverOptions:name", "TestServer"},
                        {"serverOptions:listeners:0:ip", "Any"},
                        {"serverOptions:listeners:0:port", "4041"}
                    });
                })
                .ConfigureLogging((hostCtx, loggingBuilder) =>
                {
                    //新增控制檯輸出
                    loggingBuilder.AddConsole();
                })
                .Build();
            await host.RunAsync();
        }
    }
}