From c6e47b545e729e302b094ad9636a9728a2dfcabe Mon Sep 17 00:00:00 2001 From: SmallChi <564952747@qq.com> Date: Sun, 24 Feb 2019 20:37:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E5=8F=8A=E6=B7=BB=E5=8A=A080?= =?UTF-8?q?9=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/JT809SystemCollectInfoDto.cs | 33 +++++ .../IJT809SessionPublishing.cs | 12 ++ .../JT809.DotNetty.Abstractions.csproj | 7 ++ .../JT809Constants.cs | 25 ++++ .../Codecs/JT809TcpDecoder.cs | 20 ++++ .../JT809ClientConfiguration.cs | 31 +++++ .../Configurations/JT809Configuration.cs | 29 +++++ .../Converters/JsonIPAddressConverter.cs | 26 ++++ .../Converters/JsonIPEndPointConverter.cs | 32 +++++ .../Handlers/JT809MsgIdTcpHandlerBase.cs | 109 +++++++++++++++++ .../Interfaces/IVerifyCodeGenerator.cs | 14 +++ .../JT809SessionPublishingEmptyImpl.cs | 13 ++ .../VerifyCodeGeneratorDefaultImpl.cs | 15 +++ .../JT809.DotNetty.Core.csproj | 23 ++++ .../JT809CoreDotnettyExtensions.cs | 59 +++++++++ .../Links/SubordinateLinkClient.cs | 113 ++++++++++++++++++ .../Metadata/JT809AtomicCounter.cs | 49 ++++++++ .../Metadata/JT809Request.cs | 23 ++++ .../Metadata/JT809Response.cs | 27 +++++ .../Metadata/JT809TcpSession.cs | 28 +++++ .../JT809SimpleSystemCollectService.cs | 30 +++++ .../Services/JT809TcpAtomicCounterService.cs | 51 ++++++++ .../Session/JT809TcpSessionManager.cs | 108 +++++++++++++++++ .../Handlers/JT809MsgIdDefaultTcpHandler.cs | 18 +++ .../Handlers/JT809TcpConnectionHandler.cs | 98 +++++++++++++++ .../Handlers/JT809TcpServerHandler.cs | 83 +++++++++++++ .../JT809.DotNetty.Tcp.csproj | 11 ++ .../JT809TcpDotnettyExtensions.cs | 32 +++++ src/JT809.DotNetty.Tcp/JT809TcpServerHost.cs | 95 +++++++++++++++ .../JT809.DotNetty.Host.Test.csproj | 27 +++++ .../JT809.DotNetty.Host.Test/Program.cs | 54 +++++++++ .../JT809.DotNetty.Host.Test/appsettings.json | 18 +++ .../JT809.DotNetty.Tcp.Test.csproj | 31 +++++ .../JT809.DotNetty.Tcp.Test/TestBase.cs | 37 ++++++ .../JT809.DotNetty.Tcp.Test/appsettings.json | 18 +++ src/JT809.DotNetty.sln | 83 +++++++++++++ src/JT809.Protocol | 2 +- src/JT809Netty.sln | 31 ----- 38 files changed, 1483 insertions(+), 32 deletions(-) create mode 100644 src/JT809.DotNetty.Abstractions/Dtos/JT809SystemCollectInfoDto.cs create mode 100644 src/JT809.DotNetty.Abstractions/IJT809SessionPublishing.cs create mode 100644 src/JT809.DotNetty.Abstractions/JT809.DotNetty.Abstractions.csproj create mode 100644 src/JT809.DotNetty.Abstractions/JT809Constants.cs create mode 100644 src/JT809.DotNetty.Core/Codecs/JT809TcpDecoder.cs create mode 100644 src/JT809.DotNetty.Core/Configurations/JT809ClientConfiguration.cs create mode 100644 src/JT809.DotNetty.Core/Configurations/JT809Configuration.cs create mode 100644 src/JT809.DotNetty.Core/Converters/JsonIPAddressConverter.cs create mode 100644 src/JT809.DotNetty.Core/Converters/JsonIPEndPointConverter.cs create mode 100644 src/JT809.DotNetty.Core/Handlers/JT809MsgIdTcpHandlerBase.cs create mode 100644 src/JT809.DotNetty.Core/Interfaces/IVerifyCodeGenerator.cs create mode 100644 src/JT809.DotNetty.Core/Internal/JT809SessionPublishingEmptyImpl.cs create mode 100644 src/JT809.DotNetty.Core/Internal/VerifyCodeGeneratorDefaultImpl.cs create mode 100644 src/JT809.DotNetty.Core/JT809.DotNetty.Core.csproj create mode 100644 src/JT809.DotNetty.Core/JT809CoreDotnettyExtensions.cs create mode 100644 src/JT809.DotNetty.Core/Links/SubordinateLinkClient.cs create mode 100644 src/JT809.DotNetty.Core/Metadata/JT809AtomicCounter.cs create mode 100644 src/JT809.DotNetty.Core/Metadata/JT809Request.cs create mode 100644 src/JT809.DotNetty.Core/Metadata/JT809Response.cs create mode 100644 src/JT809.DotNetty.Core/Metadata/JT809TcpSession.cs create mode 100644 src/JT809.DotNetty.Core/Services/JT809SimpleSystemCollectService.cs create mode 100644 src/JT809.DotNetty.Core/Services/JT809TcpAtomicCounterService.cs create mode 100644 src/JT809.DotNetty.Core/Session/JT809TcpSessionManager.cs create mode 100644 src/JT809.DotNetty.Tcp/Handlers/JT809MsgIdDefaultTcpHandler.cs create mode 100644 src/JT809.DotNetty.Tcp/Handlers/JT809TcpConnectionHandler.cs create mode 100644 src/JT809.DotNetty.Tcp/Handlers/JT809TcpServerHandler.cs create mode 100644 src/JT809.DotNetty.Tcp/JT809.DotNetty.Tcp.csproj create mode 100644 src/JT809.DotNetty.Tcp/JT809TcpDotnettyExtensions.cs create mode 100644 src/JT809.DotNetty.Tcp/JT809TcpServerHost.cs create mode 100644 src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/JT809.DotNetty.Host.Test.csproj create mode 100644 src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/Program.cs create mode 100644 src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/appsettings.json create mode 100644 src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/JT809.DotNetty.Tcp.Test.csproj create mode 100644 src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/TestBase.cs create mode 100644 src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/appsettings.json create mode 100644 src/JT809.DotNetty.sln delete mode 100644 src/JT809Netty.sln diff --git a/src/JT809.DotNetty.Abstractions/Dtos/JT809SystemCollectInfoDto.cs b/src/JT809.DotNetty.Abstractions/Dtos/JT809SystemCollectInfoDto.cs new file mode 100644 index 0000000..9dbb411 --- /dev/null +++ b/src/JT809.DotNetty.Abstractions/Dtos/JT809SystemCollectInfoDto.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.DotNetty.Abstractions.Dtos +{ + public class JT809SystemCollectInfoDto + { + /// + /// 进程Id + /// + public int ProcessId { get; set; } + /// + /// 进程分配内存 + /// 单位MB + /// + public double WorkingSet64 { get; set; } + /// + /// 进程分配内存峰值 + /// 单位MB + /// + public double PeakWorkingSet64 { get; set; } + /// + /// 进程分配私有内存 + /// 单位MB + /// + public double PrivateMemorySize64 { get; set; } + /// + /// 进程执行CPU总处理时间 + /// + public TimeSpan CPUTotalProcessorTime { get; set; } + } +} diff --git a/src/JT809.DotNetty.Abstractions/IJT809SessionPublishing.cs b/src/JT809.DotNetty.Abstractions/IJT809SessionPublishing.cs new file mode 100644 index 0000000..c86f05e --- /dev/null +++ b/src/JT809.DotNetty.Abstractions/IJT809SessionPublishing.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace JT809.DotNetty.Abstractions +{ + /// + /// 会话通知(在线/离线) + /// + public interface IJT809SessionPublishing + { + Task PublishAsync(string topicName, string value); + } +} diff --git a/src/JT809.DotNetty.Abstractions/JT809.DotNetty.Abstractions.csproj b/src/JT809.DotNetty.Abstractions/JT809.DotNetty.Abstractions.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/src/JT809.DotNetty.Abstractions/JT809.DotNetty.Abstractions.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/JT809.DotNetty.Abstractions/JT809Constants.cs b/src/JT809.DotNetty.Abstractions/JT809Constants.cs new file mode 100644 index 0000000..f28f5b6 --- /dev/null +++ b/src/JT809.DotNetty.Abstractions/JT809Constants.cs @@ -0,0 +1,25 @@ +namespace JT809.DotNetty.Abstractions +{ + public static class JT809Constants + { + public const string SessionOnline= "JT809SessionOnline"; + + public const string SessionOffline = "JT809SessionOffline"; + + public static class JT809WebApiRouteTable + { + public const string RouteTablePrefix = "/jt809api"; + + public const string SessionPrefix = "Session"; + + public const string SystemCollectPrefix = "SystemCollect"; + + public const string TcpPrefix = "Tcp"; + + /// + ///获取当前系统进程使用率 + /// + public static string SystemCollectGet = $"{RouteTablePrefix}/{SystemCollectPrefix}/Get"; + } + } +} diff --git a/src/JT809.DotNetty.Core/Codecs/JT809TcpDecoder.cs b/src/JT809.DotNetty.Core/Codecs/JT809TcpDecoder.cs new file mode 100644 index 0000000..35ccdab --- /dev/null +++ b/src/JT809.DotNetty.Core/Codecs/JT809TcpDecoder.cs @@ -0,0 +1,20 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using System.Collections.Generic; +using DotNetty.Transport.Channels; +using JT809.Protocol; + +namespace JT809.DotNetty.Core.Codecs +{ + public class JT809TcpDecoder : ByteToMessageDecoder + { + protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output) + { + byte[] buffer = new byte[input.Capacity + 2]; + input.ReadBytes(buffer, 1, input.Capacity); + buffer[0] = JT809Package.BEGINFLAG; + buffer[input.Capacity + 1] = JT809Package.ENDFLAG; + output.Add(buffer); + } + } +} diff --git a/src/JT809.DotNetty.Core/Configurations/JT809ClientConfiguration.cs b/src/JT809.DotNetty.Core/Configurations/JT809ClientConfiguration.cs new file mode 100644 index 0000000..ba6db3d --- /dev/null +++ b/src/JT809.DotNetty.Core/Configurations/JT809ClientConfiguration.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT809.DotNetty.Core.Configurations +{ + public class JT809ClientConfiguration + { + public string Host { get; set; } + + public int Port { get; set; } + + private EndPoint endPoint; + + public EndPoint EndPoint + { + get + { + if (endPoint == null) + { + if (IPAddress.TryParse(Host, out IPAddress ip)) + { + endPoint = new IPEndPoint(ip, Port); + } + } + return endPoint; + } + } + } +} diff --git a/src/JT809.DotNetty.Core/Configurations/JT809Configuration.cs b/src/JT809.DotNetty.Core/Configurations/JT809Configuration.cs new file mode 100644 index 0000000..74647dd --- /dev/null +++ b/src/JT809.DotNetty.Core/Configurations/JT809Configuration.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.DotNetty.Core.Configurations +{ + public class JT809Configuration + { + public int TcpPort { get; set; } = 809; + + public int QuietPeriodSeconds { get; set; } = 1; + + public TimeSpan QuietPeriodTimeSpan => TimeSpan.FromSeconds(QuietPeriodSeconds); + + public int ShutdownTimeoutSeconds { get; set; } = 3; + + public TimeSpan ShutdownTimeoutTimeSpan => TimeSpan.FromSeconds(ShutdownTimeoutSeconds); + + public int SoBacklog { get; set; } = 8192; + + public int EventLoopCount { get; set; } = Environment.ProcessorCount; + + public int ReaderIdleTimeSeconds { get; set; } = 180; + + public int WriterIdleTimeSeconds { get; set; } = 60; + + public int AllIdleTimeSeconds { get; set; } = 180; + } +} diff --git a/src/JT809.DotNetty.Core/Converters/JsonIPAddressConverter.cs b/src/JT809.DotNetty.Core/Converters/JsonIPAddressConverter.cs new file mode 100644 index 0000000..8f104d6 --- /dev/null +++ b/src/JT809.DotNetty.Core/Converters/JsonIPAddressConverter.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT809.DotNetty.Core.Converters +{ + public class JsonIPAddressConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(IPAddress)); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return IPAddress.Parse((string)reader.Value); + } + } +} diff --git a/src/JT809.DotNetty.Core/Converters/JsonIPEndPointConverter.cs b/src/JT809.DotNetty.Core/Converters/JsonIPEndPointConverter.cs new file mode 100644 index 0000000..a77cb8e --- /dev/null +++ b/src/JT809.DotNetty.Core/Converters/JsonIPEndPointConverter.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Net; + +namespace JT809.DotNetty.Core.Converters +{ + public class JsonIPEndPointConverter: JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(IPEndPoint)); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + IPEndPoint ep = (IPEndPoint)value; + JObject jo = new JObject(); + jo.Add("Host", JToken.FromObject(ep.Address, serializer)); + jo.Add("Port", ep.Port); + jo.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject jo = JObject.Load(reader); + IPAddress address = jo["Host"].ToObject(serializer); + int port = (int)jo["Port"]; + return new IPEndPoint(address, port); + } + } +} diff --git a/src/JT809.DotNetty.Core/Handlers/JT809MsgIdTcpHandlerBase.cs b/src/JT809.DotNetty.Core/Handlers/JT809MsgIdTcpHandlerBase.cs new file mode 100644 index 0000000..25fe923 --- /dev/null +++ b/src/JT809.DotNetty.Core/Handlers/JT809MsgIdTcpHandlerBase.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using JT809.DotNetty.Core.Interfaces; +using JT809.DotNetty.Core.Metadata; +using JT809.Protocol.Enums; +using JT809.Protocol.Extensions; +using JT809.Protocol.MessageBody; + +namespace JT809.DotNetty.Core.Handlers +{ + /// + /// 基于Tcp模式抽象消息处理业务 + /// 自定义消息处理业务 + /// 注意: + /// 1.ConfigureServices: + /// services.Replace(new ServiceDescriptor(typeof(JT809MsgIdTcpHandlerBase),typeof(JT809MsgIdCustomTcpHandlerImpl),ServiceLifetime.Singleton)); + /// 2.解析具体的消息体,具体消息调用具体的JT809Serializer.Deserialize + /// + public abstract class JT809MsgIdTcpHandlerBase + { + protected JT809TcpSessionManager sessionManager { get; } + protected IVerifyCodeGenerator verifyCodeGenerator { get; } + /// + /// 初始化消息处理业务 + /// + protected JT809MsgIdTcpHandlerBase( + IVerifyCodeGenerator verifyCodeGenerator, + JT809TcpSessionManager sessionManager) + { + this.sessionManager = sessionManager; + this.verifyCodeGenerator = verifyCodeGenerator; + HandlerDict = new Dictionary> + { + {JT809BusinessType.主链路登录请求消息, Msg0x1001}, + {JT809BusinessType.主链路注销请求消息, Msg0x1003}, + {JT809BusinessType.主链路连接保持请求消息, Msg0x1005}, + {JT809BusinessType.主链路动态信息交换消息, Msg0x1200} + }; + //SubHandlerDict = new Dictionary> + //{ + // {JT809SubBusinessType.实时上传车辆定位信息, Msg0x1200_0x1202}, + //}; + } + + public Dictionary> HandlerDict { get; protected set; } + + //public Dictionary> SubHandlerDict { get; protected set; } + + /// + /// 主链路登录应答消息 + /// + /// + /// + public virtual JT809Response Msg0x1001(JT809Request request) + { + var package= JT809BusinessType.主链路登录应答消息.Create(new JT809_0x1002() + { + Result= JT809_0x1002_Result.成功, + VerifyCode= verifyCodeGenerator.Create() + }); + + return new JT809Response(package,100); + } + + /// + /// 主链路注销应答消息 + /// + /// + /// + public virtual JT809Response Msg0x1003(JT809Request request) + { + var package = JT809BusinessType.主链路注销应答消息.Create(); + return new JT809Response(package, 100); + } + + /// + /// 主链路连接保持应答消息 + /// + /// + /// + public virtual JT809Response Msg0x1005(JT809Request request) + { + var package = JT809BusinessType.主链路连接保持应答消息.Create(); + return new JT809Response(package, 100); + } + + /// + /// 主链路动态信息交换消息 + /// + /// + /// + public virtual JT809Response Msg0x1200(JT809Request request) + { + + return null; + } + + ///// + ///// 主链路动态信息交换消息 + ///// + ///// + ///// + //public virtual JT809Response Msg0x1200_0x1202(JT809Request request) + //{ + + // return null; + //} + } +} diff --git a/src/JT809.DotNetty.Core/Interfaces/IVerifyCodeGenerator.cs b/src/JT809.DotNetty.Core/Interfaces/IVerifyCodeGenerator.cs new file mode 100644 index 0000000..2584683 --- /dev/null +++ b/src/JT809.DotNetty.Core/Interfaces/IVerifyCodeGenerator.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.DotNetty.Core.Interfaces +{ + /// + /// 校验码生成器 + /// + public interface IVerifyCodeGenerator + { + uint Create(); + } +} diff --git a/src/JT809.DotNetty.Core/Internal/JT809SessionPublishingEmptyImpl.cs b/src/JT809.DotNetty.Core/Internal/JT809SessionPublishingEmptyImpl.cs new file mode 100644 index 0000000..37e03da --- /dev/null +++ b/src/JT809.DotNetty.Core/Internal/JT809SessionPublishingEmptyImpl.cs @@ -0,0 +1,13 @@ +using JT809.DotNetty.Abstractions; +using System.Threading.Tasks; + +namespace JT809.DotNetty.Core +{ + internal class JT809SessionPublishingEmptyImpl : IJT809SessionPublishing + { + public Task PublishAsync(string topicName, string value) + { + return Task.CompletedTask; + } + } +} diff --git a/src/JT809.DotNetty.Core/Internal/VerifyCodeGeneratorDefaultImpl.cs b/src/JT809.DotNetty.Core/Internal/VerifyCodeGeneratorDefaultImpl.cs new file mode 100644 index 0000000..8089fdc --- /dev/null +++ b/src/JT809.DotNetty.Core/Internal/VerifyCodeGeneratorDefaultImpl.cs @@ -0,0 +1,15 @@ +using JT809.DotNetty.Core.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.DotNetty.Core.Internal +{ + internal class VerifyCodeGeneratorDefaultImpl : IVerifyCodeGenerator + { + public uint Create() + { + return (uint)Guid.NewGuid().GetHashCode(); + } + } +} diff --git a/src/JT809.DotNetty.Core/JT809.DotNetty.Core.csproj b/src/JT809.DotNetty.Core/JT809.DotNetty.Core.csproj new file mode 100644 index 0000000..12047b0 --- /dev/null +++ b/src/JT809.DotNetty.Core/JT809.DotNetty.Core.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.0 + 7.1 + + + + + + + + + + + + + + + + + + diff --git a/src/JT809.DotNetty.Core/JT809CoreDotnettyExtensions.cs b/src/JT809.DotNetty.Core/JT809CoreDotnettyExtensions.cs new file mode 100644 index 0000000..050118d --- /dev/null +++ b/src/JT809.DotNetty.Core/JT809CoreDotnettyExtensions.cs @@ -0,0 +1,59 @@ +using JT809.DotNetty.Abstractions; +using JT809.DotNetty.Core.Configurations; +using JT809.DotNetty.Core.Converters; +using JT809.DotNetty.Core.Interfaces; +using JT809.DotNetty.Core.Internal; +using JT809.DotNetty.Core.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Newtonsoft.Json; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("JT809.DotNetty.Core.Test")] +[assembly: InternalsVisibleTo("JT809.DotNetty.Tcp.Test")] +[assembly: InternalsVisibleTo("JT809.DotNetty.Udp.Test")] +[assembly: InternalsVisibleTo("JT809.DotNetty.WebApi.Test")] + +namespace JT809.DotNetty.Core +{ + public static class JT809CoreDotnettyExtensions + { + static JT809CoreDotnettyExtensions() + { + JsonConvert.DefaultSettings = new Func(() => + { + Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings(); + //日期类型默认格式化处理 + settings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat; + settings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + //空值处理 + settings.NullValueHandling = NullValueHandling.Ignore; + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + settings.Converters.Add(new JsonIPAddressConverter()); + settings.Converters.Add(new JsonIPEndPointConverter()); + return settings; + }); + } + + public static IServiceCollection AddJT809Core(this IServiceCollection serviceDescriptors, IConfiguration configuration, Newtonsoft.Json.JsonSerializerSettings settings=null) + { + if (settings != null) + { + JsonConvert.DefaultSettings = new Func(() => + { + settings.Converters.Add(new JsonIPAddressConverter()); + settings.Converters.Add(new JsonIPEndPointConverter()); + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + return settings; + }); + } + serviceDescriptors.Configure(configuration.GetSection("JT809Configuration")); + serviceDescriptors.TryAddSingleton(); + serviceDescriptors.TryAddSingleton(); + serviceDescriptors.TryAddSingleton(); + return serviceDescriptors; + } + } +} \ No newline at end of file diff --git a/src/JT809.DotNetty.Core/Links/SubordinateLinkClient.cs b/src/JT809.DotNetty.Core/Links/SubordinateLinkClient.cs new file mode 100644 index 0000000..d562494 --- /dev/null +++ b/src/JT809.DotNetty.Core/Links/SubordinateLinkClient.cs @@ -0,0 +1,113 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using JT809.DotNetty.Core.Codecs; +using JT809.Protocol; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT809.DotNetty.Core.Links +{ + /// + /// 从链路客户端 + /// + public sealed class SubordinateLinkClient:IDisposable + { + private Bootstrap bootstrap; + + private MultithreadEventLoopGroup group; + + private IChannel channel; + + private readonly ILogger logger; + + private readonly ILoggerFactory loggerFactory; + + private readonly IServiceProvider serviceProvider; + + private bool disposed = false; + + public SubordinateLinkClient( + IServiceProvider provider, + ILoggerFactory loggerFactory) + { + this.serviceProvider = provider; + this.loggerFactory = loggerFactory; + this.logger = loggerFactory.CreateLogger(); + } + + public async void ConnectAsync(string ip,int port,uint verifyCode) + { + group = new MultithreadEventLoopGroup(); + bootstrap = new Bootstrap(); + bootstrap.Group(group) + .Channel() + .Option(ChannelOption.TcpNodelay, true) + .Handler(new ActionChannelInitializer(channel => + { + IChannelPipeline pipeline = channel.Pipeline; + //下级平台1分钟发送心跳 + //上级平台是3分钟没有发送就断开连接 + channel.Pipeline.AddLast("jt809SystemIdleState", new IdleStateHandler(60,180,200)); + //pipeline.AddLast(new ClientConnectionHandler(bootstrap, channeldic, loggerFactory)); + channel.Pipeline.AddLast("jt809TcpBuffer", new DelimiterBasedFrameDecoder(int.MaxValue, + Unpooled.CopiedBuffer(new byte[] { JT809Package.BEGINFLAG }), + Unpooled.CopiedBuffer(new byte[] { JT809Package.ENDFLAG }))); + using (var scope = serviceProvider.CreateScope()) + { + channel.Pipeline.AddLast("jt809TcpDecode", scope.ServiceProvider.GetRequiredService()); + + } + })); + channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip), port)); + } + + public async void SendAsync(byte[] data) + { + if (channel == null) throw new NullReferenceException("Channel Not Open"); + if (data == null) throw new ArgumentNullException("data is null"); + if (channel.Open && channel.Active) + { + await channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(data)); + } + } + + private void Dispose(bool disposing) + { + if (disposed) + { + return; + } + if (disposing) + { + //清理托管资源 + channel.CloseAsync(); + group.ShutdownGracefullyAsync(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3)); + } + //让类型知道自己已经被释放 + disposed = true; + } + + ~SubordinateLinkClient() + { + //必须为false + //这表明,隐式清理时,只要处理非托管资源就可以了。 + Dispose(false); + } + + public void Dispose() + { + //必须为true + Dispose(true); + //通知垃圾回收机制不再调用终结器(析构器) + GC.SuppressFinalize(this); + } + } +} diff --git a/src/JT809.DotNetty.Core/Metadata/JT809AtomicCounter.cs b/src/JT809.DotNetty.Core/Metadata/JT809AtomicCounter.cs new file mode 100644 index 0000000..7b00d70 --- /dev/null +++ b/src/JT809.DotNetty.Core/Metadata/JT809AtomicCounter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace JT809.DotNetty.Core.Metadata +{ + /// + /// + /// + /// + public class JT809AtomicCounter + { + long counter = 0; + + public JT809AtomicCounter(long initialCount = 0) + { + this.counter = initialCount; + } + + public void Reset() + { + Interlocked.Exchange(ref counter, 0); + } + + public long Increment() + { + return Interlocked.Increment(ref counter); + } + + public long Add(long len) + { + return Interlocked.Add(ref counter,len); + } + + public long Decrement() + { + return Interlocked.Decrement(ref counter); + } + + public long Count + { + get + { + return Interlocked.Read(ref counter); + } + } + } +} diff --git a/src/JT809.DotNetty.Core/Metadata/JT809Request.cs b/src/JT809.DotNetty.Core/Metadata/JT809Request.cs new file mode 100644 index 0000000..93a77fa --- /dev/null +++ b/src/JT809.DotNetty.Core/Metadata/JT809Request.cs @@ -0,0 +1,23 @@ +using JT809.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT809.DotNetty.Core.Metadata +{ + public class JT809Request + { + public JT809Package Package { get; } + + /// + /// 用于消息发送 + /// + public byte[] OriginalPackage { get;} + + public JT809Request(JT809Package package, byte[] originalPackage) + { + Package = package; + OriginalPackage = originalPackage; + } + } +} \ No newline at end of file diff --git a/src/JT809.DotNetty.Core/Metadata/JT809Response.cs b/src/JT809.DotNetty.Core/Metadata/JT809Response.cs new file mode 100644 index 0000000..ecf6aa1 --- /dev/null +++ b/src/JT809.DotNetty.Core/Metadata/JT809Response.cs @@ -0,0 +1,27 @@ +using JT809.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT809.DotNetty.Core.Metadata +{ + public class JT809Response + { + public JT809Package Package { get; set; } + /// + /// 根据实际情况适当调整包的大小 + /// + public int MinBufferSize { get; set; } + + public JT809Response() + { + + } + + public JT809Response(JT809Package package, int minBufferSize = 1024) + { + Package = package; + MinBufferSize = minBufferSize; + } + } +} \ No newline at end of file diff --git a/src/JT809.DotNetty.Core/Metadata/JT809TcpSession.cs b/src/JT809.DotNetty.Core/Metadata/JT809TcpSession.cs new file mode 100644 index 0000000..f6b514f --- /dev/null +++ b/src/JT809.DotNetty.Core/Metadata/JT809TcpSession.cs @@ -0,0 +1,28 @@ +using DotNetty.Transport.Channels; +using System; +using JT809.Protocol.Enums; + +namespace JT809.DotNetty.Core.Metadata +{ + public class JT809TcpSession + { + public JT809TcpSession(IChannel channel, uint msgGNSSCENTERID) + { + MsgGNSSCENTERID = msgGNSSCENTERID; + Channel = channel; + StartTime = DateTime.Now; + LastActiveTime = DateTime.Now; + } + + /// + /// 下级平台接入码,上级平台给下级平台分配唯一标识码。 + /// + public uint MsgGNSSCENTERID { get; set; } + + public IChannel Channel { get;} + + public DateTime LastActiveTime { get; set; } + + public DateTime StartTime { get; } + } +} diff --git a/src/JT809.DotNetty.Core/Services/JT809SimpleSystemCollectService.cs b/src/JT809.DotNetty.Core/Services/JT809SimpleSystemCollectService.cs new file mode 100644 index 0000000..bd013fe --- /dev/null +++ b/src/JT809.DotNetty.Core/Services/JT809SimpleSystemCollectService.cs @@ -0,0 +1,30 @@ +using JT809.DotNetty.Abstractions.Dtos; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace JT809.DotNetty.Core.Services +{ + /// + /// 简单系统收集服务 + /// + public class JT809SimpleSystemCollectService + { + /// + /// 获取系统当前进程使用情况 + /// + /// + public JT809SystemCollectInfoDto Get() + { + JT809SystemCollectInfoDto jT808SystemCollectInfoDto = new JT809SystemCollectInfoDto(); + var proc = Process.GetCurrentProcess(); + jT808SystemCollectInfoDto.ProcessId = proc.Id; + jT808SystemCollectInfoDto.WorkingSet64 = proc.WorkingSet64 / 1024.0 / 1024.0; + jT808SystemCollectInfoDto.PeakWorkingSet64 = proc.PeakWorkingSet64 / 1024.0 / 1024.0; + jT808SystemCollectInfoDto.PrivateMemorySize64 = proc.PrivateMemorySize64 / 1024.0 / 1024.0; + jT808SystemCollectInfoDto.CPUTotalProcessorTime = proc.TotalProcessorTime; + return jT808SystemCollectInfoDto; + } + } +} diff --git a/src/JT809.DotNetty.Core/Services/JT809TcpAtomicCounterService.cs b/src/JT809.DotNetty.Core/Services/JT809TcpAtomicCounterService.cs new file mode 100644 index 0000000..aa3b77f --- /dev/null +++ b/src/JT809.DotNetty.Core/Services/JT809TcpAtomicCounterService.cs @@ -0,0 +1,51 @@ +using JT809.DotNetty.Core.Metadata; + +namespace JT809.DotNetty.Core.Services +{ + /// + /// Tcp计数包服务 + /// + public class JT809TcpAtomicCounterService + { + private readonly JT809AtomicCounter MsgSuccessCounter = new JT809AtomicCounter(); + + private readonly JT809AtomicCounter MsgFailCounter = new JT809AtomicCounter(); + + public JT809TcpAtomicCounterService() + { + + } + + public void Reset() + { + MsgSuccessCounter.Reset(); + MsgFailCounter.Reset(); + } + + public long MsgSuccessIncrement() + { + return MsgSuccessCounter.Increment(); + } + + public long MsgSuccessCount + { + get + { + return MsgSuccessCounter.Count; + } + } + + public long MsgFailIncrement() + { + return MsgFailCounter.Increment(); + } + + public long MsgFailCount + { + get + { + return MsgFailCounter.Count; + } + } + } +} diff --git a/src/JT809.DotNetty.Core/Session/JT809TcpSessionManager.cs b/src/JT809.DotNetty.Core/Session/JT809TcpSessionManager.cs new file mode 100644 index 0000000..cba8419 --- /dev/null +++ b/src/JT809.DotNetty.Core/Session/JT809TcpSessionManager.cs @@ -0,0 +1,108 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using DotNetty.Transport.Channels; +using JT809.DotNetty.Abstractions; +using JT809.DotNetty.Core.Metadata; + +namespace JT809.DotNetty.Core +{ + /// + /// JT809 Tcp会话管理 + /// + public class JT809TcpSessionManager + { + private readonly ILogger logger; + + private readonly IJT809SessionPublishing jT809SessionPublishing; + + public JT809TcpSessionManager( + IJT809SessionPublishing jT809SessionPublishing, + ILoggerFactory loggerFactory) + { + this.jT809SessionPublishing = jT809SessionPublishing; + logger = loggerFactory.CreateLogger(); + } + + private ConcurrentDictionary SessionIdDict = new ConcurrentDictionary(); + + public int SessionCount + { + get + { + return SessionIdDict.Count; + } + } + + public JT809TcpSession GetSession(uint msgGNSSCENTERID) + { + if (SessionIdDict.TryGetValue(msgGNSSCENTERID, out JT809TcpSession targetSession)) + { + return targetSession; + } + else + { + return default; + } + } + + public void Heartbeat(uint msgGNSSCENTERID) + { + if (SessionIdDict.TryGetValue(msgGNSSCENTERID, out JT809TcpSession oldjT808Session)) + { + oldjT808Session.LastActiveTime = DateTime.Now; + SessionIdDict.TryUpdate(msgGNSSCENTERID, oldjT808Session, oldjT808Session); + } + } + + public void TryAdd(JT809TcpSession appSession) + { + if (SessionIdDict.TryAdd(appSession.MsgGNSSCENTERID, appSession)) + { + jT809SessionPublishing.PublishAsync(JT809Constants.SessionOnline, appSession.MsgGNSSCENTERID.ToString()); + } + } + + public JT809TcpSession RemoveSession(uint msgGNSSCENTERID) + { + //可以使用任意mq的发布订阅 + if (!SessionIdDict.TryGetValue(msgGNSSCENTERID, out JT809TcpSession jT808Session)) + { + return default; + } + if (SessionIdDict.TryRemove(msgGNSSCENTERID, out JT809TcpSession jT808SessionRemove)) + { + logger.LogInformation($">>>{msgGNSSCENTERID} Session Remove."); + jT809SessionPublishing.PublishAsync(JT809Constants.SessionOffline, msgGNSSCENTERID.ToString()); + return jT808SessionRemove; + } + else + { + return default; + } + } + + public void RemoveSessionByChannel(IChannel channel) + { + var keys = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList(); + if (keys.Count > 0) + { + foreach (var key in keys) + { + SessionIdDict.TryRemove(key, out JT809TcpSession jT808SessionRemove); + } + string nos = string.Join(",", keys); + logger.LogInformation($">>>{nos} Channel Remove."); + jT809SessionPublishing.PublishAsync(JT809Constants.SessionOffline, nos); + } + } + + public IEnumerable GetAll() + { + return SessionIdDict.Select(s => s.Value).ToList(); + } + } +} + diff --git a/src/JT809.DotNetty.Tcp/Handlers/JT809MsgIdDefaultTcpHandler.cs b/src/JT809.DotNetty.Tcp/Handlers/JT809MsgIdDefaultTcpHandler.cs new file mode 100644 index 0000000..6eae823 --- /dev/null +++ b/src/JT809.DotNetty.Tcp/Handlers/JT809MsgIdDefaultTcpHandler.cs @@ -0,0 +1,18 @@ +using JT809.DotNetty.Core; +using JT809.DotNetty.Core.Handlers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.DotNetty.Tcp.Handlers +{ + /// + /// 默认消息处理业务实现 + /// + internal class JT809MsgIdDefaultTcpHandler : JT809MsgIdTcpHandlerBase + { + public JT809MsgIdDefaultTcpHandler(JT809TcpSessionManager sessionManager) : base(sessionManager) + { + } + } +} diff --git a/src/JT809.DotNetty.Tcp/Handlers/JT809TcpConnectionHandler.cs b/src/JT809.DotNetty.Tcp/Handlers/JT809TcpConnectionHandler.cs new file mode 100644 index 0000000..8f11e2f --- /dev/null +++ b/src/JT809.DotNetty.Tcp/Handlers/JT809TcpConnectionHandler.cs @@ -0,0 +1,98 @@ +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Channels; +using JT809.DotNetty.Core; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace JT809.DotNetty.Tcp.Handlers +{ + /// + /// JT809服务通道处理程序 + /// + internal class JT809TcpConnectionHandler : ChannelHandlerAdapter + { + private readonly ILogger logger; + + private readonly JT809TcpSessionManager jT809SessionManager; + + public JT809TcpConnectionHandler( + JT809TcpSessionManager jT809SessionManager, + ILoggerFactory loggerFactory) + { + this.jT809SessionManager = jT809SessionManager; + logger = loggerFactory.CreateLogger(); + } + + /// + /// 通道激活 + /// + /// + public override void ChannelActive(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"<<<{ channelId } Successful client connection to server."); + base.ChannelActive(context); + } + + /// + /// 设备主动断开 + /// + /// + public override void ChannelInactive(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($">>>{ channelId } The client disconnects from the server."); + jT809SessionManager.RemoveSessionByChannel(context.Channel); + base.ChannelInactive(context); + } + + /// + /// 服务器主动断开 + /// + /// + /// + public override Task CloseAsync(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"<<<{ channelId } The server disconnects from the client."); + jT809SessionManager.RemoveSessionByChannel(context.Channel); + return base.CloseAsync(context); + } + + public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); + + /// + /// 超时策略 + /// + /// + /// + public override void UserEventTriggered(IChannelHandlerContext context, object evt) + { + IdleStateEvent idleStateEvent = evt as IdleStateEvent; + if (idleStateEvent != null) + { + if(idleStateEvent.State== IdleState.ReaderIdle) + { + string channelId = context.Channel.Id.AsShortText(); + logger.LogInformation($"{idleStateEvent.State.ToString()}>>>{channelId}"); + jT809SessionManager.RemoveSessionByChannel(context.Channel); + context.CloseAsync(); + } + } + base.UserEventTriggered(context, evt); + } + + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) + { + string channelId = context.Channel.Id.AsShortText(); + logger.LogError(exception,$"{channelId} {exception.Message}" ); + jT809SessionManager.RemoveSessionByChannel(context.Channel); + context.CloseAsync(); + } + } +} + diff --git a/src/JT809.DotNetty.Tcp/Handlers/JT809TcpServerHandler.cs b/src/JT809.DotNetty.Tcp/Handlers/JT809TcpServerHandler.cs new file mode 100644 index 0000000..0100139 --- /dev/null +++ b/src/JT809.DotNetty.Tcp/Handlers/JT809TcpServerHandler.cs @@ -0,0 +1,83 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using JT809.Protocol; +using System; +using Microsoft.Extensions.Logging; +using JT809.Protocol.Exceptions; +using JT809.DotNetty.Core.Services; +using JT809.DotNetty.Core; +using JT809.DotNetty.Core.Handlers; +using JT809.DotNetty.Core.Metadata; + +namespace JT809.DotNetty.Tcp.Handlers +{ + /// + /// JT809服务端处理程序 + /// + internal class JT809TcpServerHandler : SimpleChannelInboundHandler + { + private readonly JT809MsgIdTcpHandlerBase handler; + + private readonly JT809TcpSessionManager jT809SessionManager; + + private readonly JT809TcpAtomicCounterService jT809AtomicCounterService; + + private readonly ILogger logger; + + public JT809TcpServerHandler( + ILoggerFactory loggerFactory, + JT809MsgIdTcpHandlerBase handler, + JT809TcpAtomicCounterService jT809AtomicCounterService, + JT809TcpSessionManager jT809SessionManager + ) + { + this.handler = handler; + this.jT809SessionManager = jT809SessionManager; + this.jT809AtomicCounterService = jT809AtomicCounterService; + logger = loggerFactory.CreateLogger(); + } + + + protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg) + { + try + { + JT809Package jT809Package = JT809Serializer.Deserialize(msg); + jT809AtomicCounterService.MsgSuccessIncrement(); + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug("accept package success count<<<" + jT809AtomicCounterService.MsgSuccessCount.ToString()); + } + jT809SessionManager.TryAdd(new JT809TcpSession(ctx.Channel, jT809Package.Header.MsgGNSSCENTERID)); + Func handlerFunc; + if (handler.HandlerDict.TryGetValue(jT809Package.Header.BusinessType, out handlerFunc)) + { + JT809Response jT808Response = handlerFunc(new JT809Request(jT809Package, msg)); + if (jT808Response != null) + { + var sendData = JT809Serializer.Serialize(jT808Response.Package, jT808Response.MinBufferSize); + ctx.WriteAndFlushAsync(Unpooled.WrappedBuffer(sendData)); + } + } + } + catch (JT809Exception ex) + { + jT809AtomicCounterService.MsgFailIncrement(); + if (logger.IsEnabled(LogLevel.Error)) + { + logger.LogError("accept package fail count<<<" + jT809AtomicCounterService.MsgFailCount.ToString()); + logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg)); + } + } + catch (Exception ex) + { + jT809AtomicCounterService.MsgFailIncrement(); + if (logger.IsEnabled(LogLevel.Error)) + { + logger.LogError("accept package fail count<<<" + jT809AtomicCounterService.MsgFailCount.ToString()); + logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg)); + } + } + } + } +} diff --git a/src/JT809.DotNetty.Tcp/JT809.DotNetty.Tcp.csproj b/src/JT809.DotNetty.Tcp/JT809.DotNetty.Tcp.csproj new file mode 100644 index 0000000..16a978d --- /dev/null +++ b/src/JT809.DotNetty.Tcp/JT809.DotNetty.Tcp.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/JT809.DotNetty.Tcp/JT809TcpDotnettyExtensions.cs b/src/JT809.DotNetty.Tcp/JT809TcpDotnettyExtensions.cs new file mode 100644 index 0000000..2fff12e --- /dev/null +++ b/src/JT809.DotNetty.Tcp/JT809TcpDotnettyExtensions.cs @@ -0,0 +1,32 @@ +using JT809.DotNetty.Core; +using JT809.DotNetty.Core.Codecs; +using JT809.DotNetty.Core.Handlers; +using JT809.DotNetty.Core.Services; +using JT809.DotNetty.Tcp.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("JT809.DotNetty.Tcp.Test")] + +namespace JT809.DotNetty.Tcp +{ + public static class JT809TcpDotnettyExtensions + { + public static IServiceCollection AddJT809TcpHost(this IServiceCollection serviceDescriptors) + { + serviceDescriptors.TryAddSingleton(); + serviceDescriptors.TryAddSingleton(); + serviceDescriptors.TryAddSingleton(); + serviceDescriptors.TryAddScoped(); + serviceDescriptors.TryAddScoped(); + serviceDescriptors.TryAddScoped(); + serviceDescriptors.AddHostedService(); + return serviceDescriptors; + } + } +} \ No newline at end of file diff --git a/src/JT809.DotNetty.Tcp/JT809TcpServerHost.cs b/src/JT809.DotNetty.Tcp/JT809TcpServerHost.cs new file mode 100644 index 0000000..99e178c --- /dev/null +++ b/src/JT809.DotNetty.Tcp/JT809TcpServerHost.cs @@ -0,0 +1,95 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Libuv; +using JT809.DotNetty.Core.Configurations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using JT809.Protocol; +using JT809.DotNetty.Core.Codecs; +using JT809.DotNetty.Tcp.Handlers; + +namespace JT809.DotNetty.Tcp +{ + /// + /// JT809 Tcp网关服务 + /// + internal class JT809TcpServerHost : IHostedService + { + private readonly IServiceProvider serviceProvider; + private readonly JT809Configuration configuration; + private readonly ILogger logger; + private DispatcherEventLoopGroup bossGroup; + private WorkerEventLoopGroup workerGroup; + private IChannel bootstrapChannel; + private IByteBufferAllocator serverBufferAllocator; + + public JT809TcpServerHost( + IServiceProvider provider, + ILoggerFactory loggerFactory, + IOptions jT809ConfigurationAccessor) + { + serviceProvider = provider; + configuration = jT809ConfigurationAccessor.Value; + logger=loggerFactory.CreateLogger(); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + bossGroup = new DispatcherEventLoopGroup(); + workerGroup = new WorkerEventLoopGroup(bossGroup, configuration.EventLoopCount); + serverBufferAllocator = new PooledByteBufferAllocator(); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.Group(bossGroup, workerGroup); + bootstrap.Channel(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + bootstrap + .Option(ChannelOption.SoReuseport, true) + .ChildOption(ChannelOption.SoReuseaddr, true); + } + bootstrap + .Option(ChannelOption.SoBacklog, configuration.SoBacklog) + .ChildOption(ChannelOption.Allocator, serverBufferAllocator) + .ChildHandler(new ActionChannelInitializer(channel => + { + IChannelPipeline pipeline = channel.Pipeline; + using (var scope = serviceProvider.CreateScope()) + { + channel.Pipeline.AddLast("jt809SystemIdleState", new IdleStateHandler( + configuration.ReaderIdleTimeSeconds, + configuration.WriterIdleTimeSeconds, + configuration.AllIdleTimeSeconds)); + channel.Pipeline.AddLast("jt809TcpConnection", scope.ServiceProvider.GetRequiredService()); + channel.Pipeline.AddLast("jt809TcpBuffer", new DelimiterBasedFrameDecoder(int.MaxValue, + Unpooled.CopiedBuffer(new byte[] { JT809Package.BEGINFLAG }), + Unpooled.CopiedBuffer(new byte[] { JT809Package.ENDFLAG }))); + channel.Pipeline.AddLast("jt809TcpDecode", scope.ServiceProvider.GetRequiredService()); + channel.Pipeline.AddLast("jt809TcpService", scope.ServiceProvider.GetRequiredService()); + } + })); + logger.LogInformation($"JT809 TCP Server start at {IPAddress.Any}:{configuration.TcpPort}."); + return bootstrap.BindAsync(configuration.TcpPort) + .ContinueWith(i => bootstrapChannel = i.Result); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + await bootstrapChannel.CloseAsync(); + var quietPeriod = configuration.QuietPeriodTimeSpan; + var shutdownTimeout = configuration.ShutdownTimeoutTimeSpan; + await workerGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + await bossGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + } + } +} diff --git a/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/JT809.DotNetty.Host.Test.csproj b/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/JT809.DotNetty.Host.Test.csproj new file mode 100644 index 0000000..0e4fc83 --- /dev/null +++ b/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/JT809.DotNetty.Host.Test.csproj @@ -0,0 +1,27 @@ + + + + Exe + netcoreapp2.2 + 7.3 + + + + + + + + + + + + + Always + + + + + Always + + + diff --git a/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/Program.cs b/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/Program.cs new file mode 100644 index 0000000..ce6689e --- /dev/null +++ b/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/Program.cs @@ -0,0 +1,54 @@ +using JT809.DotNetty.Core; +using JT809.DotNetty.Tcp; +using JT809.Protocol.Configs; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace JT809.DotNetty.Host.Test +{ + class Program + { + static async Task Main(string[] args) + { + JT809.Protocol.JT809GlobalConfig.Instance + .SetHeaderOptions(new JT809HeaderOptions + { + MsgGNSSCENTERID = 20190222, + Version = new JT809.Protocol.JT809Header_Version(1, 0, 0), + EncryptKey = 9595 + }); + + //主链路登录请求消息 + //5B000000480000008510010134140E010000000000270F0134140E32303139303232323132372E302E302E31000000000000000000000000000000000000000000000003297B8D5D + //主链路注销请求消息 + //5B000000260000008510030134140E010000000000270F0001E24031323334353600003FE15D + //主链路连接保持请求消息 + //5B0000001A0000008510050134140E010000000000270FBA415D + + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureLogging((context, logging) => + { + logging.AddConsole(); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT809Core(hostContext.Configuration) + .AddJT809TcpHost(); + }); + + await serverHostBuilder.RunConsoleAsync(); + } + } +} diff --git a/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/appsettings.json b/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/appsettings.json new file mode 100644 index 0000000..c505ea5 --- /dev/null +++ b/src/JT809.DotNetty.Tests/JT809.DotNetty.Host.Test/appsettings.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "JT809Configuration": { + + } +} diff --git a/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/JT809.DotNetty.Tcp.Test.csproj b/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/JT809.DotNetty.Tcp.Test.csproj new file mode 100644 index 0000000..7ab5cb6 --- /dev/null +++ b/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/JT809.DotNetty.Tcp.Test.csproj @@ -0,0 +1,31 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + Always + + + + + + + + + + + + + + diff --git a/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/TestBase.cs b/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/TestBase.cs new file mode 100644 index 0000000..39aeefe --- /dev/null +++ b/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/TestBase.cs @@ -0,0 +1,37 @@ +using JT809.DotNetty.Core; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.DotNetty.Tcp.Test +{ + public class TestBase + { + public static IServiceProvider ServiceProvider; + + static TestBase() + { + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT809Core(hostContext.Configuration) + .AddJT809TcpHost(); + }); + var build = serverHostBuilder.Build(); + build.Start(); + ServiceProvider = build.Services; + + } + } +} diff --git a/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/appsettings.json b/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/appsettings.json new file mode 100644 index 0000000..c505ea5 --- /dev/null +++ b/src/JT809.DotNetty.Tests/JT809.DotNetty.Tcp.Test/appsettings.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "JT809Configuration": { + + } +} diff --git a/src/JT809.DotNetty.sln b/src/JT809.DotNetty.sln new file mode 100644 index 0000000..92f0622 --- /dev/null +++ b/src/JT809.DotNetty.sln @@ -0,0 +1,83 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2016 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809Netty.Core", "JT809Netty.Core\JT809Netty.Core.csproj", "{2054D7E6-53B6-412F-BE9D-C6DABD80A111}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809Netty.DownMasterLink", "JT809Netty.DownMasterLink\JT809Netty.DownMasterLink.csproj", "{3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809.DotNetty.Core", "JT809.DotNetty.Core\JT809.DotNetty.Core.csproj", "{0291C1D6-B4C6-4E7E-984B-0BAFB238727D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C712B2DE-34FE-4D9C-B574-A08B019246E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809.Protocol", "JT809.Protocol\src\JT809.Protocol\JT809.Protocol.csproj", "{321EE8EE-10D7-4233-8B8A-279BE68FB18A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT809.DotNetty.Tcp", "JT809.DotNetty.Tcp\JT809.DotNetty.Tcp.csproj", "{9BE94CDE-E813-403A-A68B-45C78BCAAF74}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT809.DotNetty.Abstractions", "JT809.DotNetty.Abstractions\JT809.DotNetty.Abstractions.csproj", "{EB8276CC-1848-4E7D-B77E-29B22AF767F0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DD4611CF-79A9-45C7-91EB-1E84D22B7D07}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT809.DotNetty.Tcp.Test", "JT809.DotNetty.Tests\JT809.DotNetty.Tcp.Test\JT809.DotNetty.Tcp.Test.csproj", "{560913C8-B618-46AD-B974-9D324F1ABBAC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT809.DotNetty.Host.Test", "JT809.DotNetty.Tests\JT809.DotNetty.Host.Test\JT809.DotNetty.Host.Test.csproj", "{D4E18559-C429-416F-9399-42C0E604D27B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809.Protocol.Extensions.DependencyInjection", "JT809.Protocol\src\JT809.Protocol.Extensions.DependencyInjection\JT809.Protocol.Extensions.DependencyInjection.csproj", "{975D959C-7C0B-418E-838E-EB383E912F8C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Release|Any CPU.Build.0 = Release|Any CPU + {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Release|Any CPU.Build.0 = Release|Any CPU + {0291C1D6-B4C6-4E7E-984B-0BAFB238727D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0291C1D6-B4C6-4E7E-984B-0BAFB238727D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0291C1D6-B4C6-4E7E-984B-0BAFB238727D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0291C1D6-B4C6-4E7E-984B-0BAFB238727D}.Release|Any CPU.Build.0 = Release|Any CPU + {321EE8EE-10D7-4233-8B8A-279BE68FB18A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {321EE8EE-10D7-4233-8B8A-279BE68FB18A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {321EE8EE-10D7-4233-8B8A-279BE68FB18A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {321EE8EE-10D7-4233-8B8A-279BE68FB18A}.Release|Any CPU.Build.0 = Release|Any CPU + {9BE94CDE-E813-403A-A68B-45C78BCAAF74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BE94CDE-E813-403A-A68B-45C78BCAAF74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BE94CDE-E813-403A-A68B-45C78BCAAF74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BE94CDE-E813-403A-A68B-45C78BCAAF74}.Release|Any CPU.Build.0 = Release|Any CPU + {EB8276CC-1848-4E7D-B77E-29B22AF767F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB8276CC-1848-4E7D-B77E-29B22AF767F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB8276CC-1848-4E7D-B77E-29B22AF767F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB8276CC-1848-4E7D-B77E-29B22AF767F0}.Release|Any CPU.Build.0 = Release|Any CPU + {560913C8-B618-46AD-B974-9D324F1ABBAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {560913C8-B618-46AD-B974-9D324F1ABBAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {560913C8-B618-46AD-B974-9D324F1ABBAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {560913C8-B618-46AD-B974-9D324F1ABBAC}.Release|Any CPU.Build.0 = Release|Any CPU + {D4E18559-C429-416F-9399-42C0E604D27B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4E18559-C429-416F-9399-42C0E604D27B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4E18559-C429-416F-9399-42C0E604D27B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4E18559-C429-416F-9399-42C0E604D27B}.Release|Any CPU.Build.0 = Release|Any CPU + {975D959C-7C0B-418E-838E-EB383E912F8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {975D959C-7C0B-418E-838E-EB383E912F8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {975D959C-7C0B-418E-838E-EB383E912F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {975D959C-7C0B-418E-838E-EB383E912F8C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {321EE8EE-10D7-4233-8B8A-279BE68FB18A} = {C712B2DE-34FE-4D9C-B574-A08B019246E4} + {560913C8-B618-46AD-B974-9D324F1ABBAC} = {DD4611CF-79A9-45C7-91EB-1E84D22B7D07} + {D4E18559-C429-416F-9399-42C0E604D27B} = {DD4611CF-79A9-45C7-91EB-1E84D22B7D07} + {975D959C-7C0B-418E-838E-EB383E912F8C} = {C712B2DE-34FE-4D9C-B574-A08B019246E4} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0FC2A52E-3B7A-4485-9C3B-9080C825419D} + EndGlobalSection +EndGlobal diff --git a/src/JT809.Protocol b/src/JT809.Protocol index b84dec6..359a347 160000 --- a/src/JT809.Protocol +++ b/src/JT809.Protocol @@ -1 +1 @@ -Subproject commit b84dec6af9a812ea0ea02850987fd4455d1f20ee +Subproject commit 359a347cec6a1ee1ce3544d8ca399a94cadd9484 diff --git a/src/JT809Netty.sln b/src/JT809Netty.sln deleted file mode 100644 index 42fdaa3..0000000 --- a/src/JT809Netty.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2016 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809Netty.Core", "JT809Netty.Core\JT809Netty.Core.csproj", "{2054D7E6-53B6-412F-BE9D-C6DABD80A111}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809Netty.DownMasterLink", "JT809Netty.DownMasterLink\JT809Netty.DownMasterLink.csproj", "{3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2054D7E6-53B6-412F-BE9D-C6DABD80A111}.Release|Any CPU.Build.0 = Release|Any CPU - {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3BF1D40D-A17D-4FBC-B3FF-B6DF8B3F13ED}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {0FC2A52E-3B7A-4485-9C3B-9080C825419D} - EndGlobalSection -EndGlobal