diff --git a/src/JT808.DotNetty/Dtos/JT808UnificationSendRequestDto.cs b/src/JT808.DotNetty/Dtos/JT808UnificationSendRequestDto.cs new file mode 100644 index 0000000..5b565f1 --- /dev/null +++ b/src/JT808.DotNetty/Dtos/JT808UnificationSendRequestDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Dtos +{ + /// + /// 统一下发请求参数 + /// + public class JT808UnificationSendRequestDto + { + public string TerminalPhoneNo { get; set; } + public byte[] Data { get; set; } + } +} diff --git a/src/JT808.DotNetty/Handlers/JT808ConnectionHandler.cs b/src/JT808.DotNetty/Handlers/JT808ConnectionHandler.cs index c14d70c..8646687 100644 --- a/src/JT808.DotNetty/Handlers/JT808ConnectionHandler.cs +++ b/src/JT808.DotNetty/Handlers/JT808ConnectionHandler.cs @@ -64,10 +64,7 @@ namespace JT808.DotNetty.Handlers return base.CloseAsync(context); } - public override void ChannelReadComplete(IChannelHandlerContext context) - { - context.Flush(); - } + public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); /// /// 超时策略 diff --git a/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs b/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs index f30e557..31a0365 100644 --- a/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs +++ b/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs @@ -1,97 +1,58 @@ using DotNetty.Buffers; using DotNetty.Codecs.Http; -using DotNetty.Common; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; -using JT808.DotNetty.Dtos; -using JT808.DotNetty.Interfaces; +using JT808.DotNetty.Metadata; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using System; -using System.Collections.Generic; using System.Text; namespace JT808.DotNetty.Handlers { /// /// jt808 webapi服务 - /// 请求量不大,只支持JSON格式 + /// 请求量不大,只支持JSON格式并且只支持post发数据 /// ref: dotnetty HttpServer /// - internal class JT808WebAPIServerHandler : ChannelHandlerAdapter + internal class JT808WebAPIServerHandler : SimpleChannelInboundHandler { - private static readonly ThreadLocalCache Cache = new ThreadLocalCache(); - - sealed class ThreadLocalCache : FastThreadLocal - { - protected override AsciiString GetInitialValue() - { - DateTime dateTime = DateTime.UtcNow; - return AsciiString.Cached($"{dateTime.DayOfWeek}, {dateTime:dd MMM yyyy HH:mm:ss z}"); - } - } - private static readonly AsciiString TypeJson = AsciiString.Cached("application/json"); private static readonly AsciiString ServerName = AsciiString.Cached("JT808WebAPINetty"); private static readonly AsciiString ContentTypeEntity = HttpHeaderNames.ContentType; private static readonly AsciiString DateEntity = HttpHeaderNames.Date; private static readonly AsciiString ContentLengthEntity = HttpHeaderNames.ContentLength; private static readonly AsciiString ServerEntity = HttpHeaderNames.Server; - - volatile ICharSequence date = Cache.Value; - + private readonly JT808WebAPIService jT808WebAPIService; private readonly ILogger logger; - private readonly IJT808SessionService jT808SessionService; - - private readonly IJT808UnificationSendService jT808UnificationSendService; - public JT808WebAPIServerHandler( - IJT808SessionService jT808SessionService, - IJT808UnificationSendService jT808UnificationSendService, + JT808WebAPIService jT808WebAPIService, ILoggerFactory loggerFactory) { - this.jT808SessionService = jT808SessionService; - this.jT808UnificationSendService = jT808UnificationSendService; + this.jT808WebAPIService = jT808WebAPIService; logger = loggerFactory.CreateLogger(); } - public override void ChannelRead(IChannelHandlerContext ctx, object message) + protected override void ChannelRead0(IChannelHandlerContext ctx, IFullHttpRequest msg) { - if (message is IHttpRequest request) + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug($"Uri:{msg.Uri}"); + logger.LogDebug($"Content:{msg.Content.ToString(Encoding.UTF8)}"); + } + JT808HttpResponse jT808HttpResponse = null; + if (jT808WebAPIService.HandlerDict.TryGetValue(msg.Uri,out var funcHandler)) { - try - { - Process(ctx, request); - } - finally - { - ReferenceCountUtil.Release(message); - } + jT808HttpResponse = funcHandler( new JT808HttpRequest() { Json = msg.Content.ToString(Encoding.UTF8)}); } else { - ctx.FireChannelRead(message); + jT808HttpResponse = jT808WebAPIService.NotFoundHttpResponse(); } - } - - private void Process(IChannelHandlerContext ctx, IHttpRequest request) - { - string uri = request.Uri; - //switch (uri) - //{ - // //case "/json": - // // byte[] json = Encoding.UTF8.GetBytes(NewMessage().ToJsonFormat()); - // // this.WriteResponse(ctx, Unpooled.WrappedBuffer(json), TypeJson, JsonClheaderValue); - // // break; - // default: - // var response = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.NotFound, Unpooled.Empty, false); - // ctx.WriteAndFlushAsync(response); - // ctx.CloseAsync(); - // break; - //} - byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808DefaultResultDto())); - this.WriteResponse(ctx, Unpooled.WrappedBuffer(json), TypeJson, json.Length); + if (jT808HttpResponse != null) + { + WriteResponse(ctx, Unpooled.WrappedBuffer(jT808HttpResponse.Data), TypeJson, jT808HttpResponse.Data.Length); + } } private void WriteResponse(IChannelHandlerContext ctx, IByteBuffer buf, ICharSequence contentType, int contentLength) @@ -101,16 +62,17 @@ namespace JT808.DotNetty.Handlers HttpHeaders headers = response.Headers; headers.Set(ContentTypeEntity, contentType); headers.Set(ServerEntity, ServerName); - headers.Set(DateEntity, this.date); + headers.Set(DateEntity, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); headers.Set(ContentLengthEntity, contentLength); // Close the non-keep-alive connection after the write operation is done. - ctx.WriteAsync(response); + ctx.WriteAndFlushAsync(response); + ctx.CloseAsync(); } public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { - string channelId = context.Channel.Id.AsShortText(); - logger.LogError(exception, $"{channelId} {exception.Message}"); + WriteResponse(context, Unpooled.WrappedBuffer(jT808WebAPIService.ErrorHttpResponse(exception).Data), TypeJson, jT808WebAPIService.ErrorHttpResponse(exception).Data.Length); + logger.LogError(exception, exception.Message); context.CloseAsync(); } diff --git a/src/JT808.DotNetty/Interfaces/IJT808SessionService.cs b/src/JT808.DotNetty/Interfaces/IJT808SessionService.cs index 7eddb27..4b7dcfa 100644 --- a/src/JT808.DotNetty/Interfaces/IJT808SessionService.cs +++ b/src/JT808.DotNetty/Interfaces/IJT808SessionService.cs @@ -8,7 +8,7 @@ namespace JT808.DotNetty.Interfaces /// /// JT808会话服务 /// - public interface IJT808SessionService + internal interface IJT808SessionService { /// /// 获取真实连接数 diff --git a/src/JT808.DotNetty/Interfaces/IJT808UnificationSendService.cs b/src/JT808.DotNetty/Interfaces/IJT808UnificationSendService.cs index 2477e7b..b7434c3 100644 --- a/src/JT808.DotNetty/Interfaces/IJT808UnificationSendService.cs +++ b/src/JT808.DotNetty/Interfaces/IJT808UnificationSendService.cs @@ -8,7 +8,7 @@ namespace JT808.DotNetty.Interfaces /// /// JT808统一下发命令 /// - public interface IJT808UnificationSendService + internal interface IJT808UnificationSendService { JT808ResultDto Send(string terminalPhoneNo, byte[] data); } diff --git a/src/JT808.DotNetty/JT808DotnettyExtensions.cs b/src/JT808.DotNetty/JT808DotnettyExtensions.cs index 4951a61..b151ec8 100644 --- a/src/JT808.DotNetty/JT808DotnettyExtensions.cs +++ b/src/JT808.DotNetty/JT808DotnettyExtensions.cs @@ -24,9 +24,10 @@ namespace JT808.DotNetty services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); - services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddScoped(); + services.TryAddScoped(); services.AddHostedService(); services.AddHostedService(); }); diff --git a/src/JT808.DotNetty/JT808SessionManager.cs b/src/JT808.DotNetty/JT808SessionManager.cs index a8d3c37..178971e 100644 --- a/src/JT808.DotNetty/JT808SessionManager.cs +++ b/src/JT808.DotNetty/JT808SessionManager.cs @@ -13,31 +13,18 @@ using JT808.DotNetty.Metadata; namespace JT808.DotNetty { - public class JT808SessionManager: IDisposable + public class JT808SessionManager { private readonly ILogger logger; + private readonly JT808Configuration configuration; - private readonly CancellationTokenSource cancellationTokenSource; + public JT808SessionManager( IOptions jT808ConfigurationAccessor, ILoggerFactory loggerFactory) { logger = loggerFactory.CreateLogger(); configuration = jT808ConfigurationAccessor.Value; - cancellationTokenSource = new CancellationTokenSource(); - Task.Run(() => - { - while (!cancellationTokenSource.IsCancellationRequested) - { - logger.LogInformation($"Online Count>>>{RealSessionCount}"); - if (RealSessionCount > 0) - { - logger.LogInformation($"SessionIds>>>{string.Join(",", SessionIdDict.Select(s => s.Key))}"); - logger.LogInformation($"TerminalPhoneNos>>>{string.Join(",", TerminalPhoneNo_SessionId_Dict.Select(s => $"{s.Key}-{s.Value}"))}"); - } - Thread.Sleep(configuration.SessionReportTime); - } - }, cancellationTokenSource.Token); } /// @@ -212,12 +199,7 @@ namespace JT808.DotNetty { return SessionIdDict.Join(TerminalPhoneNo_SessionId_Dict, m => m.Key, s => s.Value, (m, s) => m.Value).ToList(); } - - public void Dispose() - { - cancellationTokenSource.Cancel(); - cancellationTokenSource.Dispose(); - } + } } diff --git a/src/JT808.DotNetty/JT808WebAPIServerHost.cs b/src/JT808.DotNetty/JT808WebAPIServerHost.cs index 6e0c39c..7aea178 100644 --- a/src/JT808.DotNetty/JT808WebAPIServerHost.cs +++ b/src/JT808.DotNetty/JT808WebAPIServerHost.cs @@ -9,10 +9,8 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; -using System.Collections.Generic; using System.Net; using System.Runtime.InteropServices; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -61,9 +59,11 @@ namespace JT808.DotNetty IChannelPipeline pipeline = channel.Pipeline; using (var scope = serviceProvider.CreateScope()) { - pipeline.AddLast("encoder", new HttpResponseEncoder()); - pipeline.AddLast("decoder", new HttpRequestDecoder(4096, 8192, 8192, false)); - pipeline.AddLast("jt808webapihandler", scope.ServiceProvider.GetRequiredService()); + pipeline.AddLast("http_encoder", new HttpResponseEncoder()); + pipeline.AddLast("http_decoder", new HttpRequestDecoder(4096, 8192, 8192, false)); + //将多个消息转换为单一的request或者response对象 =>IFullHttpRequest + pipeline.AddLast("http_aggregator", new HttpObjectAggregator(65536)); + pipeline.AddLast("http_jt808webapihandler", scope.ServiceProvider.GetRequiredService()); } })); logger.LogInformation($"WebAPI Server start at {IPAddress.Any}:{configuration.WebAPIPort}."); diff --git a/src/JT808.DotNetty/JT808WebAPIService.cs b/src/JT808.DotNetty/JT808WebAPIService.cs new file mode 100644 index 0000000..bf75288 --- /dev/null +++ b/src/JT808.DotNetty/JT808WebAPIService.cs @@ -0,0 +1,214 @@ +using JT808.DotNetty.Dtos; +using JT808.DotNetty.Interfaces; +using JT808.DotNetty.Metadata; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty +{ + internal class JT808WebAPIService + { + public Dictionary> HandlerDict { get; protected set; } + + private const string RouteTablePrefix = "/jt808api"; + + private const string sessionRoutePrefix = "Session"; + + private readonly IJT808SessionService jT808SessionService; + + private readonly IJT808UnificationSendService jT808UnificationSendService; + + /// + /// 初始化消息处理业务 + /// + protected JT808WebAPIService( + IJT808SessionService jT808SessionService, + IJT808UnificationSendService jT808UnificationSendService) + { + this.jT808SessionService = jT808SessionService; + this.jT808UnificationSendService = jT808UnificationSendService; + HandlerDict = new Dictionary> + { + {$"{RouteTablePrefix}/UnificationSend", UnificationSend}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/GetRealLinkCount", GetRealLinkCount}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/GetRelevanceLinkCount", GetRelevanceLinkCount}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/GetRealAll", GetRealAll}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/GetRelevanceAll", GetRelevanceAll}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/RemoveByChannelId", RemoveByChannelId}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/RemoveByTerminalPhoneNo", RemoveByTerminalPhoneNo}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/GetByChannelId", GetByChannelId}, + {$"{RouteTablePrefix}/{sessionRoutePrefix}/GetByTerminalPhoneNo", GetByTerminalPhoneNo}, + }; + } + + /// + /// 统一下发信息 + /// + /// + /// + public JT808HttpResponse UnificationSend(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + JT808UnificationSendRequestDto jT808UnificationSendRequestDto = JsonConvert.DeserializeObject(request.Json); + var result = jT808UnificationSendService.Send(jT808UnificationSendRequestDto.TerminalPhoneNo, jT808UnificationSendRequestDto.Data); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-获取真实连接数 + /// + /// + /// + public JT808HttpResponse GetRealLinkCount(JT808HttpRequest request) + { + var result = jT808SessionService.GetRealAll(); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-获取设备相关连的连接数 + /// + /// + /// + public JT808HttpResponse GetRelevanceLinkCount(JT808HttpRequest request) + { + var result = jT808SessionService.GetRelevanceLinkCount(); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-获取实际会话集合 + /// + /// + /// + public JT808HttpResponse GetRealAll(JT808HttpRequest request) + { + var result = jT808SessionService.GetRealAll(); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-获取设备相关连会话集合 + /// + /// + /// + public JT808HttpResponse GetRelevanceAll(JT808HttpRequest request) + { + var result = jT808SessionService.GetRelevanceAll(); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-通过通道Id移除对应会话 + /// + /// + /// + public JT808HttpResponse RemoveByChannelId(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + var result = jT808SessionService.RemoveByChannelId(request.Json); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-通过设备终端号移除对应会话 + /// + /// + /// + public JT808HttpResponse RemoveByTerminalPhoneNo(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + var result = jT808SessionService.RemoveByTerminalPhoneNo(request.Json); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-通过通道Id获取会话信息 + /// + /// + /// + public JT808HttpResponse GetByChannelId(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + var result = jT808SessionService.GetByChannelId(request.Json); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-通过设备终端号获取会话信息 + /// + /// + /// + public JT808HttpResponse GetByTerminalPhoneNo(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + var result = jT808SessionService.GetByTerminalPhoneNo(request.Json); + return CreateJT808HttpResponse(result); + } + + private JT808HttpResponse CreateJT808HttpResponse(dynamic dynamicObject) + { + byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dynamicObject)); + return new JT808HttpResponse() + { + Data = data + }; + } + + public JT808HttpResponse DefaultHttpResponse() + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808DefaultResultDto())); + return new JT808HttpResponse(json); + } + + public JT808HttpResponse EmptyHttpResponse() + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto() + { + Code=201, + Message="内容为空", + Data="Content Empty" + })); + return new JT808HttpResponse(json); + } + + public JT808HttpResponse NotFoundHttpResponse() + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto() + { + Code=404, + Message="没有该服务", + Data= "没有该服务" + })); + return new JT808HttpResponse(json); + } + + public JT808HttpResponse ErrorHttpResponse(Exception ex) + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto() + { + Code = 500, + Message = JsonConvert.SerializeObject(ex), + Data= ex.Message + })); + return new JT808HttpResponse(json); + } + } +} diff --git a/src/JT808.DotNetty/Metadata/JT808HttpRequest.cs b/src/JT808.DotNetty/Metadata/JT808HttpRequest.cs new file mode 100644 index 0000000..6a16264 --- /dev/null +++ b/src/JT808.DotNetty/Metadata/JT808HttpRequest.cs @@ -0,0 +1,22 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Metadata +{ + public class JT808HttpRequest + { + public string Json { get; set; } + + public JT808HttpRequest() + { + + } + + public JT808HttpRequest(string json) + { + Json = json; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty/Metadata/JT808HttpResponse.cs b/src/JT808.DotNetty/Metadata/JT808HttpResponse.cs new file mode 100644 index 0000000..4ab0b95 --- /dev/null +++ b/src/JT808.DotNetty/Metadata/JT808HttpResponse.cs @@ -0,0 +1,22 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Metadata +{ + public class JT808HttpResponse + { + public byte[] Data { get; set; } + + public JT808HttpResponse() + { + + } + + public JT808HttpResponse(byte[] data) + { + this.Data = data; + } + } +} \ No newline at end of file