diff --git a/src/JT808.DotNetty/Configurations/JT808Configuration.cs b/src/JT808.DotNetty/Configurations/JT808Configuration.cs index adbb104..4c73388 100644 --- a/src/JT808.DotNetty/Configurations/JT808Configuration.cs +++ b/src/JT808.DotNetty/Configurations/JT808Configuration.cs @@ -25,6 +25,13 @@ namespace JT808.DotNetty.Configurations public int WriterIdleTimeSeconds { get; set; } = 3600; public int AllIdleTimeSeconds { get; set; } = 3600; + + /// + /// WebAPI服务 + /// 默认828端口 + /// + public int WebAPIPort { get; set; } = 828; + /// /// 会话报时 /// 默认5分钟 diff --git a/src/JT808.DotNetty/Dtos/JT808DefaultResultDto.cs b/src/JT808.DotNetty/Dtos/JT808DefaultResultDto.cs new file mode 100644 index 0000000..58b18dc --- /dev/null +++ b/src/JT808.DotNetty/Dtos/JT808DefaultResultDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Dtos +{ + public class JT808DefaultResultDto: JT808ResultDto + { + public JT808DefaultResultDto() + { + Data = "Hello,JT808 WebAPI"; + Code = 200; + } + } +} diff --git a/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs b/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs new file mode 100644 index 0000000..f30e557 --- /dev/null +++ b/src/JT808.DotNetty/Handlers/JT808WebAPIServerHandler.cs @@ -0,0 +1,119 @@ +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 Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Handlers +{ + /// + /// jt808 webapi服务 + /// 请求量不大,只支持JSON格式 + /// ref: dotnetty HttpServer + /// + internal class JT808WebAPIServerHandler : ChannelHandlerAdapter + { + 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 ILogger logger; + + private readonly IJT808SessionService jT808SessionService; + + private readonly IJT808UnificationSendService jT808UnificationSendService; + + public JT808WebAPIServerHandler( + IJT808SessionService jT808SessionService, + IJT808UnificationSendService jT808UnificationSendService, + ILoggerFactory loggerFactory) + { + this.jT808SessionService = jT808SessionService; + this.jT808UnificationSendService = jT808UnificationSendService; + logger = loggerFactory.CreateLogger(); + } + + public override void ChannelRead(IChannelHandlerContext ctx, object message) + { + if (message is IHttpRequest request) + { + try + { + Process(ctx, request); + } + finally + { + ReferenceCountUtil.Release(message); + } + } + else + { + ctx.FireChannelRead(message); + } + } + + 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); + } + + private void WriteResponse(IChannelHandlerContext ctx, IByteBuffer buf, ICharSequence contentType, int contentLength) + { + // Build the response object. + var response = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK, buf, false); + HttpHeaders headers = response.Headers; + headers.Set(ContentTypeEntity, contentType); + headers.Set(ServerEntity, ServerName); + headers.Set(DateEntity, this.date); + headers.Set(ContentLengthEntity, contentLength); + // Close the non-keep-alive connection after the write operation is done. + ctx.WriteAsync(response); + } + + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) + { + string channelId = context.Channel.Id.AsShortText(); + logger.LogError(exception, $"{channelId} {exception.Message}"); + context.CloseAsync(); + } + + public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); + } +} diff --git a/src/JT808.DotNetty/JT808.DotNetty.csproj b/src/JT808.DotNetty/JT808.DotNetty.csproj index 608982c..11c8ee7 100644 --- a/src/JT808.DotNetty/JT808.DotNetty.csproj +++ b/src/JT808.DotNetty/JT808.DotNetty.csproj @@ -7,11 +7,10 @@ + - - diff --git a/src/JT808.DotNetty/JT808DotnettyExtensions.cs b/src/JT808.DotNetty/JT808DotnettyExtensions.cs index bbefa67..4951a61 100644 --- a/src/JT808.DotNetty/JT808DotnettyExtensions.cs +++ b/src/JT808.DotNetty/JT808DotnettyExtensions.cs @@ -24,24 +24,12 @@ namespace JT808.DotNetty services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); + services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); services.AddHostedService(); + services.AddHostedService(); }); } - - public static void UseJT808Host(this IServiceCollection serviceDescriptors, HostBuilderContext hostContext) - { - serviceDescriptors.Configure(hostContext.Configuration.GetSection("JT808Configuration")); - serviceDescriptors.TryAddSingleton(); - serviceDescriptors.TryAddSingleton(); - serviceDescriptors.TryAddSingleton(); - serviceDescriptors.TryAddScoped(); - serviceDescriptors.TryAddScoped(); - serviceDescriptors.TryAddScoped(); - serviceDescriptors.TryAddSingleton(); - serviceDescriptors.TryAddSingleton(); - serviceDescriptors.AddHostedService(); - } } } \ No newline at end of file diff --git a/src/JT808.DotNetty/JT808ServerHost.cs b/src/JT808.DotNetty/JT808ServerHost.cs index 4ae3e94..1674583 100644 --- a/src/JT808.DotNetty/JT808ServerHost.cs +++ b/src/JT808.DotNetty/JT808ServerHost.cs @@ -21,9 +21,9 @@ using System.Threading.Tasks; namespace JT808.DotNetty { - public class JT808ServerHost : IHostedService + internal class JT808ServerHost : IHostedService { - public IServiceProvider Provider { get; } + private readonly IServiceProvider serviceProvider; private readonly JT808Configuration configuration; private readonly ILogger logger; private DispatcherEventLoopGroup bossGroup; @@ -35,7 +35,7 @@ namespace JT808.DotNetty ILoggerFactory loggerFactory, IOptions jT808ConfigurationAccessor) { - Provider = provider; + serviceProvider = provider; configuration = jT808ConfigurationAccessor.Value; logger=loggerFactory.CreateLogger(); } @@ -59,7 +59,7 @@ namespace JT808.DotNetty .ChildHandler(new ActionChannelInitializer(channel => { IChannelPipeline pipeline = channel.Pipeline; - using(var scope= Provider.CreateScope()) + using (var scope = serviceProvider.CreateScope()) { channel.Pipeline.AddLast("systemIdleState", new IdleStateHandler( configuration.ReaderIdleTimeSeconds, diff --git a/src/JT808.DotNetty/JT808WebAPIServerHost.cs b/src/JT808.DotNetty/JT808WebAPIServerHost.cs new file mode 100644 index 0000000..6e0c39c --- /dev/null +++ b/src/JT808.DotNetty/JT808WebAPIServerHost.cs @@ -0,0 +1,82 @@ +using DotNetty.Codecs.Http; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Libuv; +using JT808.DotNetty.Configurations; +using JT808.DotNetty.Handlers; +using Microsoft.Extensions.DependencyInjection; +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; + +namespace JT808.DotNetty +{ + /// + /// 集成一个webapi服务 + /// + internal class JT808WebAPIServerHost : IHostedService + { + private readonly IServiceProvider serviceProvider; + private readonly JT808Configuration configuration; + private readonly ILogger logger; + private DispatcherEventLoopGroup bossGroup; + private WorkerEventLoopGroup workerGroup; + private IChannel bootstrapChannel; + + public JT808WebAPIServerHost( + IServiceProvider provider, + ILoggerFactory loggerFactory, + IOptions jT808ConfigurationAccessor) + { + serviceProvider = provider; + configuration = jT808ConfigurationAccessor.Value; + logger = loggerFactory.CreateLogger(); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + bossGroup = new DispatcherEventLoopGroup(); + workerGroup = new WorkerEventLoopGroup(bossGroup, 1); + 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, 8192) + .ChildHandler(new ActionChannelInitializer(channel => + { + 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()); + } + })); + logger.LogInformation($"WebAPI Server start at {IPAddress.Any}:{configuration.WebAPIPort}."); + return bootstrap.BindAsync(configuration.WebAPIPort).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/JT808.DotNetty/JT808WebHostBuilderKestrelExtensions.cs b/src/JT808.DotNetty/JT808WebHostBuilderKestrelExtensions.cs deleted file mode 100644 index dbcfc73..0000000 --- a/src/JT808.DotNetty/JT808WebHostBuilderKestrelExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection.Abstractions; -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting.Server; - -namespace JT808.DotNetty -{ - public static class JT808WebHostBuilderKestrelExtensions - { - /// - /// Specify Kestrel as the server to be used by the web host. - /// - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. - /// - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. - /// - public static IHostBuilder UseKestrel(this IHostBuilder hostBuilder, Action options) - { - return hostBuilder.ConfigureServices((context,services) => - { - services.Configure(options); - // Don't override an already-configured transport - services.TryAddSingleton(); - services.AddTransient, KestrelServerOptionsSetup>(); - services.AddSingleton(); - }); - } - } -}