@@ -0,0 +1,15 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.DotNetty.Abstractions.Dtos | |||||
{ | |||||
public class JT809DefaultResultDto: JT809ResultDto<string> | |||||
{ | |||||
public JT809DefaultResultDto() | |||||
{ | |||||
Data = "Hello,JT809 Superior WebAPI"; | |||||
Code = JT809ResultCode.Ok; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.DotNetty.Abstractions.Dtos | |||||
{ | |||||
/// <summary> | |||||
/// 健康检查 | |||||
/// </summary> | |||||
public class JT809HealthCheckDto | |||||
{ | |||||
/// <summary> | |||||
/// 主链路会话状态集合 | |||||
/// </summary> | |||||
public List<JT809SessionInfoDto> MainSessions { get; set; } | |||||
/// <summary> | |||||
/// 从链路会话状态集合 | |||||
/// </summary> | |||||
public List<JT809SessionInfoDto> SubordinateSessions { get; set; } | |||||
/// <summary> | |||||
/// 应用程序使用率 | |||||
/// </summary> | |||||
public JT809SystemCollectInfoDto SystemCollect { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,29 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.DotNetty.Abstractions.Dtos | |||||
{ | |||||
public class JT809ResultDto<T> | |||||
{ | |||||
public JT809ResultDto() | |||||
{ | |||||
Code = JT809ResultCode.Ok; | |||||
} | |||||
public string Message { get; set; } | |||||
public int Code { get; set; } | |||||
public T Data { get; set; } | |||||
} | |||||
public class JT809ResultCode | |||||
{ | |||||
public const int Ok = 200; | |||||
public const int Empty = 201; | |||||
public const int NotFound = 404; | |||||
public const int Fail = 400; | |||||
public const int Error = 500; | |||||
} | |||||
} |
@@ -0,0 +1,28 @@ | |||||
using System; | |||||
namespace JT809.DotNetty.Abstractions.Dtos | |||||
{ | |||||
public class JT809SessionInfoDto | |||||
{ | |||||
/// <summary> | |||||
/// 最后上线时间 | |||||
/// </summary> | |||||
public string LastActiveTime { get; set; } | |||||
/// <summary> | |||||
/// 上线时间 | |||||
/// </summary> | |||||
public string StartTime { get; set; } | |||||
/// <summary> | |||||
/// 通道是否打开 | |||||
/// </summary> | |||||
public bool Open { get; set; } | |||||
/// <summary> | |||||
/// 通道是否活跃 | |||||
/// </summary> | |||||
public bool Active { get; set; } | |||||
/// <summary> | |||||
/// 远程IP地址 | |||||
/// </summary> | |||||
public string RemoteIP { get; set; } | |||||
} | |||||
} |
@@ -6,7 +6,7 @@ | |||||
public const string SessionOffline = "JT809SessionOffline"; | public const string SessionOffline = "JT809SessionOffline"; | ||||
public static class JT809WebApiRouteTable | |||||
public static class JT809SuperiorWebApiRouteTable | |||||
{ | { | ||||
public const string RouteTablePrefix = "/jt809api"; | public const string RouteTablePrefix = "/jt809api"; | ||||
@@ -14,12 +14,19 @@ | |||||
public const string SystemCollectPrefix = "SystemCollect"; | public const string SystemCollectPrefix = "SystemCollect"; | ||||
public const string TcpPrefix = "Tcp"; | |||||
public const string HealthCheck = "HealthCheck"; | |||||
public const string Prefix = "Main"; | |||||
/// <summary> | /// <summary> | ||||
///获取当前系统进程使用率 | ///获取当前系统进程使用率 | ||||
/// </summary> | /// </summary> | ||||
public static string SystemCollectGet = $"{RouteTablePrefix}/{SystemCollectPrefix}/Get"; | |||||
public static string SystemCollectGet = $"{RouteTablePrefix}/{Prefix}/{SystemCollectPrefix}"; | |||||
/// <summary> | |||||
///健康检查 | |||||
/// </summary> | |||||
public static string HealthCheckGet = $"{RouteTablePrefix}/{Prefix}/{HealthCheck}"; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -30,7 +30,7 @@ namespace JT809.DotNetty.Core.Clients | |||||
private MultithreadEventLoopGroup group; | private MultithreadEventLoopGroup group; | ||||
private IChannel channel; | |||||
public IChannel Channel { get; private set; } | |||||
private readonly ILogger<JT809SubordinateClient> logger; | private readonly ILogger<JT809SubordinateClient> logger; | ||||
@@ -81,14 +81,14 @@ namespace JT809.DotNetty.Core.Clients | |||||
await Task.Delay(delay); | await Task.Delay(delay); | ||||
try | try | ||||
{ | { | ||||
if (channel == null) | |||||
if (Channel == null) | |||||
{ | { | ||||
channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip), port)); | |||||
Channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip), port)); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
await channel.CloseAsync(); | |||||
channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip), port)); | |||||
await Channel.CloseAsync(); | |||||
Channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip), port)); | |||||
} | } | ||||
} | } | ||||
catch (AggregateException ex) | catch (AggregateException ex) | ||||
@@ -103,11 +103,11 @@ namespace JT809.DotNetty.Core.Clients | |||||
public async void SendAsync(JT809Response jT809Response) | public async void SendAsync(JT809Response jT809Response) | ||||
{ | { | ||||
if (channel == null) throw new NullReferenceException("Channel Not Open"); | |||||
if (Channel == null) throw new NullReferenceException("Channel Not Open"); | |||||
if (jT809Response == null) throw new ArgumentNullException("Data is null"); | if (jT809Response == null) throw new ArgumentNullException("Data is null"); | ||||
if (channel.Open && channel.Active) | |||||
if (Channel.Open && Channel.Active) | |||||
{ | { | ||||
await channel.WriteAndFlushAsync(jT809Response); | |||||
await Channel.WriteAndFlushAsync(jT809Response); | |||||
} | } | ||||
} | } | ||||
@@ -115,8 +115,8 @@ namespace JT809.DotNetty.Core.Clients | |||||
{ | { | ||||
get | get | ||||
{ | { | ||||
if (channel == null) return false; | |||||
return channel.Open && channel.Active; | |||||
if (Channel == null) return false; | |||||
return Channel.Open && Channel.Active; | |||||
} | } | ||||
} | } | ||||
@@ -136,7 +136,7 @@ namespace JT809.DotNetty.Core.Clients | |||||
VerifyCode = verifyCodeGenerator.Get() | VerifyCode = verifyCodeGenerator.Get() | ||||
}); | }); | ||||
JT809Response jT809Response = new JT809Response(package, 100); | JT809Response jT809Response = new JT809Response(package, 100); | ||||
channel.WriteAndFlushAsync(jT809Response); | |||||
Channel.WriteAndFlushAsync(jT809Response); | |||||
logger.LogInformation($"发送从链路注销请求>>>{JT809Serializer.Serialize(package, 100).ToHexString()}"); | logger.LogInformation($"发送从链路注销请求>>>{JT809Serializer.Serialize(package, 100).ToHexString()}"); | ||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
@@ -146,7 +146,7 @@ namespace JT809.DotNetty.Core.Clients | |||||
finally | finally | ||||
{ | { | ||||
//清理托管资源 | //清理托管资源 | ||||
channel.CloseAsync(); | |||||
Channel.CloseAsync(); | |||||
group.ShutdownGracefullyAsync(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3)); | group.ShutdownGracefullyAsync(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3)); | ||||
} | } | ||||
} | } | ||||
@@ -14,6 +14,8 @@ namespace JT809.DotNetty.Core.Configurations | |||||
public int TcpPort { get; set; } = 819; | public int TcpPort { get; set; } = 819; | ||||
public int WebApiPort { get; set; } = 829; | |||||
public int QuietPeriodSeconds { get; set; } = 1; | public int QuietPeriodSeconds { get; set; } = 1; | ||||
public TimeSpan QuietPeriodTimeSpan => TimeSpan.FromSeconds(QuietPeriodSeconds); | public TimeSpan QuietPeriodTimeSpan => TimeSpan.FromSeconds(QuietPeriodSeconds); | ||||
@@ -1,5 +1,6 @@ | |||||
using DotNetty.Handlers.Timeout; | using DotNetty.Handlers.Timeout; | ||||
using DotNetty.Transport.Channels; | using DotNetty.Transport.Channels; | ||||
using JT809.DotNetty.Core.Session; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -14,10 +15,14 @@ namespace JT809.DotNetty.Core.Handlers | |||||
private readonly ILogger<JT809MainServerConnectionHandler> logger; | private readonly ILogger<JT809MainServerConnectionHandler> logger; | ||||
private readonly JT809SuperiorMainSessionManager jT809SuperiorMainSessionManager; | |||||
public JT809MainServerConnectionHandler( | public JT809MainServerConnectionHandler( | ||||
JT809SuperiorMainSessionManager jT809SuperiorMainSessionManager, | |||||
ILoggerFactory loggerFactory) | ILoggerFactory loggerFactory) | ||||
{ | { | ||||
logger = loggerFactory.CreateLogger<JT809MainServerConnectionHandler>(); | logger = loggerFactory.CreateLogger<JT809MainServerConnectionHandler>(); | ||||
this.jT809SuperiorMainSessionManager = jT809SuperiorMainSessionManager; | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -41,6 +46,7 @@ namespace JT809.DotNetty.Core.Handlers | |||||
string channelId = context.Channel.Id.AsShortText(); | string channelId = context.Channel.Id.AsShortText(); | ||||
if (logger.IsEnabled(LogLevel.Debug)) | if (logger.IsEnabled(LogLevel.Debug)) | ||||
logger.LogDebug($">>>{ channelId } The client disconnects from the server."); | logger.LogDebug($">>>{ channelId } The client disconnects from the server."); | ||||
jT809SuperiorMainSessionManager.RemoveSessionByChannel(context.Channel); | |||||
base.ChannelInactive(context); | base.ChannelInactive(context); | ||||
} | } | ||||
@@ -54,6 +60,7 @@ namespace JT809.DotNetty.Core.Handlers | |||||
string channelId = context.Channel.Id.AsShortText(); | string channelId = context.Channel.Id.AsShortText(); | ||||
if (logger.IsEnabled(LogLevel.Debug)) | if (logger.IsEnabled(LogLevel.Debug)) | ||||
logger.LogDebug($"<<<{ channelId } The server disconnects from the client."); | logger.LogDebug($"<<<{ channelId } The server disconnects from the client."); | ||||
jT809SuperiorMainSessionManager.RemoveSessionByChannel(context.Channel); | |||||
return base.CloseAsync(context); | return base.CloseAsync(context); | ||||
} | } | ||||
@@ -81,6 +88,7 @@ namespace JT809.DotNetty.Core.Handlers | |||||
{ | { | ||||
string channelId = context.Channel.Id.AsShortText(); | string channelId = context.Channel.Id.AsShortText(); | ||||
logger.LogError(exception, $"{channelId} {exception.Message}"); | logger.LogError(exception, $"{channelId} {exception.Message}"); | ||||
jT809SuperiorMainSessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | context.CloseAsync(); | ||||
} | } | ||||
} | } | ||||
@@ -9,6 +9,7 @@ using JT809.Protocol.Extensions; | |||||
using JT809.Protocol.MessageBody; | using JT809.Protocol.MessageBody; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using Newtonsoft.Json; | |||||
namespace JT809.DotNetty.Core.Handlers | namespace JT809.DotNetty.Core.Handlers | ||||
{ | { | ||||
@@ -39,11 +40,16 @@ namespace JT809.DotNetty.Core.Handlers | |||||
{JT809BusinessType.主链路登录请求消息, Msg0x1001}, | {JT809BusinessType.主链路登录请求消息, Msg0x1001}, | ||||
{JT809BusinessType.主链路注销请求消息, Msg0x1003}, | {JT809BusinessType.主链路注销请求消息, Msg0x1003}, | ||||
{JT809BusinessType.主链路连接保持请求消息, Msg0x1005}, | {JT809BusinessType.主链路连接保持请求消息, Msg0x1005}, | ||||
{JT809BusinessType.主链路断开通知消息,Msg0x1007 }, | |||||
{JT809BusinessType.主链路动态信息交换消息, Msg0x1200}, | {JT809BusinessType.主链路动态信息交换消息, Msg0x1200}, | ||||
{JT809BusinessType.下级平台主动关闭链路通知消息, Msg0x1008}, | |||||
{JT809BusinessType.从链路连接应答消息, Msg0x9002}, | |||||
{JT809BusinessType.从链路注销应答消息, Msg0x9004}, | {JT809BusinessType.从链路注销应答消息, Msg0x9004}, | ||||
}; | |||||
{JT809BusinessType.从链路连接保持应答消息, Msg0x9006}, | |||||
}; | |||||
SubHandlerDict = new Dictionary<JT809SubBusinessType, Func<JT809Request, JT809Response>> | SubHandlerDict = new Dictionary<JT809SubBusinessType, Func<JT809Request, JT809Response>> | ||||
{ | { | ||||
//{JT809SubBusinessType.实时上传车辆定位信息, Msg0x1200_0x1202}, | //{JT809SubBusinessType.实时上传车辆定位信息, Msg0x1200_0x1202}, | ||||
@@ -62,7 +68,7 @@ namespace JT809.DotNetty.Core.Handlers | |||||
public virtual JT809Response Msg0x1001(JT809Request request) | public virtual JT809Response Msg0x1001(JT809Request request) | ||||
{ | { | ||||
var verifyCode = VerifyCodeGenerator.Create(); | var verifyCode = VerifyCodeGenerator.Create(); | ||||
Logger.LogInformation($"VerifyCode-{verifyCode}"); | |||||
Logger.LogInformation($"主链路登录请求消息:VerifyCode-{verifyCode}"); | |||||
var package = JT809BusinessType.主链路登录应答消息.Create(new JT809_0x1002() | var package = JT809BusinessType.主链路登录应答消息.Create(new JT809_0x1002() | ||||
{ | { | ||||
Result = JT809_0x1002_Result.成功, | Result = JT809_0x1002_Result.成功, | ||||
@@ -83,6 +89,8 @@ namespace JT809.DotNetty.Core.Handlers | |||||
/// <returns>主链路注销应答消息</returns> | /// <returns>主链路注销应答消息</returns> | ||||
public virtual JT809Response Msg0x1003(JT809Request request) | public virtual JT809Response Msg0x1003(JT809Request request) | ||||
{ | { | ||||
var jT809_0x1003 = request.Package.Bodies as JT809_0x1003; | |||||
Logger.LogInformation(JsonConvert.SerializeObject(jT809_0x1003)); | |||||
var package = JT809BusinessType.主链路注销应答消息.Create(); | var package = JT809BusinessType.主链路注销应答消息.Create(); | ||||
return new JT809Response(package, 100); | return new JT809Response(package, 100); | ||||
} | } | ||||
@@ -99,23 +107,73 @@ namespace JT809.DotNetty.Core.Handlers | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 主链路动态信息交换消息 | |||||
/// 主链路断开通知消息 | |||||
/// </summary> | /// </summary> | ||||
/// <param name="request"></param> | /// <param name="request"></param> | ||||
/// <returns></returns> | |||||
public virtual JT809Response Msg0x1200(JT809Request request) | |||||
/// <returns>本条消息无需被通知方应答</returns> | |||||
public virtual JT809Response Msg0x1007(JT809Request request) | |||||
{ | |||||
var jT809_0x1007 = request.Package.Bodies as JT809_0x1007; | |||||
Logger.LogInformation($"主链路断开通知消息:{jT809_0x1007.ErrorCode.ToString()}-{jT809_0x1007.ErrorCode}"); | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 下级平台主动关闭链路通知消息 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns>本条消息无需被通知方应答</returns> | |||||
public virtual JT809Response Msg0x1008(JT809Request request) | |||||
{ | { | ||||
var jT809_0x1008 = request.Package.Bodies as JT809_0x1008; | |||||
Logger.LogInformation($"下级平台主动关闭链路通知消息:{jT809_0x1008.ReasonCode.ToString()}-{jT809_0x1008.ReasonCode}"); | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 从链路连接应答消息 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns>本条消息无需被通知方应答</returns> | |||||
public virtual JT809Response Msg0x9002(JT809Request request) | |||||
{ | |||||
var jT809_0x9002 = request.Package.Bodies as JT809_0x9002; | |||||
Logger.LogInformation($"从链路连接应答消息:{jT809_0x9002.Result.ToString()}-{jT809_0x9002.Result}"); | |||||
return null; | return null; | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// | |||||
/// 从链路注销应答消息 | |||||
/// </summary> | /// </summary> | ||||
/// <param name="request"></param> | /// <param name="request"></param> | ||||
/// <returns>从链路注销应答消息</returns> | |||||
/// <returns>本条消息无需被通知方应答</returns> | |||||
public virtual JT809Response Msg0x9004(JT809Request request) | public virtual JT809Response Msg0x9004(JT809Request request) | ||||
{ | { | ||||
Logger.LogInformation("从链路注销应答消息"); | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 从链路连接保持应答消息 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns>本条消息无需被通知方应答</returns> | |||||
public virtual JT809Response Msg0x9006(JT809Request request) | |||||
{ | |||||
Logger.LogInformation("从链路连接保持应答消息"); | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 主链路动态信息交换消息 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual JT809Response Msg0x1200(JT809Request request) | |||||
{ | |||||
return null; | return null; | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,92 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT809.DotNetty.Abstractions.Dtos; | |||||
using JT809.DotNetty.Core.Metadata; | |||||
using Newtonsoft.Json; | |||||
namespace JT809.DotNetty.Core.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// 上级平台 | |||||
/// 基于webapi http模式抽象消息处理业务 | |||||
/// 自定义消息处理业务 | |||||
/// 注意: | |||||
/// 1.ConfigureServices: | |||||
/// services.Replace(new ServiceDescriptor(typeof(JT809SuperiorWebAPIHandlerBase),typeof(JT809SuperiorWebAPICustomHandler),ServiceLifetime.Singleton)); | |||||
/// 2.解析具体的消息体,具体消息调用具体的JT809Serializer.Deserialize<T> | |||||
/// </summary> | |||||
public abstract class JT809SuperiorWebAPIHandlerBase | |||||
{ | |||||
/// <summary> | |||||
/// 初始化消息处理业务 | |||||
/// </summary> | |||||
protected JT809SuperiorWebAPIHandlerBase() | |||||
{ | |||||
HandlerDict = new Dictionary<string, Func<JT809HttpRequest, JT809HttpResponse>>(); | |||||
} | |||||
protected void CreateRoute(string url, Func<JT809HttpRequest, JT809HttpResponse> func) | |||||
{ | |||||
if (!HandlerDict.ContainsKey(url)) | |||||
{ | |||||
HandlerDict.Add(url, func); | |||||
} | |||||
else | |||||
{ | |||||
// 替换 | |||||
HandlerDict[url] = func; | |||||
} | |||||
} | |||||
public Dictionary<string, Func<JT809HttpRequest, JT809HttpResponse>> HandlerDict { get; } | |||||
protected JT809HttpResponse CreateJT809HttpResponse(dynamic dynamicObject) | |||||
{ | |||||
byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dynamicObject)); | |||||
return new JT809HttpResponse() | |||||
{ | |||||
Data = data | |||||
}; | |||||
} | |||||
public JT809HttpResponse DefaultHttpResponse() | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT809DefaultResultDto())); | |||||
return new JT809HttpResponse(json); | |||||
} | |||||
public JT809HttpResponse EmptyHttpResponse() | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT809ResultDto<string>() | |||||
{ | |||||
Code = JT809ResultCode.Empty, | |||||
Message = "内容为空", | |||||
Data = "Content Empty" | |||||
})); | |||||
return new JT809HttpResponse(json); | |||||
} | |||||
public JT809HttpResponse NotFoundHttpResponse() | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT809ResultDto<string>() | |||||
{ | |||||
Code = JT809ResultCode.NotFound, | |||||
Message = "没有该服务", | |||||
Data = "没有该服务" | |||||
})); | |||||
return new JT809HttpResponse(json); | |||||
} | |||||
public JT809HttpResponse ErrorHttpResponse(Exception ex) | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT809ResultDto<string>() | |||||
{ | |||||
Code = JT809ResultCode.Error, | |||||
Message = JsonConvert.SerializeObject(ex), | |||||
Data = ex.Message | |||||
})); | |||||
return new JT809HttpResponse(json); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,81 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Common.Utilities; | |||||
using DotNetty.Transport.Channels; | |||||
using JT809.DotNetty.Core.Metadata; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.DotNetty.Core.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// 上级平台 | |||||
/// webapi处理 | |||||
/// </summary> | |||||
internal class JT809SuperiorWebAPIServerHandler : SimpleChannelInboundHandler<IFullHttpRequest> | |||||
{ | |||||
private static readonly AsciiString TypeJson = AsciiString.Cached("application/json"); | |||||
private static readonly AsciiString ServerName = AsciiString.Cached("JT809SuperiorWebAPINetty"); | |||||
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; | |||||
private readonly JT809SuperiorWebAPIHandlerBase JT809SuperiorWebAPIHandler; | |||||
private readonly ILogger<JT809SuperiorWebAPIServerHandler> logger; | |||||
public JT809SuperiorWebAPIServerHandler( | |||||
JT809SuperiorWebAPIHandlerBase jT809SuperiorWebAPIHandlerBase, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.JT809SuperiorWebAPIHandler = jT809SuperiorWebAPIHandlerBase; | |||||
logger = loggerFactory.CreateLogger<JT809SuperiorWebAPIServerHandler>(); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, IFullHttpRequest msg) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"Uri:{msg.Uri}"); | |||||
logger.LogDebug($"Content:{msg.Content.ToString(Encoding.UTF8)}"); | |||||
} | |||||
JT809HttpResponse jT808HttpResponse = null; | |||||
if (JT809SuperiorWebAPIHandler.HandlerDict.TryGetValue(msg.Uri, out var funcHandler)) | |||||
{ | |||||
jT808HttpResponse = funcHandler(new JT809HttpRequest() { Json = msg.Content.ToString(Encoding.UTF8) }); | |||||
} | |||||
else | |||||
{ | |||||
jT808HttpResponse = JT809SuperiorWebAPIHandler.NotFoundHttpResponse(); | |||||
} | |||||
if (jT808HttpResponse != null) | |||||
{ | |||||
WriteResponse(ctx, Unpooled.WrappedBuffer(jT808HttpResponse.Data), TypeJson, jT808HttpResponse.Data.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, 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.WriteAndFlushAsync(response); | |||||
ctx.CloseAsync(); | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
WriteResponse(context, Unpooled.WrappedBuffer(JT809SuperiorWebAPIHandler.ErrorHttpResponse(exception).Data), TypeJson, JT809SuperiorWebAPIHandler.ErrorHttpResponse(exception).Data.Length); | |||||
logger.LogError(exception, exception.Message); | |||||
context.CloseAsync(); | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
using JT809.DotNetty.Abstractions; | |||||
using JT809.DotNetty.Abstractions.Dtos; | |||||
using JT809.DotNetty.Core.Handlers; | |||||
using JT809.DotNetty.Core.Metadata; | |||||
using JT809.DotNetty.Core.Services; | |||||
using Newtonsoft.Json; | |||||
namespace JT809.DotNetty.Core.Internal | |||||
{ | |||||
/// <summary> | |||||
/// 默认消息处理业务实现 | |||||
/// </summary> | |||||
public class JT809SuperiorWebAPIDefaultHandler : JT809SuperiorWebAPIHandlerBase | |||||
{ | |||||
private readonly JT809SimpleSystemCollectService jT809SimpleSystemCollectService; | |||||
public JT809SuperiorWebAPIDefaultHandler(JT809SimpleSystemCollectService jT809SimpleSystemCollectService) | |||||
{ | |||||
this.jT809SimpleSystemCollectService = jT809SimpleSystemCollectService; | |||||
InitRoute(); | |||||
} | |||||
/// <summary> | |||||
/// 统一下发信息 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT809HttpResponse UnificationSend(JT809HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
//JT809UnificationSendRequestDto jT808UnificationSendRequestDto = JsonConvert.DeserializeObject<JT809UnificationSendRequestDto>(request.Json); | |||||
//var result = jT808UnificationTcpSendService.Send(jT808UnificationSendRequestDto.TerminalPhoneNo, jT808UnificationSendRequestDto.Data); | |||||
//return CreateJT808HttpResponse(result); | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 获取当前系统进程使用率 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT809HttpResponse SystemCollectGet(JT809HttpRequest request) | |||||
{ | |||||
JT809ResultDto<JT809SystemCollectInfoDto> jT809ResultDto = new JT809ResultDto<JT809SystemCollectInfoDto>(); | |||||
jT809ResultDto.Data = jT809SimpleSystemCollectService.Get(); | |||||
return CreateJT809HttpResponse(jT809ResultDto); | |||||
} | |||||
protected virtual void InitRoute() | |||||
{ | |||||
CreateRoute(JT809Constants.JT809SuperiorWebApiRouteTable.SystemCollectGet, SystemCollectGet); | |||||
} | |||||
} | |||||
} |
@@ -7,13 +7,13 @@ | |||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Remove="Internal\JT809SessionPublishingEmptyImpl.cs" /> | <Compile Remove="Internal\JT809SessionPublishingEmptyImpl.cs" /> | ||||
<Compile Remove="Session\JT809MainSessionManager.cs" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="DotNetty.Handlers" Version="0.6.0" /> | <PackageReference Include="DotNetty.Handlers" Version="0.6.0" /> | ||||
<PackageReference Include="DotNetty.Transport.Libuv" Version="0.6.0" /> | <PackageReference Include="DotNetty.Transport.Libuv" Version="0.6.0" /> | ||||
<PackageReference Include="DotNetty.Codecs" Version="0.6.0" /> | <PackageReference Include="DotNetty.Codecs" Version="0.6.0" /> | ||||
<PackageReference Include="DotNetty.Codecs.Http" Version="0.6.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.2.0" /> | <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.2.0" /> | ||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" /> | <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" /> | ||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> | <PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> | ||||
@@ -24,9 +24,5 @@ | |||||
<ProjectReference Include="..\JT809.DotNetty.Abstractions\JT809.DotNetty.Abstractions.csproj" /> | <ProjectReference Include="..\JT809.DotNetty.Abstractions\JT809.DotNetty.Abstractions.csproj" /> | ||||
<ProjectReference Include="..\JT809.Protocol\src\JT809.Protocol\JT809.Protocol.csproj" /> | <ProjectReference Include="..\JT809.Protocol\src\JT809.Protocol\JT809.Protocol.csproj" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<Folder Include="Session\" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -17,10 +17,10 @@ using Newtonsoft.Json; | |||||
using System; | using System; | ||||
using System.Runtime.CompilerServices; | using System.Runtime.CompilerServices; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using JT808.DotNetty.WebApi; | |||||
using JT809.DotNetty.Core.Session; | |||||
[assembly: InternalsVisibleTo("JT809.DotNetty.Core.Test")] | [assembly: InternalsVisibleTo("JT809.DotNetty.Core.Test")] | ||||
[assembly: InternalsVisibleTo("JT809.DotNetty.WebApi.Test")] | |||||
namespace JT809.DotNetty.Core | namespace JT809.DotNetty.Core | ||||
{ | { | ||||
@@ -107,7 +107,7 @@ namespace JT809.DotNetty.Core | |||||
serviceDescriptors.TryAddScoped<JT809MainServerConnectionHandler>(); | serviceDescriptors.TryAddScoped<JT809MainServerConnectionHandler>(); | ||||
serviceDescriptors.TryAddScoped<JT809SubordinateClientConnectionHandler>(); | serviceDescriptors.TryAddScoped<JT809SubordinateClientConnectionHandler>(); | ||||
//主链路服务端会话管理 | //主链路服务端会话管理 | ||||
//serviceDescriptors.TryAddSingleton<JT809MainSessionManager>(); | |||||
serviceDescriptors.TryAddSingleton<JT809SuperiorMainSessionManager>(); | |||||
//主从链路接收消息默认业务处理器 | //主从链路接收消息默认业务处理器 | ||||
serviceDescriptors.TryAddSingleton<JT809SuperiorMsgIdReceiveHandlerBase, JT809SuperiorMsgIdReceiveDefaultHandler>(); | serviceDescriptors.TryAddSingleton<JT809SuperiorMsgIdReceiveHandlerBase, JT809SuperiorMsgIdReceiveDefaultHandler>(); | ||||
//主从链路消息接收处理器 | //主从链路消息接收处理器 | ||||
@@ -117,6 +117,10 @@ namespace JT809.DotNetty.Core | |||||
serviceDescriptors.TryAddSingleton<JT809SubordinateClient>(); | serviceDescriptors.TryAddSingleton<JT809SubordinateClient>(); | ||||
//主链路服务端 | //主链路服务端 | ||||
serviceDescriptors.AddHostedService<JT809MainServerHost>(); | serviceDescriptors.AddHostedService<JT809MainServerHost>(); | ||||
//上级平台webapi | |||||
serviceDescriptors.TryAddSingleton<JT809SuperiorWebAPIHandlerBase, JT809SuperiorWebAPIDefaultHandler>(); | |||||
serviceDescriptors.TryAddScoped<JT809SuperiorWebAPIServerHandler>(); | |||||
serviceDescriptors.AddHostedService<JT809MainWebAPIServerHost>(); | |||||
return serviceDescriptors; | return serviceDescriptors; | ||||
} | } | ||||
} | } |
@@ -0,0 +1,21 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
namespace JT809.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT809HttpRequest | |||||
{ | |||||
public string Json { get; set; } | |||||
public JT809HttpRequest() | |||||
{ | |||||
} | |||||
public JT809HttpRequest(string json) | |||||
{ | |||||
Json = json; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
namespace JT809.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT809HttpResponse | |||||
{ | |||||
public byte[] Data { get; set; } | |||||
public JT809HttpResponse() | |||||
{ | |||||
} | |||||
public JT809HttpResponse(byte[] data) | |||||
{ | |||||
this.Data = data; | |||||
} | |||||
} | |||||
} |
@@ -19,7 +19,7 @@ namespace JT809.DotNetty.Core.Metadata | |||||
/// </summary> | /// </summary> | ||||
public uint MsgGNSSCENTERID { get; set; } | public uint MsgGNSSCENTERID { get; set; } | ||||
public IChannel Channel { get;} | |||||
public IChannel Channel { get; set; } | |||||
public DateTime LastActiveTime { get; set; } | public DateTime LastActiveTime { get; set; } | ||||
@@ -0,0 +1,82 @@ | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT809.DotNetty.Core.Configurations; | |||||
using JT809.DotNetty.Core.Handlers; | |||||
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; | |||||
namespace JT808.DotNetty.WebApi | |||||
{ | |||||
/// <summary> | |||||
/// JT809 集成一个webapi服务 | |||||
/// </summary> | |||||
internal class JT809MainWebAPIServerHost : IHostedService | |||||
{ | |||||
private readonly IServiceProvider serviceProvider; | |||||
private readonly ILogger<JT809MainWebAPIServerHost> logger; | |||||
private DispatcherEventLoopGroup bossGroup; | |||||
private WorkerEventLoopGroup workerGroup; | |||||
private IChannel bootstrapChannel; | |||||
private readonly JT809SuperiorPlatformOptions configuration; | |||||
public JT809MainWebAPIServerHost( | |||||
IServiceProvider provider, | |||||
IOptions<JT809SuperiorPlatformOptions> jT809SuperiorPlatformOptionsAccessor, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
serviceProvider = provider; | |||||
configuration = jT809SuperiorPlatformOptionsAccessor.Value; | |||||
logger = loggerFactory.CreateLogger<JT809MainWebAPIServerHost>(); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
bossGroup = new DispatcherEventLoopGroup(); | |||||
workerGroup = new WorkerEventLoopGroup(bossGroup, 1); | |||||
ServerBootstrap bootstrap = new ServerBootstrap(); | |||||
bootstrap.Group(bossGroup, workerGroup); | |||||
bootstrap.Channel<TcpServerChannel>(); | |||||
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<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
using (var scope = serviceProvider.CreateScope()) | |||||
{ | |||||
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_jt809webapihandler", scope.ServiceProvider.GetRequiredService<JT809SuperiorWebAPIServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"JT809 Superior 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); | |||||
} | |||||
} | |||||
} |
@@ -9,7 +9,7 @@ namespace JT809.DotNetty.Core.Services | |||||
/// <summary> | /// <summary> | ||||
/// 简单系统收集服务 | /// 简单系统收集服务 | ||||
/// </summary> | /// </summary> | ||||
internal class JT809SimpleSystemCollectService | |||||
public class JT809SimpleSystemCollectService | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 获取系统当前进程使用情况 | /// 获取系统当前进程使用情况 | ||||
@@ -4,26 +4,26 @@ using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using DotNetty.Transport.Channels; | using DotNetty.Transport.Channels; | ||||
using JT809.DotNetty.Abstractions; | |||||
using JT809.DotNetty.Core.Metadata; | using JT809.DotNetty.Core.Metadata; | ||||
namespace JT809.DotNetty.Core.Session | namespace JT809.DotNetty.Core.Session | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// JT809 主链路会话管理 | |||||
/// 上级平台 | |||||
/// JT809主链路会话管理 | |||||
/// </summary> | /// </summary> | ||||
public class JT809MainSessionManager | |||||
public class JT809SuperiorMainSessionManager | |||||
{ | { | ||||
private readonly ILogger<JT809MainSessionManager> logger; | |||||
private readonly ILogger<JT809SuperiorMainSessionManager> logger; | |||||
//private readonly IJT809SessionPublishing jT809SessionPublishing; | //private readonly IJT809SessionPublishing jT809SessionPublishing; | ||||
public JT809MainSessionManager( | |||||
public JT809SuperiorMainSessionManager( | |||||
//IJT809SessionPublishing jT809SessionPublishing, | //IJT809SessionPublishing jT809SessionPublishing, | ||||
ILoggerFactory loggerFactory) | ILoggerFactory loggerFactory) | ||||
{ | { | ||||
//this.jT809SessionPublishing = jT809SessionPublishing; | //this.jT809SessionPublishing = jT809SessionPublishing; | ||||
logger = loggerFactory.CreateLogger<JT809MainSessionManager>(); | |||||
logger = loggerFactory.CreateLogger<JT809SuperiorMainSessionManager>(); | |||||
} | } | ||||
private ConcurrentDictionary<uint, JT809Session> SessionIdDict = new ConcurrentDictionary<uint, JT809Session>(); | private ConcurrentDictionary<uint, JT809Session> SessionIdDict = new ConcurrentDictionary<uint, JT809Session>(); | ||||
@@ -48,20 +48,17 @@ namespace JT809.DotNetty.Core.Session | |||||
} | } | ||||
} | } | ||||
public void Heartbeat(uint msgGNSSCENTERID) | |||||
public void TryAdd(IChannel channel, uint msgGNSSCENTERID) | |||||
{ | { | ||||
if (SessionIdDict.TryGetValue(msgGNSSCENTERID, out JT809Session oldjT808Session)) | |||||
if(SessionIdDict.TryGetValue(msgGNSSCENTERID,out JT809Session jT809Session)) | |||||
{ | { | ||||
oldjT808Session.LastActiveTime = DateTime.Now; | |||||
SessionIdDict.TryUpdate(msgGNSSCENTERID, oldjT808Session, oldjT808Session); | |||||
jT809Session.LastActiveTime = DateTime.Now; | |||||
jT809Session.Channel = channel; | |||||
SessionIdDict.TryUpdate(msgGNSSCENTERID, jT809Session, jT809Session); | |||||
} | } | ||||
} | |||||
public void TryAdd(IChannel channel, uint msgGNSSCENTERID) | |||||
{ | |||||
if (SessionIdDict.ContainsKey(msgGNSSCENTERID)) return; | |||||
if (SessionIdDict.TryAdd(msgGNSSCENTERID, new JT809Session(channel, msgGNSSCENTERID))) | |||||
else | |||||
{ | { | ||||
SessionIdDict.TryAdd(msgGNSSCENTERID, new JT809Session(channel, msgGNSSCENTERID)); | |||||
//jT809SessionPublishing.PublishAsync(JT809Constants.SessionOnline, msgGNSSCENTERID.ToString()); | //jT809SessionPublishing.PublishAsync(JT809Constants.SessionOnline, msgGNSSCENTERID.ToString()); | ||||
} | } | ||||
} | } |