Net6/SuperSocket2.0课程1,一个Telnet示例

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

1、新建控制台程序并引入包

dotnet add package SuperSocket.Server

 2、书写代码

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SuperSocket;
using SuperSocket.ProtoBase;

namespace MySupersocket // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            var Listeners = new List<ListenOptions>() {  new ListenOptions
        {
            Ip = "Any",
            Port = 4040
        }, new ListenOptions
        {
            Ip = "Any",
            Port = 8040
        }};
            var host = SuperSocketHostBuilder
     .Create<StringPackageInfo, CommandLinePipelineFilter>().UsePackageHandler(async (session, package) =>
     {
         var result = 0;
         //接收的包 类型为StringPackageInfo 也就是字符串
         Console.WriteLine($"package如下:");
         Console.WriteLine(JsonConvert.SerializeObject(package));
         //session 可以理解为客户端的某个连接
         Console.WriteLine($"session如下:");
         //客户端关闭连接原因
         Console.WriteLine("ChannelCloseReason:" + session.Channel.CloseReason); 
         //客户端最后活跃时间
         Console.WriteLine("LastActiveTime:" + session.LastActiveTime);
         //服务器侦听的IP+端口,也就是侦听地址
         Console.WriteLine("LocalEndPoint:" + session.LocalEndPoint);
         //客户端IP+端口,也就是客户端地址
         Console.WriteLine("RemoteEndPoint:" + session.RemoteEndPoint);
         //当前客户端连接数量
         Console.WriteLine("SessionCount:" + session.Server.SessionCount);
         //当前服务器侦听的状态
         Console.WriteLine("ServerState:" + session.Server.State);
         //当前链接的唯一标识
         Console.WriteLine("SessionID:" + session.SessionID);
         //当前链接的开始时间
         Console.WriteLine("StartTime:" + session.StartTime);
         //当前链接的状态
         Console.WriteLine("State:" + session.State);
         switch (package.Key.ToUpper())
         {
             case ("ADD"):
                 result = package.Parameters
                     .Select(p => int.Parse(p))
                     .Sum();
                 break;

             case ("SUB"):
                 result = package.Parameters
                     .Select(p => int.Parse(p))
                     .Aggregate((x, y) => x - y);
                 break;

             case ("MULT"):
                 result = package.Parameters
                     .Select(p => int.Parse(p))
                     .Aggregate((x, y) => x * y);
                 break;
         }
         // \r\n 为键盘回车换行
         await session.SendAsync(Encoding.UTF8.GetBytes(result.ToString() + "\r\n"));
     }).ConfigureSuperSocket(options =>
     {
         options.Name = "Echo Server";
         options.Listeners = Listeners;
     }).ConfigureLogging((hostCtx, loggingBuilder) =>
     {
         loggingBuilder.AddConsole();
     }).Build();
            await host.RunAsync();
            Console.Read();
        }
    }
}
View Code

3、使用telnet测试

telnet 127.0.0.1 8040

NetCore输出

4、通过配置文件启动SuperSocket

项目新增配置文件appsettings.json,并设置为始终复制

{
  "serverOptions": {
    "name": "GameMsgServer",
    "listeners": [
      {
        "ip": "Any",
        "port": "4040"
      },
      {
        "ip": "127.0.0.1",
        "port": "8040"
      }
    ]
  }
}

//配置项目
//name: 服务器的名称;
//maxPackageLength: 此服务器允许的最大的包的大小; 默认4M;
//receiveBufferSize: 接收缓冲区的大小; 默认4k;
//sendBufferSize: 发送缓冲区的大小; 默认4k;
//receiveTimeout: 接收超时时间; 微秒为单位;
//sendTimeout: 发送超时的事件; 微秒为单位;
//listeners: 服务器的监听器;
//listeners/*/ip: 监听IP; Any: 所有 ipv4 地址, IPv6Any: 所有 ipv6 地址, 其它具体的IP地址;
//listeners/*/port: 监听端口;
//listeners/*/backLog: 连接等待队列的最大长度;
//listeners/*/noDelay: 定义 Socket 是否启用 Nagle 算法;
//listeners/*/security: None/Ssl3/Tls11/Tls12/Tls13; 传输层加密所使用的TLS协议版本号;
//listeners/*/certificateOptions: 用于TLS加密/揭秘的证书的配置项目;

