@@ -1,6 +1,6 @@ | |||||
MIT License | MIT License | ||||
Copyright (c) 2018 SmallChi(Koike) | |||||
Copyright (c) 2019 SmallChi(Koike) | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||
@@ -1,6 +1,8 @@ | |||||
# JT808DotNetty | |||||
# JT808Gateway | |||||
基于DotNetty封装的JT808DotNetty支持TCP/UDP通用消息业务处理 | |||||
基于DotNetty封装的JT808DotNetty支持TCP/UDP通用消息业务处理 | |||||
基于Pipeline封装的JT808DotNetty支持TCP/UDP通用消息业务处理 | |||||
[了解JT808协议进这边](https://github.com/SmallChi/JT808) | [了解JT808协议进这边](https://github.com/SmallChi/JT808) | ||||
@@ -14,7 +16,7 @@ | |||||
[](https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE)[](https://travis-ci.org/SmallChi/JT808DotNetty) | [](https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE)[](https://travis-ci.org/SmallChi/JT808DotNetty) | ||||
## 新网关的优势: | |||||
## 新网关的优势 | |||||
1. 跨平台 | 1. 跨平台 | ||||
2. 借助 .NET Core模块化的思想 | 2. 借助 .NET Core模块化的思想 | ||||
@@ -55,7 +57,7 @@ | |||||
|Traffic|流量统计服务 |由于运营商sim卡查询流量滞后,通过流量统计服务可以实时准确的统计设备流量,可以最优配置设备的流量大小,以节省成本 | |Traffic|流量统计服务 |由于运营商sim卡查询流量滞后,通过流量统计服务可以实时准确的统计设备流量,可以最优配置设备的流量大小,以节省成本 | ||||
|Transmit| 原包转发服务|该服务可以将设备上报原始数据转发到第三方,支持全部转发,指定终端号转发| | |Transmit| 原包转发服务|该服务可以将设备上报原始数据转发到第三方,支持全部转发,指定终端号转发| | ||||
## NuGet安装 | |||||
## 基于DotNetty的NuGet安装 | |||||
| Package Name | Version | Downloads | | | Package Name | Version | Downloads | | ||||
| --------------------- | -------------------------------------------------- | --------------------------------------------------- | | | --------------------- | -------------------------------------------------- | --------------------------------------------------- | | ||||
@@ -75,6 +77,13 @@ | |||||
| Install-Package JT808.DotNetty.Kafka |  |  | | | Install-Package JT808.DotNetty.Kafka |  |  | | ||||
| Install-Package JT808.DotNetty.RabbitMQ |  |  | | | Install-Package JT808.DotNetty.RabbitMQ |  |  | | ||||
## 基于Pipeline的NuGet安装 | |||||
| Package Name | Version | Downloads | | |||||
| --------------------- | -------------------------------------------------- | --------------------------------------------------- | | |||||
| Install-Package JT808.Gateway |  |  | | |||||
| Install-Package JT808.Gateway.Kafka|  |  | | |||||
## 举个栗子1 | ## 举个栗子1 | ||||
``` demo1 | ``` demo1 | ||||
@@ -1,120 +0,0 @@ | |||||
# JT808DotNetty | |||||
基于DotNetty封装的JT808DotNetty支持TCP/UDP通用消息业务处理 | |||||
[了解JT808协议进这边](https://github.com/SmallChi/JT808) | |||||
[了解JT809协议进这边](https://github.com/SmallChi/JT809) | |||||
[了解JT1078协议进这边](https://github.com/SmallChi/JT1078) | |||||
[了解JTNE协议进这边](https://github.com/SmallChi/JTNewEnergy) | |||||
[玩一玩压力测试](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/README.md) | |||||
[V2.2.1老版本](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/README_V2.2.1.md) | |||||
[](https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE) | |||||
## 新网关的优势 | |||||
1. 跨平台 | |||||
2. 借助 .NET Core模块化的思想 | |||||
3. 单机同时一万辆车在线不是梦(真有一万辆车那都很吃香了<( ̄3 ̄)> <( ̄3 ̄)> <( ̄3 ̄)> ) | |||||
4. 简单易上手 | |||||
## 设计模型 | |||||
 | |||||
## 基于Grpc的消息业务处理程序(JT808.Gateway.GrpcService) | |||||
``` 1 | |||||
services.AddGrpcClient<JT808Gateway.JT808GatewayClient>(o => | |||||
{ | |||||
o.Address = new Uri("https://localhost:5001"); | |||||
}); | |||||
``` | |||||
## 集成接口功能(JT808.Gateway.PubSub) | |||||
|接口名称|接口说明|使用场景| | |||||
|:------:|:------|:------| | |||||
| IJT808SessionProducer| 会话通知(在线/离线)数据生产接口| 有些超长待机的设备,不会实时保持连接,那么通过平台下发的命令是无法到达的,这时候就需要设备一上线,就即时通知服务去处理,然后在即时的下发消息到设备。| | |||||
| IJT808SessionConsumer| 会话通知(在线/离线)数据消费接口| -| | |||||
| IJT808MsgProducer| 数据生产接口| 网关将接收到的数据发送到队列| | |||||
| IJT808MsgConsumer| 数据消费接口| 将数据进行对应的消息业务处理(例:设备流量统计、第三方平台数据转发、消息日志等) | | |||||
| IJT808MsgReplyProducer| 应答数据生产接口|将生产的数据解析为对应的消息Id应答发送到队列 | | |||||
| IJT808MsgReplyConsumer| 应答数据消费接口| 将接收到的应答数据下发给设备| | |||||
> 使用物联网卡通过udp下发指令时,存储的那个socket地址端口,有效期非常短,不速度快点下发,那个socket地址端口就可能映射到别的对应卡去了,所以此处采用跟随设备消息下发指令。 | |||||
## 基于网关的相关服务(JT808.Gateway.BusinessServices) | |||||
|服务名称|服务说明|使用场景| | |||||
|:------:|:------|:------| | |||||
|MsgIdHandler| 消息处理服务|从队列中消费设备上报数据,再结合自身的业务场景,将数据进行处理并入库 | | |||||
|MsgLogging | 消息日志服务|从队列中消费设备上报和平台应答数据,再将数据存入influxdb等数据库中,便于技术和技术支持排查设备与平台交互的原始数据| | |||||
|ReplyMessage| 消息响应服务| 用于响应设备上报消息,以及下发指令信息到设备| | |||||
|SessionNotice| 会话管理服务| 通知设备上线下线,对于udp设备来说,可以在设备上线时,将指令跟随消息下发到设备| | |||||
|Traffic|流量统计服务 |由于运营商sim卡查询流量滞后,通过流量统计服务可以实时准确的统计设备流量,可以最优配置设备的流量大小,以节省成本 | |||||
|Transmit| 原包转发服务|该服务可以将设备上报原始数据转发到第三方,支持全部转发,指定终端号转发| | |||||
## NuGet安装 | |||||
| Package Name | Version | Downloads | | |||||
| --------------------- | -------------------------------------------------- | --------------------------------------------------- | | |||||
| Install-Package JT808.Gateway |  |  | | |||||
| Install-Package JT808.Gateway.Kafka|  |  | | |||||
## 举个栗子1 | |||||
1.进入JT808.Gateway.SimpleServer项目下的Debug目录运行服务端 | |||||
2.进入JT808.Gateway.SimpleClient项目下的Debug目录运行客户端 | |||||
``` 1 | |||||
static void Main(string[] args) | |||||
{ | |||||
Host.CreateDefaultBuilder(args) | |||||
.ConfigureWebHostDefaults(webBuilder => | |||||
{ | |||||
//配置Grpc服务端 | |||||
webBuilder | |||||
.ConfigureKestrel(options => | |||||
{ | |||||
options.Listen(IPAddress.Any, 5001, listenOptions => | |||||
{ | |||||
listenOptions.Protocols = HttpProtocols.Http2; | |||||
listenOptions.UseHttps($"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "test.cer")}", ""); | |||||
}); | |||||
}) | |||||
.Configure(app => | |||||
{ | |||||
app.UseRouting(); | |||||
app.UseEndpoints(endpoints => | |||||
{ | |||||
//配置Grpc服务节点 | |||||
endpoints.MapGrpcService<JT808GatewayService>(); | |||||
}); | |||||
}); | |||||
}) | |||||
.ConfigureServices((hostContext,services) => | |||||
{ | |||||
//services.Configure<KestrelServerOptions>(hostContext.Configuration.GetSection("Kestrel")); | |||||
//添加Grpc服务 | |||||
services.AddGrpc(); | |||||
//添加JT808协议 | |||||
services.AddJT808Configure() | |||||
//添加JT808网关配置 | |||||
.AddJT808Gateway(hostContext.Configuration) | |||||
//添加基于Tcp的808网关 | |||||
.AddJT808GatewayTcpHost() | |||||
//添加基于Udp的808网关 | |||||
.AddJT808GatewayUdpHost() | |||||
.Builder(); | |||||
}) | |||||
.Build() | |||||
.Run(); | |||||
} | |||||
``` |
@@ -1,4 +1,6 @@ | |||||
## 压力测试 | |||||
# 压力测试 | |||||
## 基于DotNetty | |||||
[感谢泥水佬提供的压力测试工具](https://www.cnblogs.com/smark/p/4496660.html?utm_source=tuicool) | [感谢泥水佬提供的压力测试工具](https://www.cnblogs.com/smark/p/4496660.html?utm_source=tuicool) | ||||
@@ -7,10 +9,25 @@ | |||||
| win server 2016 | 4c8g | 压力测试客户端 | | | win server 2016 | 4c8g | 压力测试客户端 | | ||||
| centos7 | 4c8g | JT808服务端 | | | centos7 | 4c8g | JT808服务端 | | ||||
 | |||||
 | |||||
 | |||||
 | |||||
 | |||||
## 基于pipeline | |||||
| 操作系统 | 配置 | 使用 | | |||||
|:-------:|:-------:|:-------:| | |||||
| centos7 | 4c8g | JT808服务端 | | |||||
| centos7 | 4c8g | JT808客户端 | | |||||
> 计算网络增强型 sn1ne ecs.sn1ne.xlarge 4 vCPU 8 GiB Intel Xeon E5-2682v4 / Intel Xeon(Skylake) Platinum 8163 2.5 GHz 1.5 Gbps 50 万 PPS | |||||
 | |||||
 | |||||
 | |||||
 | |||||
 | |||||
 |
@@ -1,19 +0,0 @@ | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.CleintBenchmark.Configs | |||||
{ | |||||
public class ClientBenchmarkOptions : IOptions<ClientBenchmarkOptions> | |||||
{ | |||||
public string IP { get; set; } | |||||
public int Port { get; set; } | |||||
public int DeviceCount { get; set; } = 10; | |||||
/// <summary> | |||||
/// 5000ms毫秒 | |||||
/// </summary> | |||||
public int Interval { get; set; } = 5000; | |||||
public ClientBenchmarkOptions Value =>this; | |||||
} | |||||
} |
@@ -1,36 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8" ?> | |||||
<!-- | |||||
参考:http://www.cnblogs.com/fuchongjundream/p/3936431.html | |||||
autoReload:自动再配置 | |||||
internalLogFile:可以让NLog把内部的调试和异常信息都写入指定文件里程序没问题了,日志却出了问题。这个该怎么办,到底是哪里不正确了?假如日志本身除了bug该如何解决?这就需要日志排错。把日志的错误信息写入日志。 | |||||
<nlog throwExceptions="true" /> | |||||
<nlog internalLogFile="file.txt" />- 设置internalLogFile属性可以让NLog把内部的调试和异常信息都写入指定文件里。 | |||||
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" /> - 决定内部日志的级别,级别越高,输出的日志信息越简洁。 | |||||
<nlog internalLogToConsole="false|true" /> - 是否把内部日志输出到标准控制台。 | |||||
<nlog internalLogToConsoleError="false|true" /> - 是否把内部日志输出到标准错误控制台 (stderr)。 | |||||
设置throwExceptions属性为“true”可以让NLog不再阻挡这类异常,而是把它们抛给调用者。在部署是这样做可以帮我们快速定位问题。一旦应用程序已经正确配置了,我们建议把throwExceptions的值设为“false”,这样由于日志引发的问题不至于导致应用程序的崩溃。 | |||||
--> | |||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
autoReload="true" | |||||
internalLogFile="/data/logs/JT808.DotNetty.Hosting/internalLog.txt" | |||||
internalLogLevel="Debug" > | |||||
<variable name="Directory" value="/data/logs/JT808.DotNetty.Hosting"/> | |||||
<targets> | |||||
<target name="all" xsi:type="File" | |||||
fileName="${Directory}/all/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||||
<target name="console" xsi:type="ColoredConsole" | |||||
useDefaultRowHighlightingRules="false" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level} ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"> | |||||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> | |||||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> | |||||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> | |||||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> | |||||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> | |||||
</target> | |||||
</targets> | |||||
<rules> | |||||
<logger name="*" minlevel="Debug" maxlevel="Fatal" writeTo="all,console"/> | |||||
</rules> | |||||
</nlog> |
@@ -1,35 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8" ?> | |||||
<!-- | |||||
参考:http://www.cnblogs.com/fuchongjundream/p/3936431.html | |||||
autoReload:自动再配置 | |||||
internalLogFile:可以让NLog把内部的调试和异常信息都写入指定文件里程序没问题了,日志却出了问题。这个该怎么办,到底是哪里不正确了?假如日志本身除了bug该如何解决?这就需要日志排错。把日志的错误信息写入日志。 | |||||
<nlog throwExceptions="true" /> | |||||
<nlog internalLogFile="file.txt" />- 设置internalLogFile属性可以让NLog把内部的调试和异常信息都写入指定文件里。 | |||||
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" /> - 决定内部日志的级别,级别越高,输出的日志信息越简洁。 | |||||
<nlog internalLogToConsole="false|true" /> - 是否把内部日志输出到标准控制台。 | |||||
<nlog internalLogToConsoleError="false|true" /> - 是否把内部日志输出到标准错误控制台 (stderr)。 | |||||
设置throwExceptions属性为“true”可以让NLog不再阻挡这类异常,而是把它们抛给调用者。在部署是这样做可以帮我们快速定位问题。一旦应用程序已经正确配置了,我们建议把throwExceptions的值设为“false”,这样由于日志引发的问题不至于导致应用程序的崩溃。 | |||||
--> | |||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
autoReload="true" | |||||
internalLogFile="${basedir}/wwwroot/logs/internalLog.txt" | |||||
internalLogLevel="Debug" > | |||||
<targets> | |||||
<target name="all" xsi:type="File" | |||||
fileName="${basedir}/wwwroot/logs/all/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||||
<target name="console" xsi:type="ColoredConsole" | |||||
useDefaultRowHighlightingRules="false" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level} ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"> | |||||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> | |||||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> | |||||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> | |||||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> | |||||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> | |||||
</target> | |||||
</targets> | |||||
<rules> | |||||
<logger name="*" minlevel="Debug" maxlevel="Fatal" writeTo="all,console"/> | |||||
</rules> | |||||
</nlog> |
@@ -1,33 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>netcoreapp3.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" /> | |||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.Gateway\JT808.Gateway.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Update="appsettings.json"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\nlog.unix.config"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\nlog.win.config"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\NLog.xsd"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,51 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using NLog.Extensions.Logging; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.Configuration; | |||||
using JT808.Gateway.CleintBenchmark.Configs; | |||||
using JT808.Gateway.Client; | |||||
using JT808.Gateway.CleintBenchmark.Services; | |||||
namespace JT808.Gateway.CleintBenchmark | |||||
{ | |||||
class Program | |||||
{ | |||||
static async Task Main(string[] args) | |||||
{ | |||||
var serverHostBuilder = new HostBuilder() | |||||
.ConfigureAppConfiguration((hostingContext, config) => | |||||
{ | |||||
config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); | |||||
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); | |||||
}) | |||||
.ConfigureLogging((context, logging) => | |||||
{ | |||||
if (Environment.OSVersion.Platform == PlatformID.Unix) | |||||
{ | |||||
NLog.LogManager.LoadConfiguration("Configs/nlog.unix.config"); | |||||
} | |||||
else | |||||
{ | |||||
NLog.LogManager.LoadConfiguration("Configs/nlog.win.config"); | |||||
} | |||||
logging.AddNLog(); | |||||
logging.SetMinimumLevel(LogLevel.Trace); | |||||
}) | |||||
.ConfigureServices((hostContext, services) => | |||||
{ | |||||
services.Configure<ClientBenchmarkOptions>(hostContext.Configuration.GetSection("ClientBenchmarkOptions")); | |||||
services.AddSingleton<ILoggerFactory, LoggerFactory>(); | |||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); | |||||
services.AddJT808Configure() | |||||
.AddJT808Client(); | |||||
services.AddHostedService<CleintBenchmarkHostedService>(); | |||||
services.AddHostedService<CleintBenchmarkReportHostedService>(); | |||||
}); | |||||
await serverHostBuilder.RunConsoleAsync(); | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using JT808.Gateway.CleintBenchmark.Configs; | |||||
using JT808.Gateway.Client; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.CleintBenchmark.Services | |||||
{ | |||||
public class CleintBenchmarkHostedService : IHostedService | |||||
{ | |||||
private readonly ClientBenchmarkOptions clientBenchmarkOptions; | |||||
private readonly ILogger logger; | |||||
private readonly IJT808TcpClientFactory jT808TcpClientFactory; | |||||
private CancellationTokenSource cts=new CancellationTokenSource(); | |||||
private TaskFactory taskFactory; | |||||
public CleintBenchmarkHostedService( | |||||
ILoggerFactory loggerFactory, | |||||
IJT808TcpClientFactory jT808TcpClientFactory, | |||||
IOptions<ClientBenchmarkOptions> clientBenchmarkOptionsAccessor) | |||||
{ | |||||
this.jT808TcpClientFactory = jT808TcpClientFactory; | |||||
clientBenchmarkOptions = clientBenchmarkOptionsAccessor.Value; | |||||
logger = loggerFactory.CreateLogger("CleintBenchmarkHostedService"); | |||||
taskFactory = new TaskFactory(); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
logger.LogInformation("StartAsync..."); | |||||
ThreadPool.GetMinThreads(out var minWorkerThreads, out var minCompletionPortThreads); | |||||
ThreadPool.GetMaxThreads(out var maxWorkerThreads, out var maxCompletionPortThreads); | |||||
logger.LogInformation($"GetMinThreads:{minWorkerThreads}-{minCompletionPortThreads}"); | |||||
logger.LogInformation($"GetMaxThreads:{maxWorkerThreads}-{maxCompletionPortThreads}"); | |||||
//ThreadPool.SetMaxThreads(20, 20); | |||||
//ThreadPool.GetMaxThreads(out var setMaxWorkerThreads, out var setMaxCompletionPortThreads); | |||||
//logger.LogInformation($"SetMaxThreads:{setMaxWorkerThreads}-{setMaxCompletionPortThreads}"); | |||||
for (int i=0;i< clientBenchmarkOptions.DeviceCount; i++) | |||||
{ | |||||
taskFactory.StartNew((item) => | |||||
{ | |||||
var client = jT808TcpClientFactory.Create(new DeviceConfig(((int)item).ToString(), clientBenchmarkOptions.IP, clientBenchmarkOptions.Port)); | |||||
int lat = new Random(1000).Next(100000, 180000); | |||||
int Lng = new Random(1000).Next(100000, 180000); | |||||
while (!cts.IsCancellationRequested) | |||||
{ | |||||
client.Send(new JT808_0x0200() | |||||
{ | |||||
Lat = lat, | |||||
Lng = Lng, | |||||
GPSTime = DateTime.Now, | |||||
Speed = 50, | |||||
Direction = 30, | |||||
AlarmFlag = 5, | |||||
Altitude = 50, | |||||
StatusFlag = 10 | |||||
}); | |||||
Thread.Sleep(clientBenchmarkOptions.Interval); | |||||
} | |||||
}, i,cts.Token); | |||||
} | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
cts.Cancel(); | |||||
logger.LogInformation("StopAsync..."); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,53 +0,0 @@ | |||||
using JT808.Gateway.Services; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.CleintBenchmark.Services | |||||
{ | |||||
public class CleintBenchmarkReportHostedService : IHostedService | |||||
{ | |||||
private readonly JT808ClientReportService jT808ReportService; | |||||
private CancellationTokenSource cts=new CancellationTokenSource(); | |||||
private readonly ILogger logger; | |||||
public CleintBenchmarkReportHostedService( | |||||
ILoggerFactory loggerFactory, | |||||
JT808ClientReportService jT808ReportService) | |||||
{ | |||||
this.jT808ReportService = jT808ReportService; | |||||
logger = loggerFactory.CreateLogger("CleintBenchmarkReportHostedService"); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
logger.LogInformation("StartAsync..."); | |||||
Task.Run(() => { | |||||
while (!cts.IsCancellationRequested) | |||||
{ | |||||
logger.LogInformation(JsonConvert.SerializeObject(jT808ReportService.JT808Reports.LastOrDefault())); | |||||
Thread.Sleep(3000); | |||||
} | |||||
}, cts.Token); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
logger.LogInformation("StopAsync..."); | |||||
cts.Cancel(); | |||||
logger.LogInformation("正在生成报表..."); | |||||
logger.LogInformation(JsonConvert.SerializeObject(jT808ReportService.JT808Reports,Formatting.Indented)); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808ConsumerConfig: ConsumerConfig, IOptions<JT808ConsumerConfig> | |||||
{ | |||||
public string TopicName { get; set; } | |||||
public JT808ConsumerConfig Value => this; | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808MsgConsumerConfig : JT808ConsumerConfig, IOptions<JT808MsgConsumerConfig> | |||||
{ | |||||
JT808MsgConsumerConfig IOptions<JT808MsgConsumerConfig>.Value => this; | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808MsgProducerConfig : JT808ProducerConfig, IOptions<JT808MsgProducerConfig> | |||||
{ | |||||
JT808MsgProducerConfig IOptions<JT808MsgProducerConfig>.Value => this; | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808MsgReplyConsumerConfig : JT808ConsumerConfig, IOptions<JT808MsgReplyConsumerConfig> | |||||
{ | |||||
JT808MsgReplyConsumerConfig IOptions<JT808MsgReplyConsumerConfig>.Value => this; | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808MsgReplyProducerConfig : JT808ProducerConfig, IOptions<JT808MsgReplyProducerConfig> | |||||
{ | |||||
JT808MsgReplyProducerConfig IOptions<JT808MsgReplyProducerConfig>.Value => this; | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808ProducerConfig : ProducerConfig,IOptions<JT808ProducerConfig> | |||||
{ | |||||
public string TopicName { get; set; } | |||||
public JT808ProducerConfig Value => this; | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808SessionConsumerConfig : JT808ConsumerConfig, IOptions<JT808SessionConsumerConfig> | |||||
{ | |||||
JT808SessionConsumerConfig IOptions<JT808SessionConsumerConfig>.Value => this; | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configs.Kafka | |||||
{ | |||||
public class JT808SessionProducerConfig : JT808ProducerConfig, IOptions<JT808SessionProducerConfig> | |||||
{ | |||||
JT808SessionProducerConfig IOptions<JT808SessionProducerConfig>.Value => this; | |||||
} | |||||
} |
@@ -1,39 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>8.0</LangVersion> | |||||
<Copyright>Copyright 2018.</Copyright> | |||||
<Authors>SmallChi(Koike)</Authors> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT808DotNetty</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT808DotNetty</PackageProjectUrl> | |||||
<licenseUrl>https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE</licenseUrl> | |||||
<license>https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE</license> | |||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<Version>1.0.0-preview1</Version> | |||||
<SignAssembly>false</SignAssembly> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||||
<PackageId>JT808.Gateway.Kafka</PackageId> | |||||
<Product>JT808.Gateway.Kafka</Product> | |||||
<Description>基于Kafka的JT808消息发布与订阅</Description> | |||||
<PackageReleaseNotes>基于Kafka的JT808消息发布与订阅</PackageReleaseNotes> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Confluent.Kafka" Version="1.2.1" /> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Options" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.Gateway\JT808.Gateway.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,24 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
internal class JT808ClientBuilderDefault : IJT808ClientBuilder | |||||
{ | |||||
public IJT808Builder JT808Builder { get; } | |||||
public JT808ClientBuilderDefault(IJT808Builder builder) | |||||
{ | |||||
JT808Builder = builder; | |||||
} | |||||
public IJT808Builder Builder() | |||||
{ | |||||
return JT808Builder; | |||||
} | |||||
} | |||||
} |
@@ -1,66 +0,0 @@ | |||||
using JJT808.Gateway.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public static class JT808ClientKafkaExtensions | |||||
{ | |||||
public static IJT808ClientBuilder AddJT808ClientKafka(this IJT808Builder builder) | |||||
{ | |||||
return new JT808ClientBuilderDefault(builder); | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="serviceDescriptors"></param> | |||||
/// <param name="configuration">GetSection("JT808MsgConsumerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddMsgConsumer(this IJT808ClientBuilder jT808ClientBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.Configure<JT808MsgConsumerConfig>(configuration.GetSection("JT808MsgConsumerConfig")); | |||||
jT808ClientBuilder.JT808Builder.Services.TryAddSingleton<IJT808MsgConsumer, JT808MsgConsumer>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="serviceDescriptors"></param> | |||||
/// <param name="configuration">GetSection("JT808MsgReplyProducerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddMsgReplyProducer(this IJT808ClientBuilder jT808ClientBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.Configure<JT808MsgReplyProducerConfig>(configuration.GetSection("JT808MsgReplyProducerConfig")); | |||||
jT808ClientBuilder.JT808Builder.Services.TryAddSingleton<IJT808MsgReplyProducer, JT808MsgReplyProducer>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="jT808NettyBuilder"></param> | |||||
/// <param name="configuration">GetSection("JT808MsgReplyConsumerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddMsgReplyConsumer(this IJT808ClientBuilder jT808ClientBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.Configure<JT808MsgReplyConsumerConfig>(configuration.GetSection("JT808MsgReplyConsumerConfig")); | |||||
jT808ClientBuilder.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808MsgReplyConsumer), typeof(JT808MsgReplyConsumer), ServiceLifetime.Singleton)); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="serviceDescriptors"></param> | |||||
/// <param name="configuration">GetSection("JT808SessionConsumerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddSessionConsumer(this IJT808ClientBuilder jT808ClientBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.Configure<JT808SessionConsumerConfig>(configuration.GetSection("JT808SessionConsumerConfig")); | |||||
jT808ClientBuilder.JT808Builder.Services.TryAddSingleton<IJT808SessionConsumer, JT808SessionConsumer>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public class JT808MsgConsumer : IJT808MsgConsumer | |||||
{ | |||||
public CancellationTokenSource Cts => new CancellationTokenSource(); | |||||
private readonly IConsumer<string, byte[]> consumer; | |||||
private readonly ILogger logger; | |||||
public string TopicName { get; } | |||||
public JT808MsgConsumer( | |||||
IOptions<JT808MsgConsumerConfig> consumerConfigAccessor, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
consumer = new ConsumerBuilder<string, byte[]>(consumerConfigAccessor.Value).Build(); | |||||
TopicName = consumerConfigAccessor.Value.TopicName; | |||||
logger = loggerFactory.CreateLogger("JT808MsgConsumer"); | |||||
} | |||||
public void OnMessage(Action<(string TerminalNo, byte[] Data)> callback) | |||||
{ | |||||
Task.Run(() => | |||||
{ | |||||
while (!Cts.IsCancellationRequested) | |||||
{ | |||||
try | |||||
{ | |||||
//如果不指定分区,根据kafka的机制会从多个分区中拉取数据 | |||||
//如果指定分区,根据kafka的机制会从相应的分区中拉取数据 | |||||
var data = consumer.Consume(Cts.Token); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"Topic: {data.Topic} Key: {data.Key} Partition: {data.Partition} Offset: {data.Offset} TopicPartitionOffset:{data.TopicPartitionOffset}"); | |||||
} | |||||
callback((data.Key, data.Value)); | |||||
} | |||||
catch (ConsumeException ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
catch (OperationCanceledException ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
} | |||||
}, Cts.Token); | |||||
} | |||||
public void Subscribe() | |||||
{ | |||||
consumer.Subscribe(TopicName); | |||||
} | |||||
public void Unsubscribe() | |||||
{ | |||||
consumer.Unsubscribe(); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
consumer.Close(); | |||||
consumer.Dispose(); | |||||
} | |||||
} | |||||
} |
@@ -1,38 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public class JT808MsgProducer : IJT808MsgProducer | |||||
{ | |||||
public string TopicName { get; } | |||||
private readonly IProducer<string, byte[]> producer; | |||||
public JT808MsgProducer( | |||||
IOptions<JT808MsgProducerConfig> producerConfigAccessor) | |||||
{ | |||||
producer = new ProducerBuilder<string, byte[]>(producerConfigAccessor.Value).Build(); | |||||
TopicName = producerConfigAccessor.Value.TopicName; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
producer.Dispose(); | |||||
} | |||||
public async Task ProduceAsync(string terminalNo, byte[] data) | |||||
{ | |||||
await producer.ProduceAsync(TopicName, new Message<string, byte[]> | |||||
{ | |||||
Key = terminalNo, | |||||
Value = data | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public class JT808MsgReplyConsumer : IJT808MsgReplyConsumer | |||||
{ | |||||
public CancellationTokenSource Cts => new CancellationTokenSource(); | |||||
private readonly IConsumer<string, byte[]> consumer; | |||||
private readonly ILogger logger; | |||||
public string TopicName { get; } | |||||
public JT808MsgReplyConsumer( | |||||
IOptions<JT808MsgReplyConsumerConfig> consumerConfigAccessor, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
consumer = new ConsumerBuilder<string, byte[]>(consumerConfigAccessor.Value).Build(); | |||||
TopicName = consumerConfigAccessor.Value.TopicName; | |||||
logger = loggerFactory.CreateLogger("JT808MsgReplyConsumer"); | |||||
} | |||||
public void OnMessage(Action<(string TerminalNo, byte[] Data)> callback) | |||||
{ | |||||
Task.Run(() => | |||||
{ | |||||
while (!Cts.IsCancellationRequested) | |||||
{ | |||||
try | |||||
{ | |||||
//如果不指定分区,根据kafka的机制会从多个分区中拉取数据 | |||||
//如果指定分区,根据kafka的机制会从相应的分区中拉取数据 | |||||
var data = consumer.Consume(Cts.Token); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"Topic: {data.Topic} Key: {data.Key} Partition: {data.Partition} Offset: {data.Offset} TopicPartitionOffset:{data.TopicPartitionOffset}"); | |||||
} | |||||
callback((data.Key, data.Value)); | |||||
} | |||||
catch (ConsumeException ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
catch (OperationCanceledException ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
} | |||||
}, Cts.Token); | |||||
} | |||||
public void Subscribe() | |||||
{ | |||||
consumer.Subscribe(TopicName); | |||||
} | |||||
public void Unsubscribe() | |||||
{ | |||||
consumer.Unsubscribe(); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
consumer.Close(); | |||||
consumer.Dispose(); | |||||
} | |||||
} | |||||
} |
@@ -1,38 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JJT808.Gateway.Kafka | |||||
{ | |||||
public class JT808MsgReplyProducer : IJT808MsgReplyProducer | |||||
{ | |||||
public string TopicName { get;} | |||||
private IProducer<string, byte[]> producer; | |||||
public JT808MsgReplyProducer( | |||||
IOptions<JT808MsgReplyProducerConfig> producerConfigAccessor) | |||||
{ | |||||
producer = new ProducerBuilder<string, byte[]>(producerConfigAccessor.Value).Build(); | |||||
TopicName = producerConfigAccessor.Value.TopicName; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
producer.Dispose(); | |||||
} | |||||
public async Task ProduceAsync(string terminalNo, byte[] data) | |||||
{ | |||||
await producer.ProduceAsync(TopicName, new Message<string, byte[]> | |||||
{ | |||||
Key = terminalNo, | |||||
Value = data | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,48 +0,0 @@ | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public static class JT808ServerKafkaExtensions | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="jT808NettyBuilder"></param> | |||||
/// <param name="configuration">GetSection("JT808MsgProducerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808GatewayBuilder AddJT808ServerKafkaMsgProducer(this IJT808GatewayBuilder jT808GatewayBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808GatewayBuilder.JT808Builder.Services.Configure<JT808MsgProducerConfig>(configuration.GetSection("JT808MsgProducerConfig")); | |||||
jT808GatewayBuilder.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808MsgProducer), typeof(JT808MsgProducer), ServiceLifetime.Singleton)); | |||||
return jT808GatewayBuilder; | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="jT808NettyBuilder"></param> | |||||
/// <param name="configuration">GetSection("JT808MsgReplyConsumerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808GatewayBuilder AddJT808ServerKafkaMsgReplyConsumer(this IJT808GatewayBuilder jT808GatewayBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808GatewayBuilder.JT808Builder.Services.Configure<JT808MsgReplyConsumerConfig>(configuration.GetSection("JT808MsgReplyConsumerConfig")); | |||||
jT808GatewayBuilder.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808MsgReplyConsumer), typeof(JT808MsgReplyConsumer), ServiceLifetime.Singleton)); | |||||
return jT808GatewayBuilder; | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="jT808NettyBuilder"></param> | |||||
/// <param name="configuration">GetSection("JT808SessionProducerConfig")</param> | |||||
/// <returns></returns> | |||||
public static IJT808GatewayBuilder AddJT808ServerKafkaSessionProducer(this IJT808GatewayBuilder jT808GatewayBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808GatewayBuilder.JT808Builder.Services.Configure<JT808SessionProducerConfig>(configuration.GetSection("JT808SessionProducerConfig")); | |||||
jT808GatewayBuilder.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808SessionProducer), typeof(JT808SessionProducer), ServiceLifetime.Singleton)); | |||||
return jT808GatewayBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public class JT808SessionConsumer : IJT808SessionConsumer | |||||
{ | |||||
public CancellationTokenSource Cts => new CancellationTokenSource(); | |||||
private readonly IConsumer<string, string> consumer; | |||||
private readonly ILogger logger; | |||||
public string TopicName { get; } | |||||
public JT808SessionConsumer( | |||||
IOptions<JT808SessionConsumerConfig> consumerConfigAccessor, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
consumer = new ConsumerBuilder<string, string>(consumerConfigAccessor.Value).Build(); | |||||
TopicName = consumerConfigAccessor.Value.TopicName; | |||||
logger = loggerFactory.CreateLogger("JT808SessionConsumer"); | |||||
} | |||||
public void OnMessage(Action<(string Notice, string TerminalNo)> callback) | |||||
{ | |||||
Task.Run(() => | |||||
{ | |||||
while (!Cts.IsCancellationRequested) | |||||
{ | |||||
try | |||||
{ | |||||
//如果不指定分区,根据kafka的机制会从多个分区中拉取数据 | |||||
//如果指定分区,根据kafka的机制会从相应的分区中拉取数据 | |||||
var data = consumer.Consume(Cts.Token); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"Topic: {data.Topic} Key: {data.Key} Partition: {data.Partition} Offset: {data.Offset} TopicPartitionOffset:{data.TopicPartitionOffset}"); | |||||
} | |||||
callback((data.Key, data.Value)); | |||||
} | |||||
catch (ConsumeException ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
catch (OperationCanceledException ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex, TopicName); | |||||
} | |||||
} | |||||
}, Cts.Token); | |||||
} | |||||
public void Subscribe() | |||||
{ | |||||
consumer.Subscribe(TopicName); | |||||
} | |||||
public void Unsubscribe() | |||||
{ | |||||
consumer.Unsubscribe(); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
consumer.Close(); | |||||
consumer.Dispose(); | |||||
} | |||||
} | |||||
} |
@@ -1,38 +0,0 @@ | |||||
using Confluent.Kafka; | |||||
using JT808.Gateway.Configs.Kafka; | |||||
using JT808.Gateway.PubSub; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Kafka | |||||
{ | |||||
public class JT808SessionProducer : IJT808SessionProducer | |||||
{ | |||||
public string TopicName { get; } | |||||
private readonly IProducer<string, string> producer; | |||||
public JT808SessionProducer( | |||||
IOptions<JT808SessionProducerConfig> producerConfigAccessor) | |||||
{ | |||||
producer = new ProducerBuilder<string, string>(producerConfigAccessor.Value).Build(); | |||||
TopicName = producerConfigAccessor.Value.TopicName; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
producer.Dispose(); | |||||
} | |||||
public async Task ProduceAsync(string notice,string terminalNo) | |||||
{ | |||||
await producer.ProduceAsync(TopicName, new Message<string, string> | |||||
{ | |||||
Key = notice, | |||||
Value = terminalNo | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>netcoreapp3.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.24.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.Gateway\JT808.Gateway.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,52 +0,0 @@ | |||||
using JT808.Gateway.Client; | |||||
using JT808.Gateway.SimpleClient.Services; | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using Grpc.Net.Client; | |||||
using JT808.Gateway.GrpcService; | |||||
using System.Net; | |||||
namespace JT808.Gateway.SimpleClient | |||||
{ | |||||
class Program | |||||
{ | |||||
static async Task Main(string[] args) | |||||
{ | |||||
//ref https://docs.microsoft.com/zh-cn/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0#call-insecure-grpc-services-with-net-core-client | |||||
//ref https://docs.microsoft.com/zh-cn/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0 | |||||
//先执行 dotnet dev-certs https --trust 命令生成开发证书 | |||||
//使用 certmgr.msc 导出证书在服务端配置对应证书文件 | |||||
//Uri "https://localhost:5001" | |||||
var serverHostBuilder = new HostBuilder() | |||||
.ConfigureLogging((context, logging) => | |||||
{ | |||||
logging.AddConsole(); | |||||
logging.SetMinimumLevel(LogLevel.Trace); | |||||
}) | |||||
.ConfigureServices((hostContext, services) => | |||||
{ | |||||
services.AddGrpcClient<JT808Gateway.JT808GatewayClient>(o => | |||||
{ | |||||
o.Address = new Uri("https://localhost:5001"); | |||||
}); | |||||
services.AddSingleton<ILoggerFactory, LoggerFactory>(); | |||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); | |||||
services.AddLogging(options => { | |||||
options.AddConsole(); | |||||
options.SetMinimumLevel(LogLevel.Trace); | |||||
}); | |||||
services.AddJT808Configure() | |||||
.AddJT808Client(); | |||||
services.AddHostedService<UpService>(); | |||||
//services.AddHostedService<GrpcClientService>(); | |||||
}); | |||||
await serverHostBuilder.RunConsoleAsync(); | |||||
} | |||||
} | |||||
} |
@@ -1,68 +0,0 @@ | |||||
using JT808.Gateway.Client; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT808.Gateway.GrpcService; | |||||
using static JT808.Gateway.GrpcService.JT808Gateway; | |||||
using Google.Protobuf; | |||||
using System.Text.Json; | |||||
using JT808.Protocol.Extensions; | |||||
namespace JT808.Gateway.SimpleClient.Services | |||||
{ | |||||
public class GrpcClientService : IHostedService | |||||
{ | |||||
private readonly ILogger logger; | |||||
private readonly JT808GatewayClient client; | |||||
public GrpcClientService( | |||||
ILoggerFactory loggerFactory, | |||||
JT808GatewayClient jT808GatewayClient) | |||||
{ | |||||
this.client = jT808GatewayClient; | |||||
logger = loggerFactory.CreateLogger("GrpcClientService"); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
Task.Run(() => { | |||||
//while (!cancellationToken.IsCancellationRequested) | |||||
//{ | |||||
Thread.Sleep(1000 * 10); | |||||
var result1 = client.GetTcpAtomicCounter(new Empty()); | |||||
var result2 = client.GetUdpAtomicCounter(new Empty()); | |||||
var result3 = client.GetTcpSessionAll(new Empty()); | |||||
var result4 = client.GetUdpSessionAll(new Empty()); | |||||
var result5 = client.UnificationSend(new UnificationSendRequest() | |||||
{ | |||||
TerminalPhoneNo= "12345678910", | |||||
Data= ByteString.CopyFrom("7E 02 00 00 26 12 34 56 78 90 12 00 7D 02 00 00 00 01 00 00 00 02 00 BA 7F 0E 07 E4 F1 1C 00 28 00 3C 00 00 18 10 15 10 10 10 01 04 00 00 00 64 02 02 00 7D 01 13 7E".ToHexBytes()) | |||||
}); | |||||
var result6 = client.RemoveSessionByTerminalPhoneNo(new SessionRemoveRequest() | |||||
{ | |||||
TerminalPhoneNo= "12345678910" | |||||
}); | |||||
logger.LogDebug(JsonSerializer.Serialize(result1)); | |||||
logger.LogDebug(JsonSerializer.Serialize(result2)); | |||||
logger.LogDebug(JsonSerializer.Serialize(result3)); | |||||
logger.LogDebug(JsonSerializer.Serialize(result4)); | |||||
logger.LogDebug(JsonSerializer.Serialize(result5)); | |||||
logger.LogDebug(JsonSerializer.Serialize(result6)); | |||||
//} | |||||
}, cancellationToken); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,68 +0,0 @@ | |||||
using JT808.Gateway.Client; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.SimpleClient.Services | |||||
{ | |||||
public class UpService : IHostedService | |||||
{ | |||||
private readonly IJT808TcpClientFactory jT808TcpClientFactory; | |||||
public UpService(IJT808TcpClientFactory jT808TcpClientFactory) | |||||
{ | |||||
this.jT808TcpClientFactory = jT808TcpClientFactory; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
JT808TcpClient client1 = jT808TcpClientFactory.Create(new DeviceConfig("12345678910", "127.0.0.1", 808)); | |||||
//1.终端注册 | |||||
client1.Send(new JT808_0x0100() | |||||
{ | |||||
PlateNo = "粤A12345", | |||||
PlateColor = 2, | |||||
AreaID = 0, | |||||
CityOrCountyId = 0, | |||||
MakerId = "Koike001", | |||||
TerminalId = "Koike001", | |||||
TerminalModel = "Koike001" | |||||
}); | |||||
//2.终端鉴权 | |||||
client1.Send(new JT808_0x0102() | |||||
{ | |||||
Code = "1234" | |||||
}); | |||||
Task.Run(() => { | |||||
while (true) | |||||
{ | |||||
var i = 0; | |||||
//3.每5000秒发一次 | |||||
client1.Send(new JT808_0x0200() | |||||
{ | |||||
Lat = 110000 + i, | |||||
Lng = 100000 + i, | |||||
GPSTime = DateTime.Now, | |||||
Speed = 50, | |||||
Direction = 30, | |||||
AlarmFlag = 5, | |||||
Altitude = 50, | |||||
StatusFlag = 10 | |||||
}); | |||||
i++; | |||||
Thread.Sleep(5000); | |||||
} | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,36 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8" ?> | |||||
<!-- | |||||
参考:http://www.cnblogs.com/fuchongjundream/p/3936431.html | |||||
autoReload:自动再配置 | |||||
internalLogFile:可以让NLog把内部的调试和异常信息都写入指定文件里程序没问题了,日志却出了问题。这个该怎么办,到底是哪里不正确了?假如日志本身除了bug该如何解决?这就需要日志排错。把日志的错误信息写入日志。 | |||||
<nlog throwExceptions="true" /> | |||||
<nlog internalLogFile="file.txt" />- 设置internalLogFile属性可以让NLog把内部的调试和异常信息都写入指定文件里。 | |||||
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" /> - 决定内部日志的级别,级别越高,输出的日志信息越简洁。 | |||||
<nlog internalLogToConsole="false|true" /> - 是否把内部日志输出到标准控制台。 | |||||
<nlog internalLogToConsoleError="false|true" /> - 是否把内部日志输出到标准错误控制台 (stderr)。 | |||||
设置throwExceptions属性为“true”可以让NLog不再阻挡这类异常,而是把它们抛给调用者。在部署是这样做可以帮我们快速定位问题。一旦应用程序已经正确配置了,我们建议把throwExceptions的值设为“false”,这样由于日志引发的问题不至于导致应用程序的崩溃。 | |||||
--> | |||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
autoReload="true" | |||||
internalLogFile="/data/gps_services/serviceslogs/JT808.Gateway.SimpleServer/internalLog.txt" | |||||
internalLogLevel="Debug" > | |||||
<variable name="Directory" value="/data/gps_services/serviceslogs/JT808.Gateway.SimpleServer"/> | |||||
<targets> | |||||
<target name="SimpleServer" xsi:type="File" | |||||
fileName="${Directory}/SimpleServer.${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||||
<target name="console" xsi:type="ColoredConsole" | |||||
useDefaultRowHighlightingRules="false" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level} ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"> | |||||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> | |||||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> | |||||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> | |||||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> | |||||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> | |||||
</target> | |||||
</targets> | |||||
<rules> | |||||
<logger name="*" minlevel="Debug" maxlevel="Fatal" writeTo="SimpleServer"/> | |||||
</rules> | |||||
</nlog> |
@@ -1,36 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8" ?> | |||||
<!-- | |||||
参考:http://www.cnblogs.com/fuchongjundream/p/3936431.html | |||||
autoReload:自动再配置 | |||||
internalLogFile:可以让NLog把内部的调试和异常信息都写入指定文件里程序没问题了,日志却出了问题。这个该怎么办,到底是哪里不正确了?假如日志本身除了bug该如何解决?这就需要日志排错。把日志的错误信息写入日志。 | |||||
<nlog throwExceptions="true" /> | |||||
<nlog internalLogFile="file.txt" />- 设置internalLogFile属性可以让NLog把内部的调试和异常信息都写入指定文件里。 | |||||
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" /> - 决定内部日志的级别,级别越高,输出的日志信息越简洁。 | |||||
<nlog internalLogToConsole="false|true" /> - 是否把内部日志输出到标准控制台。 | |||||
<nlog internalLogToConsoleError="false|true" /> - 是否把内部日志输出到标准错误控制台 (stderr)。 | |||||
设置throwExceptions属性为“true”可以让NLog不再阻挡这类异常,而是把它们抛给调用者。在部署是这样做可以帮我们快速定位问题。一旦应用程序已经正确配置了,我们建议把throwExceptions的值设为“false”,这样由于日志引发的问题不至于导致应用程序的崩溃。 | |||||
--> | |||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
autoReload="true" | |||||
internalLogFile="wwwroot/logs/JT808.Gateway.SimpleServer/internalLog.txt" | |||||
internalLogLevel="Debug" > | |||||
<variable name="Directory" value="/wwwroot/logs/JT808.Gateway.SimpleServer"/> | |||||
<targets> | |||||
<target name="SimpleServer" xsi:type="File" | |||||
fileName="${Directory}/SimpleServer.${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||||
<target name="console" xsi:type="ColoredConsole" | |||||
useDefaultRowHighlightingRules="false" | |||||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level} ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"> | |||||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> | |||||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> | |||||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> | |||||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> | |||||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> | |||||
</target> | |||||
</targets> | |||||
<rules> | |||||
<logger name="*" minlevel="Trace" maxlevel="Fatal" writeTo="SimpleServer,console"/> | |||||
</rules> | |||||
</nlog> |
@@ -1,39 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>netcoreapp3.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Grpc.AspNetCore" Version="2.24.0" /> | |||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.1" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.Gateway\JT808.Gateway.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Update="appsettings.Development.json"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="appsettings.json"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\nlog.Unix.config"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\nlog.Win32NT.config"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\NLog.xsd"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\test.cer"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | |||||
</Project> | |||||
@@ -1,71 +0,0 @@ | |||||
using JT808.Gateway.Services; | |||||
using JT808.Gateway.Tcp; | |||||
using JT808.Gateway.Udp; | |||||
using JT808.Protocol; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Server.Kestrel.Core; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using NLog.Extensions.Logging; | |||||
using System; | |||||
using System.IO; | |||||
using System.Net; | |||||
namespace JT808.Gateway.SimpleServer | |||||
{ | |||||
class Program | |||||
{ | |||||
static void Main(string[] args) | |||||
{ | |||||
Host.CreateDefaultBuilder(args) | |||||
.ConfigureAppConfiguration((hostingContext, config) => | |||||
{ | |||||
config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); | |||||
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); | |||||
}) | |||||
.ConfigureLogging((hostContext, configLogging) => { | |||||
Console.WriteLine($"Environment.OSVersion.Platform:{Environment.OSVersion.Platform.ToString()}"); | |||||
NLog.LogManager.LoadConfiguration($"Configs/nlog.{Environment.OSVersion.Platform.ToString()}.config"); | |||||
configLogging.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); | |||||
configLogging.SetMinimumLevel(LogLevel.Trace); | |||||
}) | |||||
.ConfigureWebHostDefaults(webBuilder => | |||||
{ | |||||
webBuilder | |||||
.ConfigureKestrel(options => | |||||
{ | |||||
options.Listen(IPAddress.Any, 5001, listenOptions => | |||||
{ | |||||
listenOptions.Protocols = HttpProtocols.Http2; | |||||
listenOptions.UseHttps($"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "test.cer")}", ""); | |||||
}); | |||||
}) | |||||
.Configure(app => | |||||
{ | |||||
app.UseRouting(); | |||||
app.UseEndpoints(endpoints => | |||||
{ | |||||
endpoints.MapGrpcService<JT808GatewayService>(); | |||||
}); | |||||
}); | |||||
}) | |||||
.ConfigureServices((hostContext,services) => | |||||
{ | |||||
//services.Configure<KestrelServerOptions>(hostContext.Configuration.GetSection("Kestrel")); | |||||
services.AddGrpc(); | |||||
services.AddSingleton<ILoggerFactory, LoggerFactory>(); | |||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); | |||||
services.AddJT808Configure() | |||||
.AddJT808Gateway(hostContext.Configuration) | |||||
.AddJT808GatewayTcpHost() | |||||
.AddJT808GatewayUdpHost() | |||||
.Builder(); | |||||
}) | |||||
.Build() | |||||
.Run(); | |||||
} | |||||
} | |||||
} |
@@ -1,102 +0,0 @@ | |||||
| |||||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||||
# Visual Studio Version 16 | |||||
VisualStudioVersion = 16.0.29411.108 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway", "JT808.Gateway\JT808.Gateway.csproj", "{A42A396F-D32B-4AC2-B554-735AA7E2DBA8}" | |||||
EndProject | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.Gateway.SimpleServer", "JT808.Gateway.SimpleServer\JT808.Gateway.SimpleServer.csproj", "{F9ABFDDB-84A2-44C8-A162-A1FE4EA4D332}" | |||||
EndProject | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.Gateway.SimpleClient", "JT808.Gateway.SimpleClient\JT808.Gateway.SimpleClient.csproj", "{886D4937-7265-40DC-87CC-85CE35553214}" | |||||
EndProject | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.Gateway.Kafka", "JT808.Gateway.Kafka\JT808.Gateway.Kafka.csproj", "{790E132C-7D92-4A6A-8CF0-2C181331FB31}" | |||||
EndProject | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.Gateway.CleintBenchmark", "JT808.Gateway.CleintBenchmark\JT808.Gateway.CleintBenchmark.csproj", "{1A925C08-2590-4E55-84F2-96F01306D34D}" | |||||
EndProject | |||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DC0E0AC1-C4BD-4291-AD16-744080411C3D}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway.Traffic.Test", "JT808.Gateway.Tests\JT808.Gateway.Traffic.Test\JT808.Gateway.Traffic.Test.csproj", "{D4DF5285-612A-417D-BDE4-690BDF0C07C6}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway.Transmit.Test", "JT808.Gateway.Tests\JT808.Gateway.Transmit.Test\JT808.Gateway.Transmit.Test.csproj", "{5E2C290C-637B-4F8B-AE03-1A4B669F7FFB}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway.SessionNotice.Test", "JT808.Gateway.Tests\JT808.Gateway.SessionNotice.Test\JT808.Gateway.SessionNotice.Test.csproj", "{562B16BD-1D4C-40FD-86CF-7845DBC9C1EE}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway.ReplyMessage.Test", "JT808.Gateway.Tests\JT808.Gateway.ReplyMessage.Test\JT808.Gateway.ReplyMessage.Test.csproj", "{CDE064DE-0E8E-4165-8F6A-6C03A8783506}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway.MsgLogging.Test", "JT808.Gateway.Tests\JT808.Gateway.MsgLogging.Test\JT808.Gateway.MsgLogging.Test.csproj", "{8BD638B1-1F16-4BA8-8506-1B83DA4A884E}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Gateway.MsgIdHandler.Test", "JT808.Gateway.Tests\JT808.Gateway.MsgIdHandler.Test\JT808.Gateway.MsgIdHandler.Test.csproj", "{CC2ABC8E-CC78-4B68-8670-90C2BCAB434E}" | |||||
EndProject | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.Gateway.Test", "JT808.Gateway.Tests\JT808.Gateway.Test\JT808.Gateway.Test.csproj", "{F1031636-69CF-4C44-AF0B-F8BE8DE03864}" | |||||
EndProject | |||||
Global | |||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||||
Debug|Any CPU = Debug|Any CPU | |||||
Release|Any CPU = Release|Any CPU | |||||
EndGlobalSection | |||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||||
{A42A396F-D32B-4AC2-B554-735AA7E2DBA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{A42A396F-D32B-4AC2-B554-735AA7E2DBA8}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{A42A396F-D32B-4AC2-B554-735AA7E2DBA8}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{A42A396F-D32B-4AC2-B554-735AA7E2DBA8}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{F9ABFDDB-84A2-44C8-A162-A1FE4EA4D332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{F9ABFDDB-84A2-44C8-A162-A1FE4EA4D332}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{F9ABFDDB-84A2-44C8-A162-A1FE4EA4D332}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{F9ABFDDB-84A2-44C8-A162-A1FE4EA4D332}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{886D4937-7265-40DC-87CC-85CE35553214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{886D4937-7265-40DC-87CC-85CE35553214}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{886D4937-7265-40DC-87CC-85CE35553214}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{886D4937-7265-40DC-87CC-85CE35553214}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{790E132C-7D92-4A6A-8CF0-2C181331FB31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{790E132C-7D92-4A6A-8CF0-2C181331FB31}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{790E132C-7D92-4A6A-8CF0-2C181331FB31}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{790E132C-7D92-4A6A-8CF0-2C181331FB31}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{1A925C08-2590-4E55-84F2-96F01306D34D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{1A925C08-2590-4E55-84F2-96F01306D34D}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{1A925C08-2590-4E55-84F2-96F01306D34D}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{1A925C08-2590-4E55-84F2-96F01306D34D}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{D4DF5285-612A-417D-BDE4-690BDF0C07C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{D4DF5285-612A-417D-BDE4-690BDF0C07C6}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{D4DF5285-612A-417D-BDE4-690BDF0C07C6}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{D4DF5285-612A-417D-BDE4-690BDF0C07C6}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{5E2C290C-637B-4F8B-AE03-1A4B669F7FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{5E2C290C-637B-4F8B-AE03-1A4B669F7FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{5E2C290C-637B-4F8B-AE03-1A4B669F7FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{5E2C290C-637B-4F8B-AE03-1A4B669F7FFB}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{562B16BD-1D4C-40FD-86CF-7845DBC9C1EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{562B16BD-1D4C-40FD-86CF-7845DBC9C1EE}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{562B16BD-1D4C-40FD-86CF-7845DBC9C1EE}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{562B16BD-1D4C-40FD-86CF-7845DBC9C1EE}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{CDE064DE-0E8E-4165-8F6A-6C03A8783506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{CDE064DE-0E8E-4165-8F6A-6C03A8783506}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{CDE064DE-0E8E-4165-8F6A-6C03A8783506}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{CDE064DE-0E8E-4165-8F6A-6C03A8783506}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{8BD638B1-1F16-4BA8-8506-1B83DA4A884E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{8BD638B1-1F16-4BA8-8506-1B83DA4A884E}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{8BD638B1-1F16-4BA8-8506-1B83DA4A884E}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{8BD638B1-1F16-4BA8-8506-1B83DA4A884E}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{CC2ABC8E-CC78-4B68-8670-90C2BCAB434E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{CC2ABC8E-CC78-4B68-8670-90C2BCAB434E}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{CC2ABC8E-CC78-4B68-8670-90C2BCAB434E}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{CC2ABC8E-CC78-4B68-8670-90C2BCAB434E}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{F1031636-69CF-4C44-AF0B-F8BE8DE03864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{F1031636-69CF-4C44-AF0B-F8BE8DE03864}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{F1031636-69CF-4C44-AF0B-F8BE8DE03864}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{F1031636-69CF-4C44-AF0B-F8BE8DE03864}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | |||||
GlobalSection(SolutionProperties) = preSolution | |||||
HideSolutionNode = FALSE | |||||
EndGlobalSection | |||||
GlobalSection(NestedProjects) = preSolution | |||||
{D4DF5285-612A-417D-BDE4-690BDF0C07C6} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
{5E2C290C-637B-4F8B-AE03-1A4B669F7FFB} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
{562B16BD-1D4C-40FD-86CF-7845DBC9C1EE} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
{CDE064DE-0E8E-4165-8F6A-6C03A8783506} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
{8BD638B1-1F16-4BA8-8506-1B83DA4A884E} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
{CC2ABC8E-CC78-4B68-8670-90C2BCAB434E} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
{F1031636-69CF-4C44-AF0B-F8BE8DE03864} = {DC0E0AC1-C4BD-4291-AD16-744080411C3D} | |||||
EndGlobalSection | |||||
GlobalSection(ExtensibilityGlobals) = postSolution | |||||
SolutionGuid = {F5671BD2-B44A-4A7C-80EA-E060A512992D} | |||||
EndGlobalSection | |||||
EndGlobal |
@@ -1,14 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.MsgIdHandler | |||||
{ | |||||
/// <summary> | |||||
/// JT808消息Id处理程序 | |||||
/// </summary> | |||||
public interface IJT808MsgIdHandler | |||||
{ | |||||
void Processor((string TerminalNo, byte[] Data) parameter); | |||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.MsgIdHandler | |||||
{ | |||||
public static class JT808MsgIdHandlerExtensions | |||||
{ | |||||
public static IJT808ClientBuilder AddJT808MsgIdHandler<TJT808MsgIdHandler>(this IJT808ClientBuilder jT808ClientBuilder) | |||||
where TJT808MsgIdHandler: IJT808MsgIdHandler | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton(typeof(IJT808MsgIdHandler),typeof(TJT808MsgIdHandler)); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808MsgIdHandlerHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,34 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.MsgIdHandler | |||||
{ | |||||
public class JT808MsgIdHandlerHostedService : IHostedService | |||||
{ | |||||
private readonly IJT808MsgConsumer jT808MsgConsumer; | |||||
private readonly IJT808MsgIdHandler jT808MsgIdHandler; | |||||
public JT808MsgIdHandlerHostedService( | |||||
IJT808MsgIdHandler jT808DotNettyMsgIdHandler, | |||||
IJT808MsgConsumer jT808MsgConsumer) | |||||
{ | |||||
this.jT808MsgIdHandler = jT808DotNettyMsgIdHandler; | |||||
this.jT808MsgConsumer = jT808MsgConsumer; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Subscribe(); | |||||
jT808MsgConsumer.OnMessage(jT808MsgIdHandler.Processor); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.BusinessServices.MsgLogging | |||||
{ | |||||
/// <summary> | |||||
/// 808数据上下行日志接口 | |||||
/// </summary> | |||||
public interface IJT808MsgLogging | |||||
{ | |||||
void Processor((string TerminalNo, byte[] Data) parameter, JT808MsgLoggingType jT808MsgLoggingType); | |||||
} | |||||
} |
@@ -1,36 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.MsgLogging | |||||
{ | |||||
public class JT808MsgDownLoggingHostedService : IHostedService | |||||
{ | |||||
private readonly IJT808MsgReplyConsumer jT808MsgReplyConsumer; | |||||
private readonly IJT808MsgLogging jT808MsgLogging; | |||||
public JT808MsgDownLoggingHostedService( | |||||
IJT808MsgLogging jT808MsgLogging, | |||||
IJT808MsgReplyConsumer jT808MsgReplyConsumer) | |||||
{ | |||||
this.jT808MsgReplyConsumer = jT808MsgReplyConsumer; | |||||
this.jT808MsgLogging = jT808MsgLogging; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgReplyConsumer.Subscribe(); | |||||
jT808MsgReplyConsumer.OnMessage(item=> | |||||
{ | |||||
jT808MsgLogging.Processor(item, JT808MsgLoggingType.down); | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgReplyConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,19 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.MsgLogging | |||||
{ | |||||
public static class JT808MsgLoggingExtensions | |||||
{ | |||||
public static IJT808ClientBuilder AddJT808MsgLogging<TJT808MsgLogging>(this IJT808ClientBuilder jT808ClientBuilder) | |||||
where TJT808MsgLogging: IJT808MsgLogging | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton(typeof(IJT808MsgLogging),typeof(TJT808MsgLogging)); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808MsgDownLoggingHostedService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808MsgUpLoggingHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.MsgLogging | |||||
{ | |||||
public enum JT808MsgLoggingType | |||||
{ | |||||
/// <summary> | |||||
/// 数据上行 | |||||
/// </summary> | |||||
up, | |||||
/// <summary> | |||||
/// 数据下行 | |||||
/// </summary> | |||||
down | |||||
} | |||||
} |
@@ -1,36 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.MsgLogging | |||||
{ | |||||
public class JT808MsgUpLoggingHostedService : IHostedService | |||||
{ | |||||
private readonly IJT808MsgConsumer jT808MsgConsumer; | |||||
private readonly IJT808MsgLogging jT808MsgLogging; | |||||
public JT808MsgUpLoggingHostedService( | |||||
IJT808MsgLogging jT808MsgLogging, | |||||
IJT808MsgConsumer jT808MsgConsumer) | |||||
{ | |||||
this.jT808MsgConsumer = jT808MsgConsumer; | |||||
this.jT808MsgLogging = jT808MsgLogging; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Subscribe(); | |||||
jT808MsgConsumer.OnMessage(item=> | |||||
{ | |||||
jT808MsgLogging.Processor(item, JT808MsgLoggingType.up); | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,57 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.ReplyMessage | |||||
{ | |||||
public static class JT808ReplyMessageExtensions | |||||
{ | |||||
/// <summary> | |||||
/// 独享消息应答服务(不同的消费者实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddInprocJT808ReplyMessage(this IJT808ClientBuilder jT808ClientBuilder) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808ReplyMessageService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808ReplyMessageHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 独享消息应答服务(不同的消费者实例) | |||||
/// </summary> | |||||
/// <typeparam name="TReplyMessageService">自定义消息回复服务</typeparam> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddInprocJT808ReplyMessage<TReplyMessageService>(this IJT808ClientBuilder jT808ClientBuilder) | |||||
where TReplyMessageService : JT808ReplyMessageService | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808ReplyMessageService,TReplyMessageService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808ReplyMessageHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 共享消息应答服务(消费者单实例) | |||||
/// </summary> | |||||
/// <typeparam name="TReplyMessageService">自定义消息回复服务</typeparam> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddShareJT808ReplyMessage<TReplyMessageService>(this IJT808ClientBuilder jT808ClientBuilder) | |||||
where TReplyMessageService : JT808ReplyMessageService | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808ReplyMessageService, TReplyMessageService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 共享消息应答服务(消费者单实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddShareJT808ReplyMessage(this IJT808ClientBuilder jT808ClientBuilder) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808ReplyMessageService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,34 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.ReplyMessage | |||||
{ | |||||
public class JT808ReplyMessageHostedService : IHostedService | |||||
{ | |||||
private readonly IJT808MsgConsumer jT808MsgConsumer; | |||||
private readonly JT808ReplyMessageService jT808DotNettyReplyMessageService; | |||||
public JT808ReplyMessageHostedService( | |||||
JT808ReplyMessageService jT808DotNettyReplyMessageService, | |||||
IJT808MsgConsumer jT808MsgConsumer) | |||||
{ | |||||
this.jT808MsgConsumer = jT808MsgConsumer; | |||||
this.jT808DotNettyReplyMessageService = jT808DotNettyReplyMessageService; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Subscribe(); | |||||
jT808MsgConsumer.OnMessage(jT808DotNettyReplyMessageService.Processor); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,165 +0,0 @@ | |||||
using JT808.Gateway.PubSub; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Enums; | |||||
using JT808.Protocol.Extensions; | |||||
using JT808.Protocol.MessageBody; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.ReplyMessage | |||||
{ | |||||
public class JT808ReplyMessageService | |||||
{ | |||||
protected Dictionary<ushort, Func<JT808HeaderPackage, byte[]>> HandlerDict { get; } | |||||
protected JT808Serializer JT808Serializer { get; } | |||||
protected IJT808MsgReplyProducer JT808MsgReplyProducer { get; } | |||||
public JT808ReplyMessageService( | |||||
IJT808Config jT808Config, | |||||
IJT808MsgReplyProducer jT808MsgReplyProducer) | |||||
{ | |||||
this.JT808Serializer = jT808Config.GetSerializer(); | |||||
this.JT808MsgReplyProducer = jT808MsgReplyProducer; | |||||
HandlerDict = new Dictionary<ushort, Func<JT808HeaderPackage, byte[]>> { | |||||
{JT808MsgId.终端通用应答.ToUInt16Value(), Msg0x0001}, | |||||
{JT808MsgId.终端鉴权.ToUInt16Value(), Msg0x0102}, | |||||
{JT808MsgId.终端心跳.ToUInt16Value(), Msg0x0002}, | |||||
{JT808MsgId.终端注销.ToUInt16Value(), Msg0x0003}, | |||||
{JT808MsgId.终端注册.ToUInt16Value(), Msg0x0100}, | |||||
{JT808MsgId.位置信息汇报.ToUInt16Value(),Msg0x0200 }, | |||||
{JT808MsgId.定位数据批量上传.ToUInt16Value(),Msg0x0704 }, | |||||
{JT808MsgId.数据上行透传.ToUInt16Value(),Msg0x0900 } | |||||
}; | |||||
} | |||||
public virtual void Processor((string TerminalNo, byte[] Data) parameter) | |||||
{ | |||||
try | |||||
{ | |||||
var request = JT808Serializer.HeaderDeserialize(parameter.Data); | |||||
if (HandlerDict.TryGetValue(request.Header.MsgId, out var func)) | |||||
{ | |||||
var buffer = func(request); | |||||
if (buffer != null) | |||||
{ | |||||
JT808MsgReplyProducer.ProduceAsync(parameter.TerminalNo, buffer); | |||||
} | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// 终端通用应答 | |||||
/// 平台无需回复 | |||||
/// 实现自己的业务 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0001(JT808HeaderPackage request) | |||||
{ | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 终端心跳 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0002(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.平台通用应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8001() | |||||
{ | |||||
MsgId = request.Header.MsgId, | |||||
JT808PlatformResult = JT808PlatformResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 终端注销 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0003(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.平台通用应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8001() | |||||
{ | |||||
MsgId = request.Header.MsgId, | |||||
JT808PlatformResult = JT808PlatformResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 终端注册 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0100(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.终端注册应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8100() | |||||
{ | |||||
Code = "J" + request.Header.TerminalPhoneNo, | |||||
JT808TerminalRegisterResult = JT808TerminalRegisterResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 终端鉴权 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0102(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.平台通用应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8001() | |||||
{ | |||||
MsgId = request.Header.MsgId, | |||||
JT808PlatformResult = JT808PlatformResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 位置信息汇报 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0200(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.平台通用应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8001() | |||||
{ | |||||
MsgId = request.Header.MsgId, | |||||
JT808PlatformResult = JT808PlatformResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 定位数据批量上传 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0704(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.平台通用应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8001() | |||||
{ | |||||
MsgId = request.Header.MsgId, | |||||
JT808PlatformResult = JT808PlatformResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 数据上行透传 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public virtual byte[] Msg0x0900(JT808HeaderPackage request) | |||||
{ | |||||
return JT808Serializer.Serialize(JT808MsgId.平台通用应答.Create(request.Header.TerminalPhoneNo, new JT808_0x8001() | |||||
{ | |||||
MsgId = request.Header.MsgId, | |||||
JT808PlatformResult = JT808PlatformResult.成功, | |||||
MsgNum = request.Header.MsgNum | |||||
})); | |||||
} | |||||
} | |||||
} |
@@ -1,60 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.SessionNotice | |||||
{ | |||||
public static class JT808SessionNoticeExtensions | |||||
{ | |||||
/// <summary> | |||||
/// 独享消息会话通知服务(不同的消费者实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddInprocJT808SessionNotice(this IJT808ClientBuilder jT808ClientBuilder) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808SessionNoticeService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808SessionNoticeHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 独享消息会话通知服务(不同的消费者实例) | |||||
/// </summary> | |||||
/// <typeparam name="TSessionNoticeService">自定义会话通知服务</typeparam> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddInprocJT808SessionNotice<TSessionNoticeService>(this IJT808ClientBuilder jT808ClientBuilder) | |||||
where TSessionNoticeService : JT808SessionNoticeService | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808SessionNoticeService,TSessionNoticeService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808SessionNoticeHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 共享消息会话通知服务(消费者单实例) | |||||
/// </summary> | |||||
/// <typeparam name="TSessionNoticeService">自定义会话通知服务</typeparam> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddShareJT808SessionNotice<TSessionNoticeService>(this IJT808ClientBuilder jT808ClientBuilder) | |||||
where TSessionNoticeService : JT808SessionNoticeService | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808SessionNoticeService, TSessionNoticeService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 共享消息会话通知服务(消费者单实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddShareJT808SessionNotice(this IJT808ClientBuilder jT808ClientBuilder) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808SessionNoticeService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,35 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.SessionNotice | |||||
{ | |||||
public class JT808SessionNoticeHostedService : IHostedService | |||||
{ | |||||
private readonly JT808SessionNoticeService jT808DotNettySessionNoticeService; | |||||
private readonly IJT808SessionConsumer jT808SessionConsumer; | |||||
public JT808SessionNoticeHostedService( | |||||
IJT808SessionConsumer jT808SessionConsumer, | |||||
JT808SessionNoticeService jT808DotNettySessionNoticeService) | |||||
{ | |||||
this.jT808DotNettySessionNoticeService = jT808DotNettySessionNoticeService; | |||||
this.jT808SessionConsumer = jT808SessionConsumer; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808SessionConsumer.Subscribe(); | |||||
jT808SessionConsumer.OnMessage(jT808DotNettySessionNoticeService.Processor); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808SessionConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.SessionNotice | |||||
{ | |||||
public class JT808SessionNoticeService | |||||
{ | |||||
protected ILogger logger { get; } | |||||
public JT808SessionNoticeService(ILoggerFactory loggerFactory) | |||||
{ | |||||
logger = loggerFactory.CreateLogger("JT808DotNettySessionNoticeService"); | |||||
} | |||||
public virtual void Processor((string Notice, string TerminalNo) parameter) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"{parameter.Notice}-{parameter.TerminalNo}"); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,32 +0,0 @@ | |||||
using Microsoft.Extensions.Configuration; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.Traffic | |||||
{ | |||||
public class JT808TrafficService:IDisposable | |||||
{ | |||||
private readonly CSRedis.CSRedisClient redisClien; | |||||
public JT808TrafficService(IConfiguration configuration) | |||||
{ | |||||
redisClien = new CSRedis.CSRedisClient(configuration.GetConnectionString("TrafficRedisHost")); | |||||
TrafficRedisClient.Initialization(redisClien); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
redisClien.Dispose(); | |||||
} | |||||
/// <summary> | |||||
/// 按设备每天统计sim卡流量 | |||||
/// </summary> | |||||
/// <param name="terminalNo"></param> | |||||
/// <param name="len"></param> | |||||
public void Processor(string terminalNo,int len) | |||||
{ | |||||
TrafficRedisClient.HIncrBy(terminalNo, DateTime.Now.ToString("yyyyMMdd"), len); | |||||
} | |||||
} | |||||
} |
@@ -1,33 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.Traffic | |||||
{ | |||||
public static class JT808TrafficServiceExtensions | |||||
{ | |||||
/// <summary> | |||||
/// 独享消息流量统计服务(不同的消费者实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddInprocJT808Traffic(this IJT808ClientBuilder jT808ClientBuilder) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808TrafficService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808TrafficServiceHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 共享消息流量统计服务(消费者单实例) | |||||
/// </summary> | |||||
/// <typeparam name="TReplyMessageService"></typeparam> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddShareJT808Traffic(this IJT808ClientBuilder jT808ClientBuilder) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808TrafficService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,38 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Protocol.Extensions; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.Traffic | |||||
{ | |||||
public class JT808TrafficServiceHostedService : IHostedService | |||||
{ | |||||
private readonly IJT808MsgConsumer jT808MsgConsumer; | |||||
private readonly JT808TrafficService jT808DotNettyTrafficService; | |||||
public JT808TrafficServiceHostedService( | |||||
JT808TrafficService jT808DotNettyTrafficService, | |||||
IJT808MsgConsumer jT808MsgConsumer) | |||||
{ | |||||
this.jT808MsgConsumer = jT808MsgConsumer; | |||||
this.jT808DotNettyTrafficService = jT808DotNettyTrafficService; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Subscribe(); | |||||
jT808MsgConsumer.OnMessage((item)=> { | |||||
string str = item.Data.ToHexString(); | |||||
jT808DotNettyTrafficService.Processor(item.TerminalNo, item.Data.Length); | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,9 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.Traffic | |||||
{ | |||||
class TrafficRedisClient: RedisHelper<TrafficRedisClient> | |||||
{ } | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.Transmit.Configs | |||||
{ | |||||
public class DataTransferOptions | |||||
{ | |||||
public string Host { get; set; } | |||||
public List<string> TerminalNos { get; set; } | |||||
} | |||||
} |
@@ -1,11 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.BusinessServices.Transmit.Configs | |||||
{ | |||||
public class RemoteServerOptions | |||||
{ | |||||
public List<DataTransferOptions> DataTransfer { get; set; } | |||||
} | |||||
} |
@@ -1,76 +0,0 @@ | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using Polly; | |||||
using System; | |||||
using System.Linq; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace JT808.Gateway.BusinessServices.Transmit.Handlers | |||||
{ | |||||
public class ClientConnectionHandler : ChannelHandlerAdapter | |||||
{ | |||||
private readonly Bootstrap bootstrap; | |||||
public Dictionary<string, IChannel> channeldic; | |||||
private readonly ILogger<ClientConnectionHandler> logger; | |||||
public ClientConnectionHandler(Bootstrap bootstrap, | |||||
Dictionary<string, IChannel> channeldic, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.bootstrap = bootstrap; | |||||
this.channeldic = channeldic; | |||||
logger = loggerFactory.CreateLogger<ClientConnectionHandler>(); | |||||
} | |||||
public override void ChannelInactive(IChannelHandlerContext context) | |||||
{ | |||||
Policy.HandleResult<bool>(context.Channel.Open) | |||||
.WaitAndRetryForeverAsync(retryAttempt => | |||||
{ | |||||
return retryAttempt > 20 ? TimeSpan.FromSeconds(Math.Pow(2, 50)) : TimeSpan.FromSeconds(Math.Pow(2, retryAttempt));//超过重试20次,之后重试都是接近12个小时重试一次 | |||||
}, | |||||
(exception, timespan, ctx) => | |||||
{ | |||||
logger.LogError($"服务端断开{context.Channel.RemoteAddress},重试结果{exception.Result},重试次数{timespan},下次重试间隔(s){ctx.TotalSeconds}"); | |||||
}) | |||||
.ExecuteAsync(async () => | |||||
{ | |||||
try | |||||
{ | |||||
var oldChannel = channeldic.FirstOrDefault(m => m.Value == context.Channel); | |||||
if (default(KeyValuePair<string, IChannel>).Equals(oldChannel)) | |||||
{ | |||||
if(logger.IsEnabled( LogLevel.Debug)) | |||||
logger.LogDebug($"服务器已经删除了{oldChannel.Key}远程服务器配置"); | |||||
return true; | |||||
} | |||||
var channel = await bootstrap.ConnectAsync(context.Channel.RemoteAddress); | |||||
channeldic.Remove(oldChannel.Key); | |||||
channeldic.Add(oldChannel.Key, channel); | |||||
return channel.Open; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError($"服务端断开后{context.Channel.RemoteAddress},重连异常:{ex}"); | |||||
return false; | |||||
} | |||||
}); | |||||
} | |||||
public override void ChannelRead(IChannelHandlerContext context, object message) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogError($"服务端返回消息{message}"); | |||||
} | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
logger.LogError($"服务端Exception: {exception}"); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
} |
@@ -1,39 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.Configuration; | |||||
using JT808.Gateway.BusinessServices.Transmit.Configs; | |||||
namespace JT808.Gateway.BusinessServices.Transmit | |||||
{ | |||||
public static class JT808DotNettyTransmitExtensions | |||||
{ | |||||
/// <summary> | |||||
/// 独享转发服务(不同的消费者实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <param name="configuration"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddInprocJT808Transmit(this IJT808ClientBuilder jT808ClientBuilder,IConfiguration configuration) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.Configure<RemoteServerOptions>(configuration.GetSection("RemoteServerOptions")); | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808DotNettyTransmitService>(); | |||||
jT808ClientBuilder.JT808Builder.Services.AddHostedService<JT808DotNettyTransmitHostedService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
/// <summary> | |||||
/// 共享转发服务(消费者单实例) | |||||
/// </summary> | |||||
/// <param name="jT808ClientBuilder"></param> | |||||
/// <param name="configuration"></param> | |||||
/// <returns></returns> | |||||
public static IJT808ClientBuilder AddShareJT808Transmit(this IJT808ClientBuilder jT808ClientBuilder, IConfiguration configuration) | |||||
{ | |||||
jT808ClientBuilder.JT808Builder.Services.Configure<RemoteServerOptions>(configuration.GetSection("RemoteServerOptions")); | |||||
jT808ClientBuilder.JT808Builder.Services.AddSingleton<JT808DotNettyTransmitService>(); | |||||
return jT808ClientBuilder; | |||||
} | |||||
} | |||||
} |
@@ -1,33 +0,0 @@ | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System.Threading; | |||||
using JT808.Gateway.PubSub; | |||||
namespace JT808.Gateway.BusinessServices.Transmit | |||||
{ | |||||
public class JT808DotNettyTransmitHostedService:IHostedService | |||||
{ | |||||
private readonly JT808DotNettyTransmitService jT808DotNettyTransmitService; | |||||
private readonly IJT808MsgConsumer jT808MsgConsumer; | |||||
public JT808DotNettyTransmitHostedService( | |||||
IJT808MsgConsumer jT808MsgConsumer, | |||||
JT808DotNettyTransmitService jT808DotNettyTransmitService) | |||||
{ | |||||
this.jT808DotNettyTransmitService = jT808DotNettyTransmitService; | |||||
this.jT808MsgConsumer = jT808MsgConsumer; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Subscribe(); | |||||
jT808MsgConsumer.OnMessage(jT808DotNettyTransmitService.Send); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
jT808MsgConsumer.Unsubscribe(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,236 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System.Linq; | |||||
using JT808.Gateway.BusinessServices.Transmit.Configs; | |||||
using JT808.Gateway.BusinessServices.Transmit.Handlers; | |||||
namespace JT808.Gateway.BusinessServices.Transmit | |||||
{ | |||||
public class JT808DotNettyTransmitService | |||||
{ | |||||
private readonly ILogger logger; | |||||
private readonly ILoggerFactory loggerFactory; | |||||
private IOptionsMonitor<RemoteServerOptions> optionsMonitor; | |||||
public Dictionary<string, IChannel> channeldic = new Dictionary<string, IChannel>(); | |||||
public JT808DotNettyTransmitService(ILoggerFactory loggerFactory, | |||||
IOptionsMonitor<RemoteServerOptions> optionsMonitor) | |||||
{ | |||||
this.loggerFactory = loggerFactory; | |||||
logger = loggerFactory.CreateLogger("JT808DotNettyTransmitService"); | |||||
this.optionsMonitor = optionsMonitor; | |||||
InitialDispatcherClient(); | |||||
} | |||||
public void Send((string TerminalNo, byte[] Data) parameter) | |||||
{ | |||||
if (optionsMonitor.CurrentValue.DataTransfer != null) | |||||
{ | |||||
foreach (var item in optionsMonitor.CurrentValue.DataTransfer) | |||||
{ | |||||
if (channeldic.TryGetValue($"all_{item.Host}", out var allClientChannel)) | |||||
{ | |||||
try | |||||
{ | |||||
if (allClientChannel.Open) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"转发所有数据到该网关{item.Host}"); | |||||
} | |||||
allClientChannel.WriteAndFlushAsync(Unpooled.WrappedBuffer(parameter.Data)); | |||||
} | |||||
else | |||||
{ | |||||
logger.LogError($"{item.Host}链接已关闭"); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError($"{item.Host}发送数据出现异常:{ex}"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (item.TerminalNos.Contains(parameter.TerminalNo) && channeldic.TryGetValue($"{parameter.TerminalNo}_{item.Host}", out var clientChannel)) | |||||
{ | |||||
try | |||||
{ | |||||
if (clientChannel.Open) | |||||
{ | |||||
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) | |||||
logger.LogDebug($"转发{parameter.TerminalNo}到该网关{item.Host}"); | |||||
clientChannel.WriteAndFlushAsync(Unpooled.WrappedBuffer(parameter.Data)); | |||||
} | |||||
else | |||||
{ | |||||
logger.LogError($"{item.Host},{parameter.TerminalNo}链接已关闭"); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError($"{item.Host},{parameter.TerminalNo}发送数据出现异常:{ex}"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public void InitialDispatcherClient() | |||||
{ | |||||
Task.Run(async () => | |||||
{ | |||||
var group = new MultithreadEventLoopGroup(); | |||||
var bootstrap = new Bootstrap(); | |||||
bootstrap.Group(group) | |||||
.Channel<TcpSocketChannel>() | |||||
.Option(ChannelOption.TcpNodelay, true) | |||||
.Handler(new ActionChannelInitializer<ISocketChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
pipeline.AddLast(new ClientConnectionHandler(bootstrap, channeldic, loggerFactory)); | |||||
})); | |||||
optionsMonitor.OnChange(options => | |||||
{ | |||||
List<string> lastRemoteServers = new List<string>(); | |||||
if (options.DataTransfer != null) | |||||
{ | |||||
if (options.DataTransfer.Any()) | |||||
{ | |||||
foreach (var item in options.DataTransfer) | |||||
{ | |||||
if (item.TerminalNos != null) | |||||
{ | |||||
if (item.TerminalNos.Any()) | |||||
{ | |||||
foreach (var terminal in item.TerminalNos) | |||||
{ | |||||
lastRemoteServers.Add($"{terminal}_{item.Host}"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
lastRemoteServers.Add($"all_{item.Host}"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
lastRemoteServers.Add($"all_{item.Host}"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
DelRemoteServsers(lastRemoteServers); | |||||
AddRemoteServsers(bootstrap, lastRemoteServers); | |||||
}); | |||||
await InitRemoteServsers(bootstrap); | |||||
}); | |||||
} | |||||
/// <summary> | |||||
/// 初始化远程服务器 | |||||
/// </summary> | |||||
/// <param name="bootstrap"></param> | |||||
/// <param name="remoteServers"></param> | |||||
/// <returns></returns> | |||||
private async Task InitRemoteServsers(Bootstrap bootstrap) | |||||
{ | |||||
List<string> remoteServers = new List<string>(); | |||||
if (optionsMonitor.CurrentValue.DataTransfer != null) | |||||
{ | |||||
if (optionsMonitor.CurrentValue.DataTransfer.Any()) | |||||
{ | |||||
foreach (var item in optionsMonitor.CurrentValue.DataTransfer) | |||||
{ | |||||
if (item.TerminalNos != null) | |||||
{ | |||||
if (item.TerminalNos.Any()) | |||||
{ | |||||
foreach (var terminal in item.TerminalNos) | |||||
{ | |||||
remoteServers.Add($"{terminal}_{item.Host}"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
remoteServers.Add($"all_{item.Host}"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
remoteServers.Add($"all_{item.Host}"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
foreach (var item in remoteServers) | |||||
{ | |||||
try | |||||
{ | |||||
string ip_port = item.Split('_')[1]; | |||||
IChannel clientChannel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip_port.Split(':')[0]), int.Parse(ip_port.Split(':')[1]))); | |||||
channeldic.Add(item, clientChannel); | |||||
if (clientChannel.Open) | |||||
{ | |||||
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"该终端{item.Replace("_", "已连接上该服务器")}"); | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError($"初始化配置链接远程服务端{item},链接异常:{ex}"); | |||||
} | |||||
} | |||||
await Task.CompletedTask; | |||||
} | |||||
/// <summary> | |||||
/// 动态删除远程服务器 | |||||
/// </summary> | |||||
/// <param name="lastRemoteServers"></param> | |||||
private void DelRemoteServsers(List<string> lastRemoteServers) | |||||
{ | |||||
var delChannels = channeldic.Keys.Except(lastRemoteServers).ToList(); | |||||
foreach (var item in delChannels) | |||||
{ | |||||
channeldic[item].CloseAsync(); | |||||
channeldic.Remove(item); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// 动态添加远程服务器 | |||||
/// </summary> | |||||
/// <param name="bootstrap"></param> | |||||
/// <param name="lastRemoteServers"></param> | |||||
private void AddRemoteServsers(Bootstrap bootstrap, List<string> lastRemoteServers) | |||||
{ | |||||
var addChannels = lastRemoteServers.Except(channeldic.Keys).ToList(); | |||||
foreach (var item in addChannels) | |||||
{ | |||||
try | |||||
{ | |||||
var ip_port = item.Split('_')[1]; | |||||
IChannel clientChannel = bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip_port.Split(':')[0]), int.Parse(ip_port.Split(':')[1]))).Result; | |||||
channeldic.Add(item, clientChannel); | |||||
if (clientChannel.Open) { | |||||
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"该终端{item.Replace("_", "已连接上该服务器")}"); | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError($"变更配置后链接远程服务端{item},重连异常:{ex}"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,28 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Client | |||||
{ | |||||
public class DeviceConfig | |||||
{ | |||||
public DeviceConfig(string terminalPhoneNo, string tcpHost,int tcpPort) | |||||
{ | |||||
TerminalPhoneNo = terminalPhoneNo; | |||||
TcpHost = tcpHost; | |||||
TcpPort = tcpPort; | |||||
MsgSNDistributed = new JT808ClientMsgSNDistributedImpl(); | |||||
} | |||||
public string TerminalPhoneNo { get; private set; } | |||||
public string TcpHost { get; private set; } | |||||
public int TcpPort { get; private set; } | |||||
/// <summary> | |||||
/// 心跳时间(秒) | |||||
/// </summary> | |||||
public int Heartbeat { get; set; } = 30; | |||||
public IJT808MsgSNDistributed MsgSNDistributed { get; } | |||||
} | |||||
} |
@@ -1,23 +0,0 @@ | |||||
| |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.Protocol; | |||||
using JT808.Gateway.Services; | |||||
namespace JT808.Gateway.Client | |||||
{ | |||||
public static class JT808ClientDotnettyExtensions | |||||
{ | |||||
public static IJT808Builder AddJT808Client(this IJT808Builder jT808Builder) | |||||
{ | |||||
jT808Builder.Services.AddSingleton<JT808ClientSendAtomicCounterService>(); | |||||
jT808Builder.Services.AddSingleton<JT808ClientReceiveAtomicCounterService>(); | |||||
jT808Builder.Services.AddSingleton<IJT808TcpClientFactory, JT808TcpClientFactory>(); | |||||
jT808Builder.Services.AddSingleton<JT808ClientReportService>(); | |||||
jT808Builder.Services.AddHostedService<JT808ClientReportHostedService>(); | |||||
return jT808Builder; | |||||
} | |||||
} | |||||
} |
@@ -1,16 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Interfaces; | |||||
using System.Threading; | |||||
namespace JT808.Gateway.Client | |||||
{ | |||||
internal class JT808ClientMsgSNDistributedImpl : IJT808MsgSNDistributed | |||||
{ | |||||
int _counter = 0; | |||||
public ushort Increment() | |||||
{ | |||||
return (ushort)Interlocked.Increment(ref _counter); | |||||
} | |||||
} | |||||
} |
@@ -1,112 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Runtime.InteropServices; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System.Net; | |||||
using JT808.Protocol; | |||||
using JT808.Gateway.Services; | |||||
using JT808.Gateway.Codecs; | |||||
using JT808.Gateway.Handlers; | |||||
using JT808.Gateway.Metadata; | |||||
namespace JT808.Gateway.Client | |||||
{ | |||||
public sealed class JT808TcpClient : IDisposable | |||||
{ | |||||
private MultithreadEventLoopGroup group; | |||||
private IChannel clientChannel; | |||||
private bool disposed = false; | |||||
public DeviceConfig DeviceConfig { get; private set; } | |||||
public ILoggerFactory LoggerFactory { get; private set; } | |||||
public JT808TcpClient(DeviceConfig deviceConfig, IServiceProvider serviceProvider) | |||||
{ | |||||
DeviceConfig = deviceConfig; | |||||
LoggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); | |||||
JT808ClientSendAtomicCounterService jT808SendAtomicCounterService = serviceProvider.GetRequiredService<JT808ClientSendAtomicCounterService>(); | |||||
JT808ClientReceiveAtomicCounterService jT808ReceiveAtomicCounterService = serviceProvider.GetRequiredService<JT808ClientReceiveAtomicCounterService>(); | |||||
IJT808Config jT808Config = serviceProvider.GetRequiredService<IJT808Config>(); | |||||
group = new MultithreadEventLoopGroup(1); | |||||
Bootstrap bootstrap = new Bootstrap(); | |||||
bootstrap.Group(group); | |||||
bootstrap.Channel<TcpSocketChannel>(); | |||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | |||||
{ | |||||
bootstrap.Option(ChannelOption.SoReuseport, true); | |||||
} | |||||
bootstrap | |||||
.Option(ChannelOption.SoBacklog, 8192) | |||||
.Handler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
channel.Pipeline.AddLast("jt808TcpBuffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("systemIdleState", new IdleStateHandler(60, deviceConfig.Heartbeat, 3600)); | |||||
channel.Pipeline.AddLast("jt808TcpDecode", new JT808ClientTcpDecoder()); | |||||
channel.Pipeline.AddLast("jt808TcpEncode", new JT808ClientTcpEncoder(jT808Config,jT808SendAtomicCounterService, LoggerFactory)); | |||||
channel.Pipeline.AddLast("jt808TcpClientConnection", new JT808TcpClientConnectionHandler(this)); | |||||
channel.Pipeline.AddLast("jt808TcpService", new JT808TcpClientHandler(jT808ReceiveAtomicCounterService,this)); | |||||
})); | |||||
clientChannel = bootstrap.ConnectAsync(IPAddress.Parse(DeviceConfig.TcpHost), DeviceConfig.TcpPort).Result; | |||||
} | |||||
public async void Send(JT808ClientRequest request) | |||||
{ | |||||
if (disposed) return; | |||||
if (clientChannel == null) throw new NullReferenceException("Channel is empty."); | |||||
if (request == null) throw new ArgumentNullException("JT808ClientRequest Parameter is empty."); | |||||
if (clientChannel.Active && clientChannel.Open) | |||||
{ | |||||
await clientChannel.WriteAndFlushAsync(request); | |||||
} | |||||
} | |||||
public bool IsOpen | |||||
{ | |||||
get | |||||
{ | |||||
if (clientChannel == null) return false; | |||||
return clientChannel.Active && clientChannel.Open; | |||||
} | |||||
} | |||||
private void Dispose(bool disposing) | |||||
{ | |||||
if (disposed) | |||||
{ | |||||
return; | |||||
} | |||||
if (disposing) | |||||
{ | |||||
// 清理托管资源 | |||||
group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)); | |||||
} | |||||
disposed = true; | |||||
} | |||||
~JT808TcpClient() | |||||
{ | |||||
//必须为false | |||||
//这表明,隐式清理时,只要处理非托管资源就可以了。 | |||||
Dispose(false); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
//必须为true | |||||
Dispose(true); | |||||
//通知垃圾回收机制不再调用终结器(析构器) | |||||
GC.SuppressFinalize(this); | |||||
} | |||||
} | |||||
} |
@@ -1,103 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.MessageBody; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.Protocol.Enums; | |||||
using JT808.Protocol.Extensions; | |||||
using JT808.Gateway.Metadata; | |||||
namespace JT808.Gateway.Client | |||||
{ | |||||
public static class JT808TcpClientExtensions | |||||
{ | |||||
public static void Send(this JT808TcpClient client, JT808Header header, JT808Bodies bodies, int minBufferSize = 1024) | |||||
{ | |||||
JT808Package package = new JT808Package(); | |||||
package.Header = header; | |||||
package.Bodies = bodies; | |||||
package.Header.TerminalPhoneNo = client.DeviceConfig.TerminalPhoneNo; | |||||
package.Header.MsgNum = client.DeviceConfig.MsgSNDistributed.Increment(); | |||||
JT808ClientRequest request = new JT808ClientRequest(package, minBufferSize); | |||||
client.Send(request); | |||||
} | |||||
/// <summary> | |||||
/// 终端通用应答 | |||||
/// </summary> | |||||
/// <param name="client"></param> | |||||
/// <param name="bodies"></param> | |||||
/// <param name="minBufferSize"></param> | |||||
public static void Send(this JT808TcpClient client, JT808_0x0001 bodies, int minBufferSize = 100) | |||||
{ | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = JT808MsgId.终端通用应答.ToUInt16Value(); | |||||
client.Send(header, bodies, minBufferSize); | |||||
} | |||||
/// <summary> | |||||
/// 终端心跳 | |||||
/// </summary> | |||||
/// <param name="client"></param> | |||||
/// <param name="bodies"></param> | |||||
/// <param name="minBufferSize"></param> | |||||
public static void Send(this JT808TcpClient client, JT808_0x0002 bodies, int minBufferSize = 100) | |||||
{ | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = JT808MsgId.终端心跳.ToUInt16Value(); | |||||
client.Send(header, bodies, minBufferSize); | |||||
} | |||||
/// <summary> | |||||
/// 终端注销 | |||||
/// </summary> | |||||
/// <param name="client"></param> | |||||
/// <param name="bodies"></param> | |||||
/// <param name="minBufferSize"></param> | |||||
public static void Send(this JT808TcpClient client, JT808_0x0003 bodies, int minBufferSize = 100) | |||||
{ | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = JT808MsgId.终端注销.ToUInt16Value(); | |||||
client.Send(header, bodies, minBufferSize); | |||||
} | |||||
/// <summary> | |||||
/// 终端鉴权 | |||||
/// </summary> | |||||
/// <param name="client"></param> | |||||
/// <param name="bodies"></param> | |||||
/// <param name="minBufferSize"></param> | |||||
public static void Send(this JT808TcpClient client, JT808_0x0102 bodies, int minBufferSize = 100) | |||||
{ | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = JT808MsgId.终端鉴权.ToUInt16Value(); | |||||
client.Send(header, bodies, minBufferSize); | |||||
} | |||||
/// <summary> | |||||
/// 终端注册 | |||||
/// </summary> | |||||
/// <param name="client"></param> | |||||
/// <param name="bodies"></param> | |||||
/// <param name="minBufferSize"></param> | |||||
public static void Send(this JT808TcpClient client, JT808_0x0100 bodies, int minBufferSize = 100) | |||||
{ | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = JT808MsgId.终端注册.ToUInt16Value(); | |||||
client.Send(header, bodies, minBufferSize); | |||||
} | |||||
/// <summary> | |||||
/// 位置信息汇报 | |||||
/// </summary> | |||||
/// <param name="client"></param> | |||||
/// <param name="bodies"></param> | |||||
/// <param name="minBufferSize"></param> | |||||
public static void Send(this JT808TcpClient client, JT808_0x0200 bodies, int minBufferSize = 200) | |||||
{ | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = JT808MsgId.位置信息汇报.ToUInt16Value(); | |||||
client.Send(header, bodies, minBufferSize); | |||||
} | |||||
} | |||||
} |
@@ -1,62 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
namespace JT808.Gateway.Client | |||||
{ | |||||
public interface IJT808TcpClientFactory : IDisposable | |||||
{ | |||||
JT808TcpClient Create(DeviceConfig deviceConfig); | |||||
List<JT808TcpClient> GetAll(); | |||||
} | |||||
public class JT808TcpClientFactory: IJT808TcpClientFactory | |||||
{ | |||||
private readonly ConcurrentDictionary<string, JT808TcpClient> dict; | |||||
private readonly IServiceProvider serviceProvider; | |||||
public JT808TcpClientFactory(IServiceProvider serviceProvider) | |||||
{ | |||||
dict = new ConcurrentDictionary<string, JT808TcpClient>(StringComparer.OrdinalIgnoreCase); | |||||
this.serviceProvider = serviceProvider; | |||||
} | |||||
public JT808TcpClient Create(DeviceConfig deviceConfig) | |||||
{ | |||||
if(dict.TryGetValue(deviceConfig.TerminalPhoneNo,out var client)) | |||||
{ | |||||
return client; | |||||
} | |||||
else | |||||
{ | |||||
JT808TcpClient jT808TcpClient = new JT808TcpClient(deviceConfig, serviceProvider); | |||||
dict.TryAdd(deviceConfig.TerminalPhoneNo, jT808TcpClient); | |||||
return jT808TcpClient; | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
foreach(var client in dict) | |||||
{ | |||||
try | |||||
{ | |||||
client.Value.Dispose(); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
} | |||||
} | |||||
public List<JT808TcpClient> GetAll() | |||||
{ | |||||
return dict.Values.ToList(); | |||||
} | |||||
} | |||||
} |
@@ -1,20 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using System.Collections.Generic; | |||||
using JT808.Protocol; | |||||
using DotNetty.Transport.Channels; | |||||
namespace JT808.Gateway.Codecs | |||||
{ | |||||
public class JT808ClientTcpDecoder : ByteToMessageDecoder | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) | |||||
{ | |||||
byte[] buffer = new byte[input.Capacity + 2]; | |||||
input.ReadBytes(buffer, 1, input.Capacity); | |||||
buffer[0] = JT808Package.BeginFlag; | |||||
buffer[input.Capacity + 1] = JT808Package.EndFlag; | |||||
output.Add(buffer); | |||||
} | |||||
} | |||||
} |
@@ -1,52 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using JT808.Protocol; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT808.Gateway.Metadata; | |||||
using JT808.Gateway.Services; | |||||
namespace JT808.Gateway.Codecs | |||||
{ | |||||
public class JT808ClientTcpEncoder : MessageToByteEncoder<JT808ClientRequest> | |||||
{ | |||||
private readonly ILogger<JT808ClientTcpEncoder> logger; | |||||
private readonly JT808ClientSendAtomicCounterService jT808SendAtomicCounterService; | |||||
private readonly JT808Serializer JT808Serializer; | |||||
public JT808ClientTcpEncoder( | |||||
IJT808Config jT808Config, | |||||
JT808ClientSendAtomicCounterService jT808SendAtomicCounterService,ILoggerFactory loggerFactory) | |||||
{ | |||||
logger=loggerFactory.CreateLogger<JT808ClientTcpEncoder>(); | |||||
this.jT808SendAtomicCounterService = jT808SendAtomicCounterService; | |||||
JT808Serializer = jT808Config.GetSerializer(); | |||||
} | |||||
protected override void Encode(IChannelHandlerContext context, JT808ClientRequest message, IByteBuffer output) | |||||
{ | |||||
if (message.Package != null) | |||||
{ | |||||
try | |||||
{ | |||||
var sendData = JT808Serializer.Serialize(message.Package, message.MinBufferSize); | |||||
output.WriteBytes(sendData); | |||||
jT808SendAtomicCounterService.MsgSuccessIncrement(); | |||||
} | |||||
catch (JT808.Protocol.Exceptions.JT808Exception ex) | |||||
{ | |||||
logger.LogError(ex, context.Channel.Id.AsShortText()); | |||||
} | |||||
catch (System.Exception ex) | |||||
{ | |||||
logger.LogError(ex, context.Channel.Id.AsShortText()); | |||||
} | |||||
} | |||||
else if (message.HexData != null) | |||||
{ | |||||
output.WriteBytes(message.HexData); | |||||
jT808SendAtomicCounterService.MsgSuccessIncrement(); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,32 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using System.Collections.Generic; | |||||
using JT808.Protocol; | |||||
using DotNetty.Transport.Channels; | |||||
namespace JT808.Gateway.Codecs | |||||
{ | |||||
public class JT808TcpDecoder : ByteToMessageDecoder | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) | |||||
{ | |||||
//过滤掉不是808标准包 | |||||
//不包括头尾标识 | |||||
//(消息 ID )2+(消息体属性)2+(终端手机号)6+(消息流水号)2+(检验码 )1 | |||||
if (input.Capacity < 12) | |||||
{ | |||||
byte[] buffer = new byte[input.Capacity]; | |||||
input.ReadBytes(buffer, 0, input.Capacity); | |||||
return; | |||||
} | |||||
else | |||||
{ | |||||
byte[] buffer = new byte[input.Capacity + 2]; | |||||
input.ReadBytes(buffer, 1, input.Capacity); | |||||
buffer[0] = JT808Package.BeginFlag; | |||||
buffer[input.Capacity + 1] = JT808Package.EndFlag; | |||||
output.Add(buffer); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,52 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using JT808.Protocol; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Gateway.Interfaces; | |||||
namespace JT808.Gateway.Codecs | |||||
{ | |||||
/// <summary> | |||||
/// tcp统一下发出口 | |||||
/// </summary> | |||||
public class JT808TcpEncoder : MessageToByteEncoder<IJT808Reply> | |||||
{ | |||||
private readonly ILogger<JT808TcpEncoder> logger; | |||||
private readonly JT808Serializer JT808Serializer; | |||||
public JT808TcpEncoder( | |||||
IJT808Config jT808Config, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
logger = loggerFactory.CreateLogger<JT808TcpEncoder>(); | |||||
this.JT808Serializer = jT808Config.GetSerializer(); | |||||
} | |||||
protected override void Encode(IChannelHandlerContext context, IJT808Reply message, IByteBuffer output) | |||||
{ | |||||
if (message.Package != null) | |||||
{ | |||||
try | |||||
{ | |||||
var sendData = JT808Serializer.Serialize(message.Package, message.MinBufferSize); | |||||
output.WriteBytes(Unpooled.WrappedBuffer(sendData)); | |||||
} | |||||
catch (JT808.Protocol.Exceptions.JT808Exception ex) | |||||
{ | |||||
logger.LogError(ex, context.Channel.Id.AsShortText()); | |||||
} | |||||
catch (System.Exception ex) | |||||
{ | |||||
logger.LogError(ex, context.Channel.Id.AsShortText()); | |||||
} | |||||
} | |||||
else if (message.HexData != null) | |||||
{ | |||||
output.WriteBytes(Unpooled.WrappedBuffer(message.HexData)); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,33 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Channels; | |||||
using System.Collections.Generic; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT808.Gateway.Metadata; | |||||
namespace JT808.Gateway.Codecs | |||||
{ | |||||
public class JT808UdpDecoder : MessageToMessageDecoder<DatagramPacket> | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List<object> output) | |||||
{ | |||||
if (!message.Content.IsReadable()) return; | |||||
IByteBuffer byteBuffer = message.Content; | |||||
//过滤掉非808标准包 | |||||
//不包括头尾标识 | |||||
//(消息 ID )2+(消息体属性)2+(终端手机号)6+(消息流水号)2+(检验码 )1 | |||||
if (byteBuffer.ReadableBytes < 12) | |||||
{ | |||||
byte[] buffer = new byte[byteBuffer.ReadableBytes]; | |||||
byteBuffer.ReadBytes(buffer, 0, byteBuffer.ReadableBytes); | |||||
return; | |||||
} | |||||
else | |||||
{ | |||||
byte[] buffer = new byte[byteBuffer.ReadableBytes]; | |||||
byteBuffer.ReadBytes(buffer); | |||||
output.Add(new JT808UdpPackage(buffer, message.Sender)); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,41 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Configurations | |||||
{ | |||||
public class JT808Configuration | |||||
{ | |||||
public int TcpPort { get; set; } = 808; | |||||
public int UdpPort { get; set; } = 808; | |||||
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; } = 3600; | |||||
public int WriterIdleTimeSeconds { get; set; } = 3600; | |||||
public int AllIdleTimeSeconds { get; set; } = 3600; | |||||
/// <summary> | |||||
/// 转发远程地址 (可选项)知道转发的地址有利于提升性能 | |||||
/// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: | |||||
// 1.消息的序列化 | |||||
// 2.消息的下发 | |||||
// 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle | |||||
// 就跟神兽貔貅一样。。。 | |||||
/// </summary> | |||||
public List<string> ForwardingRemoteIPAddress { get; set; } | |||||
} | |||||
} |
@@ -1,29 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Dtos | |||||
{ | |||||
public class JT808ResultDto<T> | |||||
{ | |||||
public JT808ResultDto() | |||||
{ | |||||
Code = JT808ResultCode.Ok; | |||||
} | |||||
public string Message { get; set; } | |||||
public int Code { get; set; } | |||||
public T Data { get; set; } | |||||
} | |||||
public class JT808ResultCode | |||||
{ | |||||
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; | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using System; | |||||
namespace JT808.Gateway.Dtos | |||||
{ | |||||
public class JT808TcpSessionInfoDto | |||||
{ | |||||
/// <summary> | |||||
/// 最后上线时间 | |||||
/// </summary> | |||||
public DateTime LastActiveTime { get; set; } | |||||
/// <summary> | |||||
/// 上线时间 | |||||
/// </summary> | |||||
public DateTime StartTime { get; set; } | |||||
/// <summary> | |||||
/// 终端手机号 | |||||
/// </summary> | |||||
public string TerminalPhoneNo { get; set; } | |||||
/// <summary> | |||||
/// 远程ip地址 | |||||
/// </summary> | |||||
public string RemoteAddressIP { get; set; } | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using System; | |||||
namespace JT808.Gateway.Dtos | |||||
{ | |||||
public class JT808UdpSessionInfoDto | |||||
{ | |||||
/// <summary> | |||||
/// 最后上线时间 | |||||
/// </summary> | |||||
public DateTime LastActiveTime { get; set; } | |||||
/// <summary> | |||||
/// 上线时间 | |||||
/// </summary> | |||||
public DateTime StartTime { get; set; } | |||||
/// <summary> | |||||
/// 终端手机号 | |||||
/// </summary> | |||||
public string TerminalPhoneNo { get; set; } | |||||
/// <summary> | |||||
/// 远程ip地址 | |||||
/// </summary> | |||||
public string RemoteAddressIP { get; set; } | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Enums | |||||
{ | |||||
/// <summary> | |||||
/// 传输协议类型 | |||||
/// </summary> | |||||
public enum JT808TransportProtocolType | |||||
{ | |||||
tcp=1, | |||||
udp = 2 | |||||
} | |||||
} |
@@ -1,96 +0,0 @@ | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.Gateway.Client; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808客户端连接通道处理程序 | |||||
/// </summary> | |||||
public class JT808TcpClientConnectionHandler : ChannelHandlerAdapter | |||||
{ | |||||
private readonly ILogger<JT808TcpClientConnectionHandler> logger; | |||||
private readonly JT808TcpClient jT808TcpClient; | |||||
public JT808TcpClientConnectionHandler( | |||||
JT808TcpClient jT808TcpClient) | |||||
{ | |||||
logger = jT808TcpClient.LoggerFactory.CreateLogger<JT808TcpClientConnectionHandler>(); | |||||
this.jT808TcpClient = jT808TcpClient; | |||||
} | |||||
/// <summary> | |||||
/// 通道激活 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
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); | |||||
} | |||||
/// <summary> | |||||
/// 设备主动断开 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
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."); | |||||
base.ChannelInactive(context); | |||||
} | |||||
/// <summary> | |||||
/// 服务器主动断开 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
/// <returns></returns> | |||||
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."); | |||||
return base.CloseAsync(context); | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); | |||||
/// <summary> | |||||
/// 超时策略 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
/// <param name="evt"></param> | |||||
public override void UserEventTriggered(IChannelHandlerContext context, object evt) | |||||
{ | |||||
IdleStateEvent idleStateEvent = evt as IdleStateEvent; | |||||
if (idleStateEvent != null) | |||||
{ | |||||
if(idleStateEvent.State== IdleState.WriterIdle) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
logger.LogInformation($"{idleStateEvent.State.ToString()}>>>{channelId}"); | |||||
jT808TcpClient.Send(new JT808_0x0002()); | |||||
} | |||||
} | |||||
base.UserEventTriggered(context, evt); | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
logger.LogError(exception,$"{channelId} {exception.Message}" ); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,31 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using JT808.Gateway.Services; | |||||
using JT808.Gateway.Client; | |||||
namespace JT808.Gateway.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808客户端处理程序 | |||||
/// </summary> | |||||
internal class JT808TcpClientHandler : SimpleChannelInboundHandler<byte[]> | |||||
{ | |||||
private readonly ILogger<JT808TcpClientHandler> logger; | |||||
private readonly JT808ClientReceiveAtomicCounterService jT808ReceiveAtomicCounterService; | |||||
public JT808TcpClientHandler(JT808ClientReceiveAtomicCounterService jT808ReceiveAtomicCounterService,JT808TcpClient jT808TcpClient) | |||||
{ | |||||
logger = jT808TcpClient.LoggerFactory.CreateLogger<JT808TcpClientHandler>(); | |||||
this.jT808ReceiveAtomicCounterService= jT808ReceiveAtomicCounterService; | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg) | |||||
{ | |||||
if(logger.IsEnabled(LogLevel.Trace)) | |||||
logger.LogTrace("accept msg<<<" + ByteBufferUtil.HexDump(msg)); | |||||
jT808ReceiveAtomicCounterService.MsgSuccessIncrement(); | |||||
} | |||||
} | |||||
} |
@@ -1,104 +0,0 @@ | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.Gateway.Session; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.Gateway.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808服务通道处理程序 | |||||
/// </summary> | |||||
internal class JT808TcpConnectionHandler : ChannelHandlerAdapter | |||||
{ | |||||
private readonly ILogger<JT808TcpConnectionHandler> logger; | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
public JT808TcpConnectionHandler( | |||||
JT808SessionManager jT808SessionManager, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
logger = loggerFactory.CreateLogger<JT808TcpConnectionHandler>(); | |||||
} | |||||
/// <summary> | |||||
/// 通道激活 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
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); | |||||
} | |||||
/// <summary> | |||||
/// 设备主动断开 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
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."); | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
base.ChannelInactive(context); | |||||
} | |||||
/// <summary> | |||||
/// 服务器主动断开 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
/// <returns></returns> | |||||
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."); | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
return base.CloseAsync(context); | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); | |||||
/// <summary> | |||||
/// 超时策略 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
/// <param name="evt"></param> | |||||
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}"); | |||||
// 由于808是设备发心跳,如果很久没有上报数据,那么就由服务器主动关闭连接。 | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | |||||
} | |||||
// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: | |||||
// 1.消息的序列化 | |||||
// 2.消息的下发 | |||||
// 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle | |||||
// 就跟神兽貔貅一样。。。 | |||||
} | |||||
base.UserEventTriggered(context, evt); | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
logger.LogError(exception,$"{channelId} {exception.Message}" ); | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,77 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.Protocol; | |||||
using System; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT808.Protocol.Exceptions; | |||||
using JT808.Gateway.Session; | |||||
using JT808.Gateway.Services; | |||||
using JT808.Gateway.PubSub; | |||||
using JT808.Gateway.Enums; | |||||
namespace JT808.Gateway.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808服务端处理程序 | |||||
/// </summary> | |||||
internal class JT808TcpServerHandler : SimpleChannelInboundHandler<byte[]> | |||||
{ | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
private readonly JT808AtomicCounterService jT808AtomicCounterService; | |||||
private readonly ILogger<JT808TcpServerHandler> logger; | |||||
private readonly JT808Serializer JT808Serializer; | |||||
private readonly IJT808MsgProducer JT808MsgProducer; | |||||
public JT808TcpServerHandler( | |||||
IJT808MsgProducer jT808MsgProducer, | |||||
IJT808Config jT808Config, | |||||
ILoggerFactory loggerFactory, | |||||
JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory, | |||||
JT808SessionManager jT808SessionManager) | |||||
{ | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
this.JT808MsgProducer = jT808MsgProducer; | |||||
this.jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.tcp); | |||||
this.JT808Serializer = jT808Config.GetSerializer(); | |||||
logger = loggerFactory.CreateLogger<JT808TcpServerHandler>(); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg) | |||||
{ | |||||
try | |||||
{ | |||||
//解析到头部,然后根据具体的消息Id通过队列去进行消费 | |||||
//要是一定要解析到数据体可以在JT808MsgIdHandlerBase类中根据具体的消息, | |||||
//解析具体的消息体,具体调用JT808Serializer.Deserialize<T> | |||||
JT808HeaderPackage jT808HeaderPackage = JT808Serializer.Deserialize<JT808HeaderPackage>(msg); | |||||
if (logger.IsEnabled(LogLevel.Trace)) | |||||
{ | |||||
logger.LogTrace($"accept package success count=>{jT808AtomicCounterService.MsgSuccessCount.ToString()},accept msg=>{ByteBufferUtil.HexDump(msg)}"); | |||||
} | |||||
jT808AtomicCounterService.MsgSuccessIncrement(); | |||||
jT808SessionManager.TryAdd(jT808HeaderPackage.Header.TerminalPhoneNo,ctx.Channel); | |||||
JT808MsgProducer.ProduceAsync(jT808HeaderPackage.Header.TerminalPhoneNo, msg); | |||||
} | |||||
catch (JT808Exception ex) | |||||
{ | |||||
jT808AtomicCounterService.MsgFailIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError(ex,$"accept package fail count=>{jT808AtomicCounterService.MsgFailCount.ToString()},accept msg=>{ByteBufferUtil.HexDump(msg)}"); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
jT808AtomicCounterService.MsgFailIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError(ex, $"accept package fail count=>{jT808AtomicCounterService.MsgFailCount.ToString()},accept msg=>{ByteBufferUtil.HexDump(msg)}"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,79 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.Protocol; | |||||
using System; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT808.Gateway.Services; | |||||
using JT808.Gateway.Session; | |||||
using JT808.Gateway.PubSub; | |||||
using JT808.Gateway.Metadata; | |||||
using JT808.Gateway.Enums; | |||||
namespace JT808.Gateway.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808 Udp服务端处理程序 | |||||
/// </summary> | |||||
internal class JT808UdpServerHandler : SimpleChannelInboundHandler<JT808UdpPackage> | |||||
{ | |||||
private readonly JT808AtomicCounterService jT808AtomicCounterService; | |||||
private readonly ILogger<JT808UdpServerHandler> logger; | |||||
private readonly JT808SessionManager jT808UdpSessionManager; | |||||
private readonly JT808Serializer JT808Serializer; | |||||
private readonly IJT808MsgProducer JT808MsgProducer; | |||||
public JT808UdpServerHandler( | |||||
IJT808MsgProducer jT808MsgProducer, | |||||
IJT808Config jT808Config, | |||||
ILoggerFactory loggerFactory, | |||||
JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory, | |||||
JT808SessionManager jT808UdpSessionManager) | |||||
{ | |||||
this.JT808MsgProducer = jT808MsgProducer; | |||||
this.jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.udp); | |||||
this.jT808UdpSessionManager = jT808UdpSessionManager; | |||||
logger = loggerFactory.CreateLogger<JT808UdpServerHandler>(); | |||||
JT808Serializer = jT808Config.GetSerializer(); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, JT808UdpPackage msg) | |||||
{ | |||||
try | |||||
{ | |||||
//解析到头部,然后根据具体的消息Id通过队列去进行消费 | |||||
//要是一定要解析到数据体可以在JT808MsgIdHandlerBase类中根据具体的消息, | |||||
//解析具体的消息体,具体调用JT808Serializer.Deserialize<T> | |||||
JT808HeaderPackage jT808HeaderPackage = JT808Serializer.Deserialize<JT808HeaderPackage>(msg.Buffer); | |||||
if (logger.IsEnabled(LogLevel.Trace)) | |||||
{ | |||||
logger.LogTrace($"accept package success count=>{jT808AtomicCounterService.MsgFailCount.ToString()},accept msg=>{ByteBufferUtil.HexDump(msg.Buffer)}"); | |||||
} | |||||
jT808AtomicCounterService.MsgSuccessIncrement(); | |||||
jT808UdpSessionManager.TryAdd(ctx.Channel, msg.Sender, jT808HeaderPackage.Header.TerminalPhoneNo); | |||||
JT808MsgProducer.ProduceAsync(jT808HeaderPackage.Header.TerminalPhoneNo, msg.Buffer); | |||||
} | |||||
catch (JT808.Protocol.Exceptions.JT808Exception ex) | |||||
{ | |||||
jT808AtomicCounterService.MsgFailIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError(ex, $"accept package fail count=>{jT808AtomicCounterService.MsgFailCount.ToString()},accept msg=>{ByteBufferUtil.HexDump(msg.Buffer)}"); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
jT808AtomicCounterService.MsgFailIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError(ex, $"accept package fail count=>{jT808AtomicCounterService.MsgFailCount.ToString()},accept msg=>{ByteBufferUtil.HexDump(msg.Buffer)}"); | |||||
} | |||||
} | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); | |||||
} | |||||
} |
@@ -1,14 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway | |||||
{ | |||||
public interface IJT808ClientBuilder | |||||
{ | |||||
IJT808Builder JT808Builder { get; } | |||||
IJT808Builder Builder(); | |||||
} | |||||
} |
@@ -1,14 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway | |||||
{ | |||||
public interface IJT808GatewayBuilder | |||||
{ | |||||
IJT808Builder JT808Builder { get; } | |||||
IJT808Builder Builder(); | |||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT808.Gateway.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Impls | |||||
{ | |||||
class JT808DatagramPacketImpl : IJT808DatagramPacket | |||||
{ | |||||
public DatagramPacket Create(byte[] message, EndPoint recipient) | |||||
{ | |||||
return new DatagramPacket(Unpooled.WrappedBuffer(message), recipient); | |||||
} | |||||
} | |||||
} |
@@ -1,25 +0,0 @@ | |||||
using JT808.Gateway; | |||||
using JT808.Protocol; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Gateway.Impls | |||||
{ | |||||
internal class JT808GatewayBuilderDefault : IJT808GatewayBuilder | |||||
{ | |||||
public IJT808Builder JT808Builder { get; } | |||||
public JT808GatewayBuilderDefault(IJT808Builder builder) | |||||
{ | |||||
JT808Builder = builder; | |||||
} | |||||
public IJT808Builder Builder() | |||||
{ | |||||
return JT808Builder; | |||||
} | |||||
} | |||||
} |