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();
        }
    }
}
posted @ 2021-08-05 16:47  xhubobo  阅读(826)  评论(15编辑  收藏  举报