SuperSocket的配置文件和 Asp.Net Core 一样,SuperSocket 使用 JSON 配置文件 appsettings.json。 我们只需要将该文件放到应用程序的根目录然后确保它能够被复制到输出目录就行。

  • appsettings.json
  • appsettings.Development.json // for Development environment
  • appsettings.Production.json // for Production environment

如何通过配置文件启动 SuperSocket,事实上,我们除了书写正常的启动代码之外就不需要做任何其它事情了。

修改上述代码如下:

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SuperSocket;
using SuperSocket.ProtoBase;

namespace MySupersocket // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
        //    var Listeners = new List<ListenOptions>() {  new ListenOptions
        //{
        //    Ip = "Any",
        //    Port = 4040
        //}, new ListenOptions
        //{
        //    Ip = "Any",
        //    Port = 8040
        //}};
            var host = SuperSocketHostBuilder
     .Create<StringPackageInfo, CommandLinePipelineFilter>().UsePackageHandler(async (session, package) =>
     {
         var result = 0;
         //接收的包 类型为StringPackageInfo 也就是字符串
         Console.WriteLine($"package如下:");
         Console.WriteLine(JsonConvert.SerializeObject(package));
         //session 可以理解为客户端的某个连接
         Console.WriteLine($"session如下:");
         //客户端关闭连接原因
         Console.WriteLine("ChannelCloseReason:" + session.Channel.CloseReason); 
         //客户端最后活跃时间
         Console.WriteLine("LastActiveTime:" + session.LastActiveTime);
         //服务器侦听的IP+端口,也就是侦听地址
         Console.WriteLine("LocalEndPoint:" + session.LocalEndPoint);
         //客户端IP+端口,也就是客户端地址
         Console.WriteLine("RemoteEndPoint:" + session.RemoteEndPoint);
         //当前客户端连接数量
         Console.WriteLine("SessionCount:" + session.Server.SessionCount);
         //当前服务器侦听的状态
         Console.WriteLine("ServerState:" + session.Server.State);
         //当前链接的唯一标识
         Console.WriteLine("SessionID:" + session.SessionID);
         //当前链接的开始时间
         Console.WriteLine("StartTime:" + session.StartTime);
         //当前链接的状态
         Console.WriteLine("State:" + session.State);
         switch (package.Key.ToUpper())
         {
             case ("ADD"):
                 result = package.Parameters
                     .Select(p => int.Parse(p))
                     .Sum();
                 break;

             case ("SUB"):
                 result = package.Parameters
                     .Select(p => int.Parse(p))
                     .Aggregate((x, y) => x - y);
                 break;

             case ("MULT"):
                 result = package.Parameters
                     .Select(p => int.Parse(p))
                     .Aggregate((x, y) => x * y);
                 break;
         }
         // \r\n 为键盘回车换行
         await session.SendAsync(Encoding.UTF8.GetBytes(result.ToString() + "\r\n"));
     })
     //.ConfigureSuperSocket(options =>
     //{
     //    options.Name = "Echo Server";
     //    options.Listeners = Listeners;
     //})
     .ConfigureLogging((hostCtx, loggingBuilder) =>
     {
         loggingBuilder.AddConsole();
     }).Build();
            await host.RunAsync();
            Console.Read();
        }
    }
}
View Code

实际上,我们只需将下列代码注释掉,supersocket会自动从配置文件中读取配置并侦听相关端口

注释掉手写的侦听地址

 

 注释掉手写的配置信息

 

 

5、概念解读

5.1、Package Type(包类型/内置行协议)

TextPackageInfo

 包类型 TextPackageInfo(SuperSocket.ProtoBase.TextPackageInfo,SuperSocket.ProtoBase) 是在 SuperSocket 中定义的最简单的数据包。它代表这类型的包仅包含一个字符串。

namespace SuperSocket.ProtoBase
{
    public class TextPackageInfo
    {
        public string Text { get; set; }

        public override string ToString()
        {
            return Text;
        }
    }
}

StringPackageInfo

StringPackageInfo是简单的包括键值和body的数据结构,一些内置Filter默认将过滤好的数据解析为StringPackageInfo类。StringPackageInfo类使用了一个空格来分割请求key和参数

namespace SuperSocket.ProtoBase
{
    public class StringPackageInfo : IKeyedPackageInfo<string>, IStringPackage
    {
        public string Key { get; set; }

        public string Body { get; set; }

        public string[] Parameters { get; set; }
    }
}

5.2、AppSession

AppSession

根据官方描述,AppSession代表一个和客户端的逻辑连接,基于连接的操作应该定于在该类之中。你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接。

对于实现固定头协议来说,AppSession不是必须的,但是站在应用服务器的角度来说,一个AppSession对象就是一个客户端连接,该客户端起码会含有标识自己的信息,这是应用服务器所需要的。

4.3、PipelineFilter 行协议过滤器

SuperSocket 提供了这些 PipelineFilter 模版:

TerminatorPipelineFilter (SuperSocket.ProtoBase.TerminatorPipelineFilter, SuperSocket.ProtoBase)
TerminatorTextPipelineFilter(SuperSocket.ProtoBase.TerminatorTextPipelineFilter, SuperSocket.ProtoBase)
LinePipelineFilter (SuperSocket.ProtoBase.LinePipelineFilter, SuperSocket.ProtoBase)
CommandLinePipelineFilter (SuperSocket.ProtoBase.CommandLinePipelineFilter, SuperSocket.ProtoBase)  
BeginEndMarkPipelineFilter (SuperSocket.ProtoBase.BeginEndMarkPipelineFilter, SuperSocket.ProtoBase) 
FixedSizePipelineFilter (SuperSocket.ProtoBase.FixedSizePipelineFilter, SuperSocket.ProtoBase)  
FixedHeaderPipelineFilter (SuperSocket.ProtoBase.FixedHeaderPipelineFilter, SuperSocket.ProtoBase) 

1、TerminatorPipelineFilter 终止符过滤器(过滤解析指定终止符之前的数据,默认解析成StringPackageInfo类)

2、TerminatorTextPipelineFilter 终止符文本管道过滤器(过滤解析指定终止符之前的数据,默认解析成TextPackageInfo类)

3、LinePipelineFilter 管路过滤器(最普通的过滤器,除了命令行限制,其他无限制,默认解析成TextPackageInfo类)

4、CommandLinePipelineFilter 命令行管道过滤器(最普通的过滤器,除了命令行限制,其他无限制,默认解析成StringPackageInfo类)

5、BeginEndMarkPipelineFilter 有固定开始结束标记管道过滤器(根据开始和结束符过滤有效数据,默认解析成StringPackageInfo类)

6、FixedSizePipelineFilter 固定长度过滤器(只解析指定长度的数据,当数据长度小于指定长度时,会报错并断开连接重连,默认解析成StringPackageInfo类)

7、FixedHeaderPipelineFilter 固定开头(长度)管道过滤器(通过指定数据包开头长度,重写GetBodyLengthFromHeader方法,从开头数据包中获取有效数据包的长度,默认解析成StringPackageInfo类)

 

 参考文档:

https://blog.csdn.net/weixin_33277597/article/details/122051837

https://docs.supersocket.net/v2-0/zh-CN/Start-SuperSocket-by-Configuration

 

posted @ 2023-01-30 13:58  天才卧龙  阅读(478)  评论(0编辑  收藏  举报