commit 59e216ebfdcde1787cfc68d323b7df55273cf38d Author: smallchi <564952747@qq.com> Date: Mon Jul 22 13:46:18 2019 +0800 v2.1.2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c9e495 --- /dev/null +++ b/.gitignore @@ -0,0 +1,333 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ +*.json +/nupkgs +/src/JT808.DotNetty.Admin/tools/protoc-gen-grpc-web-1.0.3-windows-x86_64.exe diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..67b52f7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 SmallChi(Koike) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1dded66 --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +# 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) + +[![MIT Licence](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE) + +## 新网关的优势: + +1. 跨平台 +2. 借助 .NET Core模块化的思想 +3. 单机同时一万辆车在线不是梦(真有一万辆车那都很吃香了<( ̄3 ̄)> <( ̄3 ̄)> <( ̄3 ̄)> ) +4. 简单易上手 + +## 设计模型 + +![design_model](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/design_model.png) + +## 基于Tcp的消息业务处理程序(JT808.DotNetty.Tcp) + +通过继承JT808.DotNetty.Core.Handlers.JT808MsgIdTcpHandlerBase去实现自定义的消息业务处理程序。 + +## 基于Udp的消息业务处理程序(JT808.DotNetty.Udp) + +通过继承JT808.DotNetty.Core.Handlers.JT808MsgIdUdpHandlerBase去实现自定义的消息业务处理程序。 + +## 基于WebApi的消息业务处理程序(JT808.DotNetty.WebApi) + +通过继承JT808.DotNetty.Core.Handlers.JT808MsgIdHttpHandlerBase去实现自定义的WebApi接口服务。 + +[WebApi公共接口服务](https://github.com/SmallChi/JT808DotNetty/blob/master/api/README.md) + +## 集成接口功能(JT808.DotNetty.Abstractions) + +|接口名称|接口说明|使用场景| +|:------:|:------|:------| +| IJT808SessionPublishing| 会话通知(在线/离线)| 有些超长待机的设备,不会实时保持连接,那么通过平台下发的命令是无法到达的,这时候就需要设备一上线,就即时通知服务去处理,然后在即时的下发消息到设备。| +| IJT808SourcePackageDispatcher| 原包分发器| 需要将源数据转给其他平台| +| IJT808UplinkPacket| 上行数据包处理接口| 平台需要查看网关的上行数据日志(可以配合InfluxDB使用)| +| IJT808DownlinkPacket| 下行数据包处理接口| 平台需要查看网关的下行数据日志(可以配合InfluxDB使用)| + +> 只要实现IJT808SessionPublishing接口的任意一款MQ都能实现该功能。 + +> 使用物联网卡通过udp下发指令时,存储的那个socket地址端口,有效期非常短,不速度快点下发,那个socket地址端口就可能映射到别的对应卡去了,所以此处采用跟随设备消息下发指令。 + +## NuGet安装 + +| Package Name | Version | Downloads | +| --------------------- | -------------------------------------------------- | --------------------------------------------------- | +| Install-Package JT808.DotNetty.Core | ![JT808](https://img.shields.io/nuget/v/JT808.DotNetty.Core.svg) | ![JT808](https://img.shields.io/nuget/dt/JT808.DotNetty.Core.svg) | +| Install-Package JT808.DotNetty.Abstractions | ![JT808](https://img.shields.io/nuget/v/JT808.DotNetty.Abstractions.svg) | ![JT808](https://img.shields.io/nuget/dt/JT808.DotNetty.Abstractions.svg) | +| Install-Package JT808.DotNetty.Tcp | ![JT808](https://img.shields.io/nuget/v/JT808.DotNetty.Tcp.svg) | ![JT808](https://img.shields.io/nuget/dt/JT808.DotNetty.Tcp.svg) | +| Install-Package JT808.DotNetty.Udp | ![JT808](https://img.shields.io/nuget/v/JT808.DotNetty.Udp.svg) | ![JT808](https://img.shields.io/nuget/dt/JT808.DotNetty.Udp.svg) | +| Install-Package JT808.DotNetty.WebApi | ![JT808](https://img.shields.io/nuget/v/JT808.DotNetty.WebApi.svg) | ![JT808](https://img.shields.io/nuget/dt/JT808.DotNetty.WebApi.svg) | +| Install-Package JT808.DotNetty.WebApiClientTool | ![JT808](https://img.shields.io/nuget/v/JT808.DotNetty.WebApiClientTool.svg) | ![JT808](https://img.shields.io/nuget/dt/JT808.DotNetty.WebApiClientTool.svg) | + +## 举个栗子1 + +``` demo1 +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) => + { + logging.AddConsole(); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration) + //自定义日志下发包 + .ReplaceDownlinkPacket() + //自定义会话通知(在线/离线)使用异步方式 + //.ReplaceSessionPublishing() + //自定义原包转发 使用异步方式 + //.ReplaceSourcePackageDispatcher + .AddJT808TcpNettyHost() + // 自定义Tcp消息处理业务 + .ReplaceMsgIdHandler() + .Builder() + .AddJT808UdpNettyHost() + // 自定义Udp消息处理业务 + .ReplaceMsgIdHandler() + .Builder() + .AddJT808WebApiNettyHost() + .Builder(); + //webapi客户端调用 + services.AddHttpApi().ConfigureHttpApiConfig((c, p) => + { + c.HttpHost = new Uri("http://localhost:828/jt808api/"); + c.FormatOptions.DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff"; + c.LoggerFactory = p.GetRequiredService(); + }); + var client = services.BuildServiceProvider().GetRequiredService(); + var result = client.GetTcpAtomicCounter().InvokeAsync().Result; + }); + + await serverHostBuilder.RunConsoleAsync(); +} +``` + +如图所示: +![demo1](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/demo1.png) + +## 举个栗子2 + +1.拉取JT808子模块 + +2.打开项目进行还原编译生成 + +3.进入JT808.DotNetty.SimpleServer项目下的Debug目录运行服务端 + +4.进入JT808.DotNetty.SimpleClient项目下的Debug目录运行客户端 + +如图所示: +![demo2](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/demo2.png) diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..7d8fa86 --- /dev/null +++ b/api/README.md @@ -0,0 +1,512 @@ +# JT808 WebApi服务 + +基地址:127.0.0.1:828/jt808api/ + +> 注意url格式 + +数据格式:只支持Json格式 + +默认端口:828 + +## 1.统一下发设备消息服务 + +[基于Tcp统一下发设备消息服务](#tcp_send) + +[基于Udp统一下发设备消息服务](#udp_send) + +## 2.管理会话服务 + +[基于Tcp管理会话服务](#tcp_session) + +[基于Udp管理会话服务](#udp_session) + +## 3.转发地址过滤服务 + +[基于Tcp转发地址过滤服务](#tcp_transmit) + +## 4.消息包计数服务(次日清零) + +[基于Tcp消息包计数服务](#tcp_counter) + +[基于Udp消息包计数服务](#udp_counter) + +## 5.流量统计服务(次日清零) + +[基于Tcp流量统计服务](#tcp_traffic) + +[基于Udp流量统计服务](#udp_traffic) + +## 6.系统性能数据采集服务 + +[获取当前系统进程使用率](#system_collect) + +## 接口请求对照表 + +### 基于Tcp接口请求 + +|请求Url|请求方式|说明| +|:------|:------|:------| +| 127.0.0.1:828/jt808api/Tcp/UnificationSend| POST| 基于Tcp统一下发设备消息服务| +| 127.0.0.1:828/jt808api/Tcp/Session/GetAll| GET| 基于Tcp管理会话服务-获取会话集合| +| 127.0.0.1:828/jt808api/Tcp/Session/RemoveByTerminalPhoneNo| POST| 基于Tcp管理会话服务-通过设备终端号移除对应会话| +| 127.0.0.1:828/jt808api/Tcp/Transmit/Add| POST| 基于Tcp转发地址过滤服务-添加转发过滤地址| +| 127.0.0.1:828/jt808api/Tcp/Transmit/Remove| POST| 基于Tcp转发地址过滤服务-删除转发过滤地址| +| 127.0.0.1:828/jt808api/Tcp/Transmit/GetAll| GET| 基于Tcp转发地址过滤服务-获取转发过滤地址信息集合| +| 127.0.0.1:828/jt808api/Tcp/GetAtomicCounter| GET| 基于Tcp消息包计数服务| +| 127.0.0.1:828/jt808api/Tcp/Traffic/Get| GET| 基于Tcp流量统计服务| + +### 基于Udp接口请求 + +|请求Url|请求方式|说明| +|:------|:------|:------| +| 127.0.0.1:828/jt808api/Udp/UnificationSend| POST| 基于Udp统一下发设备消息服务| +| 127.0.0.1:828/jt808api/Udp/Session/GetAll| GET| 基于Udp管理会话服务-获取会话集合| +| 127.0.0.1:828/jt808api/Udp/Session/RemoveByTerminalPhoneNo| POST| 基于Udp管理会话服务-通过设备终端号移除对应会话| +| 127.0.0.1:828/jt808api/Udp/GetAtomicCounter| GET| 基于Udp消息包计数服务| +| 127.0.0.1:828/jt808api/Udp/Traffic/Get| GET| 基于Udp流量统计服务| + +### 公共接口请求 + +|请求Url|请求方式|说明| +|:------|:------|:------| +| 127.0.0.1:828/jt808api/SystemCollect/Get| GET| 获取当前系统进程使用情况| + +### 统一对象返回 JT808ResultDto\ + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Message| string| 消息描述| +| Code| int| 状态码| +| Data| T(泛型)| 数据| + +返回Code[状态码]说明: + +|状态码|说明| +|:------:|:------:| +| 200 | 返回成功 | +| 201 | 内容为空 | +| 404 | 没有该服务 | +| 500 | 服务内部错误 | + +### 基于Tcp统一下发设备消息服务 + +请求地址:Tcp/UnificationSend + +请求方式:POST + +请求参数: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| TerminalPhoneNo| string| 设备终端号| +| Data| byte[]| JT808 byte[]数组| + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| bool| 是否成功| + +返回结果: + +``` result1 +{ + "Message":"", + "Code":200, + "Data":true +} +``` + +### 基于Udp统一下发设备消息服务 + +请求地址:Udp/UnificationSend + +请求方式:POST + +请求参数: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| TerminalPhoneNo| string| 设备终端号| +| Data| byte[]| JT808 byte[]数组| + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| bool| 是否成功| + +返回结果: + +``` result1 +{ + "Message":"", + "Code":200, + "Data":true +} +``` + +### 基于Tcp管理会话服务 + +#### 统一会话信息对象返回 JT808TcpSessionInfoDto + +|属性|数据类型|参数说明| +|------|------|------| +| LastActiveTime| DateTime| 最后上线时间| +| StartTime| DateTime| 上线时间| +| TerminalPhoneNo|string| 终端手机号| +| RemoteAddressIP| string| 远程ip地址| + +#### 1.获取会话集合 + +请求地址:Tcp/Session/GetAll + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| List\ | 实际会话信息集合 | + +返回结果: + +``` session1 +{ + "Message":"", + "Code":200, + "Data":[ + { + "LastActiveTime":"2018-11-27 20:00:00", + "StartTime":"2018-11-25 20:00:00", + "TerminalPhoneNo":"123456789012", + "RemoteAddressIP":"127.0.0.1:11808" + },{ + "LastActiveTime":"2018-11-27 20:00:00", + "StartTime":"2018-11-25 20:00:00", + "TerminalPhoneNo":"123456789013", + "RemoteAddressIP":"127.0.0.1:11808" + } + ] +} +``` + +#### 2.通过设备终端号移除对应会话 + +请求地址:Tcp/Session/RemoveByTerminalPhoneNo + +请求方式:POST + +请求参数: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| terminalPhoneNo| string| 设备终端号| + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| bool | 是否成功 + +返回结果: + +``` session3 +{ + "Message":"", + "Code":200, + "Data":true +} +``` + +### 基于Udp管理会话服务 + +#### 统一会话信息对象返回 JT808UdpSessionInfoDto + +|属性|数据类型|参数说明| +|------|------|------| +| LastActiveTime| DateTime| 最后上线时间| +| StartTime| DateTime| 上线时间| +| TerminalPhoneNo|string| 终端手机号| +| RemoteAddressIP| string| 远程ip地址| + +#### 1.获取会话集合 + +请求地址:Udp/Session/GetAll + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| List\ | 实际会话信息集合 | + +返回结果: + +``` session1 +{ + "Message":"", + "Code":200, + "Data":[ + { + "LastActiveTime":"2018-11-27 20:00:00", + "StartTime":"2018-11-25 20:00:00", + "TerminalPhoneNo":"123456789012", + "RemoteAddressIP":"127.0.0.1:11808" + },{ + "LastActiveTime":"2018-11-27 20:00:00", + "StartTime":"2018-11-25 20:00:00", + "TerminalPhoneNo":"123456789013", + "RemoteAddressIP":"127.0.0.1:11808" + } + ] +} +``` + +#### 2.通过设备终端号移除对应会话 + +请求地址:Udp/Session/RemoveByTerminalPhoneNo + +请求方式:POST + +请求参数: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| terminalPhoneNo| string| 设备终端号| + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| bool | 是否成功 + +返回结果: + +``` session3 +{ + "Message":"", + "Code":200, + "Data":true +} +``` + +### 基于Tcp转发地址过滤服务 + +#### 1.添加转发过滤地址 + +请求地址:Tcp/Transmit/Add + +请求方式:POST + +请求参数: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Host| string| ip地址| +| Port| int| 端口号| + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| bool | 是否成功 + +返回结果: + +``` tr1 +{ + "Message":"", + "Code":200, + "Data":true +} +``` + +#### 2.删除转发过滤地址(不能删除在网关服务器配置文件配的地址) + +请求地址:Tcp/Transmit/Remove + +请求方式:POST + +请求参数: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Host| string| ip地址| + +返回数据: + +|属性|数据类型|参数说明| +|:------:|:------:|:------| +| Data| bool | 是否成功| + +返回结果: + +``` tr2 +{ + "Message":"", + "Code":200, + "Data":true +} +``` + +#### 3.获取转发过滤地址信息集合 + +请求地址:Tcp/Transmit/GetAll + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| Data| List\ | 远程ip地址(不加端口号)| + +返回结果: + +``` tr3 +{ + "Message":"", + "Code":200, + "Data":[ + "127.0.0.1" + ] +} +``` + +### 基于Tcp消息包计数服务 + +请求地址:Tcp/GetAtomicCounter + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| MsgSuccessCount| long| 消息包成功数| +| MsgFailCount| long| 消息包失败数| + +返回结果: + +``` counter +{ + "Message":"", + "Code":200, + "Data":{ + "MsgSuccessCount":10000, + "MsgFailCount":0 + } +} +``` + +### 基于Udp消息包计数服务 + +请求地址:Udp/GetAtomicCounter + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| MsgSuccessCount| long| 消息包成功数| +| MsgFailCount| long| 消息包失败数| + +返回结果: + +``` counter +{ + "Message":"", + "Code":200, + "Data":{ + "MsgSuccessCount":1000, + "MsgFailCount":0 + } +} +``` + +### 基于Tcp流量统计服务 + +请求地址:Tcp/Traffic/Get + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| TotalReceiveSize| double| 总接收大小(单位KB)| +| TotalSendSize| double| 总发送大小(单位KB)| + +返回结果: + +``` traffic1 +{ + "Message":"", + "Code":200, + "Data":{ + "TotalReceiveSize":0.0478515625, + "TotalSendSize":0.01953125 + } +} +``` + +### 基于Udp流量统计服务 + +请求地址:Udp/Traffic/Get + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| TotalReceiveSize| double| 总接收大小(单位KB)| +| TotalSendSize| double| 总发送大小(单位KB)| + +返回结果: + +``` traffic2 +{ + "Message":"", + "Code":200, + "Data":{ + "TotalReceiveSize":0.0478515625, + "TotalSendSize":0.01953125 + } +} +``` + +### 系统性能数据采集服务 + +请求地址:SystemCollect/Get + +请求方式:GET + +返回数据: + +|属性|数据类型|参数说明| +|------|:------:|:------| +| ProcessId| int| 进程Id| +| WorkingSet64| double| 进程分配内存(单位MB)| +| PeakWorkingSet64| double| 进程分配内存峰值(单位MB)| +| PrivateMemorySize64| double| 进程分配私有内存(单位MB)| +| CPUTotalProcessorTime| TimeSpan|进程执行CPU总处理时间| + +返回结果: + +``` sc +{ + "Message":"", + "Code":200, + "Data":{ + "ProcessId":101412, + "WorkingSet64":73.0625, + "PeakWorkingSet64":73.0625, + "PrivateMemorySize64":134.6796875, + "CPUTotalProcessorTime":"00:00:14.5625000" + } +} +``` \ No newline at end of file diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..ae791e0 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,16 @@ +## 压力测试 + +[感谢泥水佬提供的压力测试工具](https://www.cnblogs.com/smark/p/4496660.html?utm_source=tuicool) + +| 操作系统 | 配置 | 使用 | +|:-------:|:-------:|:-------:| +| win server 2016 | 4c8g | 压力测试客户端 | +| centos7 | 4c8g | JT808服务端 | + +![performance_1000](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/performance_1000.png) + +![performance_2000](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/performance_2000.png) + +![performance_5000](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/performance_5000.png) + +![performance_10000](https://github.com/SmallChi/JT808DotNetty/blob/master/doc/img/performance_10000.png) diff --git a/doc/img/demo1.png b/doc/img/demo1.png new file mode 100644 index 0000000..da66b65 Binary files /dev/null and b/doc/img/demo1.png differ diff --git a/doc/img/demo2.png b/doc/img/demo2.png new file mode 100644 index 0000000..04883b4 Binary files /dev/null and b/doc/img/demo2.png differ diff --git a/doc/img/design_model.png b/doc/img/design_model.png new file mode 100644 index 0000000..297e9aa Binary files /dev/null and b/doc/img/design_model.png differ diff --git a/doc/img/performance_1000.png b/doc/img/performance_1000.png new file mode 100644 index 0000000..fb47436 Binary files /dev/null and b/doc/img/performance_1000.png differ diff --git a/doc/img/performance_10000.png b/doc/img/performance_10000.png new file mode 100644 index 0000000..16e9fba Binary files /dev/null and b/doc/img/performance_10000.png differ diff --git a/doc/img/performance_2000.png b/doc/img/performance_2000.png new file mode 100644 index 0000000..4fb945b Binary files /dev/null and b/doc/img/performance_2000.png differ diff --git a/doc/img/performance_5000.png b/doc/img/performance_5000.png new file mode 100644 index 0000000..cf97f7e Binary files /dev/null and b/doc/img/performance_5000.png differ diff --git a/simples/JT808.DotNetty.SimpleClient/JT808.DotNetty.SimpleClient.csproj b/simples/JT808.DotNetty.SimpleClient/JT808.DotNetty.SimpleClient.csproj new file mode 100644 index 0000000..cef4e89 --- /dev/null +++ b/simples/JT808.DotNetty.SimpleClient/JT808.DotNetty.SimpleClient.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp2.2 + 7.3 + + + + + + + + + + + + + diff --git a/simples/JT808.DotNetty.SimpleClient/Program.cs b/simples/JT808.DotNetty.SimpleClient/Program.cs new file mode 100644 index 0000000..2f549bb --- /dev/null +++ b/simples/JT808.DotNetty.SimpleClient/Program.cs @@ -0,0 +1,38 @@ +using JT808.DotNetty.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using JT808.Protocol.MessageBody; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.Extensions.Hosting; +using JT808.DotNetty.SimpleClient.Services; + +namespace JT808.DotNetty.SimpleClient +{ + class Program + { + static async Task Main(string[] args) + { + var serverHostBuilder = new HostBuilder() + .ConfigureLogging((context, logging) => + { + logging.AddConsole(); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddLogging(options => { + options.AddConsole(); + options.SetMinimumLevel(LogLevel.Trace); + }); + services.AddJT808Client(); + services.AddHostedService(); + }); + await serverHostBuilder.RunConsoleAsync(); + } + } +} diff --git a/simples/JT808.DotNetty.SimpleClient/Services/UpService.cs b/simples/JT808.DotNetty.SimpleClient/Services/UpService.cs new file mode 100644 index 0000000..f9251cd --- /dev/null +++ b/simples/JT808.DotNetty.SimpleClient/Services/UpService.cs @@ -0,0 +1,68 @@ +using JT808.DotNetty.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.DotNetty.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", 12808)); + //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.每30秒发一次 + 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(30000); + } + }); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} diff --git a/simples/JT808.DotNetty.SimpleServer/Handlers/JT808MsgIdTcpSimpleHandler.cs b/simples/JT808.DotNetty.SimpleServer/Handlers/JT808MsgIdTcpSimpleHandler.cs new file mode 100644 index 0000000..7489978 --- /dev/null +++ b/simples/JT808.DotNetty.SimpleServer/Handlers/JT808MsgIdTcpSimpleHandler.cs @@ -0,0 +1,59 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.SimpleServer.Handlers +{ + public class JT808MsgIdTcpSimpleHandler : JT808MsgIdTcpHandlerBase + { + public JT808MsgIdTcpSimpleHandler( + ILoggerFactory loggerFactory, + JT808TcpSessionManager sessionManager) : base(sessionManager) + { + logger = loggerFactory.CreateLogger(); + } + + private readonly ILogger logger; + + public override IJT808Reply Msg0x0200(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0200"); + return base.Msg0x0200(request); + } + + public override IJT808Reply Msg0x0001(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0001"); + return base.Msg0x0001(request); + } + + public override IJT808Reply Msg0x0002(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0002"); + return base.Msg0x0002(request); + } + + public override IJT808Reply Msg0x0003(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0003"); + return base.Msg0x0003(request); + } + + public override IJT808Reply Msg0x0100(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0100"); + return base.Msg0x0100(request); + } + + public override IJT808Reply Msg0x0102(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0102"); + return base.Msg0x0102(request); + } + } +} diff --git a/simples/JT808.DotNetty.SimpleServer/JT808.DotNetty.SimpleServer.csproj b/simples/JT808.DotNetty.SimpleServer/JT808.DotNetty.SimpleServer.csproj new file mode 100644 index 0000000..d34d6bc --- /dev/null +++ b/simples/JT808.DotNetty.SimpleServer/JT808.DotNetty.SimpleServer.csproj @@ -0,0 +1,25 @@ + + + + Exe + netcoreapp2.2 + 7.3 + + + + + + + + + + + + + + + Always + + + + diff --git a/simples/JT808.DotNetty.SimpleServer/Program.cs b/simples/JT808.DotNetty.SimpleServer/Program.cs new file mode 100644 index 0000000..65d730f --- /dev/null +++ b/simples/JT808.DotNetty.SimpleServer/Program.cs @@ -0,0 +1,47 @@ + +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.SimpleServer.Handlers; +using JT808.DotNetty.Tcp; +using JT808.Protocol; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace JT808.DotNetty.SimpleServer +{ + 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) => + { + logging.AddConsole(); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration) + .AddJT808TcpNettyHost() + // 自定义Tcp消息处理业务 + .ReplaceMsgIdHandler() + .Builder(); + }); + + await serverHostBuilder.RunConsoleAsync(); + } + } +} diff --git a/simples/JT808.DotNetty.Simples.sln b/simples/JT808.DotNetty.Simples.sln new file mode 100644 index 0000000..7619fac --- /dev/null +++ b/simples/JT808.DotNetty.Simples.sln @@ -0,0 +1,125 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5A80356-5AF6-449F-9D8B-3C1BBB9D2443}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Protocol", "..\src\JT808.Protocol\src\JT808.Protocol\JT808.Protocol.csproj", "{9FCA2EE9-8253-41AA-A64C-9883413864F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Udp", "..\src\JT808.DotNetty.Udp\JT808.DotNetty.Udp.csproj", "{C960084C-2CF4-4748-AD35-D2384285D6A3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Core", "..\src\JT808.DotNetty.Core\JT808.DotNetty.Core.csproj", "{67C5DC72-0004-48B3-BB5A-9CB7069B4F02}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Abstractions", "..\src\JT808.DotNetty.Abstractions\JT808.DotNetty.Abstractions.csproj", "{4DCF33C0-67C5-4179-AF1E-4E919F9F856D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3BD7FF02-8516-4A77-A385-9FDCDD792E22}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Tcp", "..\src\JT808.DotNetty.Tcp\JT808.DotNetty.Tcp.csproj", "{330CD783-5564-4083-ABFC-573CDC369F50}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApi", "..\src\JT808.DotNetty.WebApi\JT808.DotNetty.WebApi.csproj", "{B783DE53-CE2A-4225-921F-04E5E57B28F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Core.Test", "..\src\JT808.DotNetty.Tests\JT808.DotNetty.Core.Test\JT808.DotNetty.Core.Test.csproj", "{1C4CCE9B-761B-4581-B5DA-5B6D83572D56}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Tcp.Test", "..\src\JT808.DotNetty.Tests\JT808.DotNetty.Tcp.Test\JT808.DotNetty.Tcp.Test.csproj", "{AEF1E1E2-C861-4268-86F6-6F376FAF79A7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Udp.Test", "..\src\JT808.DotNetty.Tests\JT808.DotNetty.Udp.Test\JT808.DotNetty.Udp.Test.csproj", "{E503BFD8-D90A-4610-97C7-5B9A0497303B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApi.Test", "..\src\JT808.DotNetty.Tests\JT808.DotNetty.WebApi.Test\JT808.DotNetty.WebApi.Test.csproj", "{EDE77A29-0840-450C-8B08-2D3388845AE5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Hosting", "..\src\JT808.DotNetty.Tests\JT808.DotNetty.Hosting\JT808.DotNetty.Hosting.csproj", "{A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApiClientTool", "..\src\JT808.DotNetty.WebApiClientTool\JT808.DotNetty.WebApiClientTool.csproj", "{9D86C951-94F2-4CBD-B177-8AF31DDB05D8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Client", "..\src\JT808.DotNetty.Client\JT808.DotNetty.Client.csproj", "{87C08239-C57F-4FC5-9579-05D0723AA4A0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "simples", "simples", "{2459FB59-8A33-49A4-ADBC-A0B12C5886A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.SimpleClient", "JT808.DotNetty.SimpleClient\JT808.DotNetty.SimpleClient.csproj", "{E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.SimpleServer", "JT808.DotNetty.SimpleServer\JT808.DotNetty.SimpleServer.csproj", "{CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9FCA2EE9-8253-41AA-A64C-9883413864F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FCA2EE9-8253-41AA-A64C-9883413864F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FCA2EE9-8253-41AA-A64C-9883413864F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FCA2EE9-8253-41AA-A64C-9883413864F9}.Release|Any CPU.Build.0 = Release|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Release|Any CPU.Build.0 = Release|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Release|Any CPU.Build.0 = Release|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Release|Any CPU.Build.0 = Release|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Release|Any CPU.Build.0 = Release|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Release|Any CPU.Build.0 = Release|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Release|Any CPU.Build.0 = Release|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Release|Any CPU.Build.0 = Release|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Release|Any CPU.Build.0 = Release|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Release|Any CPU.Build.0 = Release|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Release|Any CPU.Build.0 = Release|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Release|Any CPU.Build.0 = Release|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Release|Any CPU.Build.0 = Release|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Release|Any CPU.Build.0 = Release|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9FCA2EE9-8253-41AA-A64C-9883413864F9} = {B5A80356-5AF6-449F-9D8B-3C1BBB9D2443} + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {E503BFD8-D90A-4610-97C7-5B9A0497303B} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {EDE77A29-0840-450C-8B08-2D3388845AE5} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5} = {2459FB59-8A33-49A4-ADBC-A0B12C5886A6} + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88} = {2459FB59-8A33-49A4-ADBC-A0B12C5886A6} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FC0FFCEA-E1EF-4C97-A1C5-F89418B6834B} + EndGlobalSection +EndGlobal diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808AtomicCounterDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808AtomicCounterDto.cs new file mode 100644 index 0000000..b61c2a8 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808AtomicCounterDto.cs @@ -0,0 +1,12 @@ +namespace JT808.DotNetty.Abstractions.Dtos +{ + /// + /// 包计数器服务 + /// + public class JT808AtomicCounterDto + { + public long MsgSuccessCount { get; set; } + + public long MsgFailCount { get; set; } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808DefaultResultDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808DefaultResultDto.cs new file mode 100644 index 0000000..7faf2ee --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808DefaultResultDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808DefaultResultDto: JT808ResultDto + { + public JT808DefaultResultDto() + { + Data = "Hello,JT808 WebAPI"; + Code = JT808ResultCode.Ok; + } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808IPAddressDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808IPAddressDto.cs new file mode 100644 index 0000000..3f69192 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808IPAddressDto.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.Serialization; +using System.Text; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808IPAddressDto + { + public string Host { get; set; } + + public int Port { get; set; } + + public EndPoint endPoint; + + public EndPoint EndPoint + { + get + { + if (endPoint == null) + { + if (IPAddress.TryParse(Host, out IPAddress ip)) + { + endPoint = new IPEndPoint(ip, Port); + } + else + { + endPoint = new DnsEndPoint(Host, Port); + } + } + return endPoint; + } + set { } + } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808ResultDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808ResultDto.cs new file mode 100644 index 0000000..c6e8970 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808ResultDto.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808ResultDto + { + 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; + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808SystemCollectInfoDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808SystemCollectInfoDto.cs new file mode 100644 index 0000000..23c883c --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808SystemCollectInfoDto.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808SystemCollectInfoDto + { + /// + /// 进程Id + /// + public int ProcessId { get; set; } + /// + /// 进程分配内存 + /// 单位MB + /// + public double WorkingSet64 { get; set; } + /// + /// 进程分配内存峰值 + /// 单位MB + /// + public double PeakWorkingSet64 { get; set; } + /// + /// 进程分配私有内存 + /// 单位MB + /// + public double PrivateMemorySize64 { get; set; } + /// + /// 进程执行CPU总处理时间 + /// + public TimeSpan CPUTotalProcessorTime { get; set; } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808TcpSessionInfoDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808TcpSessionInfoDto.cs new file mode 100644 index 0000000..4a71a69 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808TcpSessionInfoDto.cs @@ -0,0 +1,24 @@ +using System; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808TcpSessionInfoDto + { + /// + /// 最后上线时间 + /// + public DateTime LastActiveTime { get; set; } + /// + /// 上线时间 + /// + public DateTime StartTime { get; set; } + /// + /// 终端手机号 + /// + public string TerminalPhoneNo { get; set; } + /// + /// 远程ip地址 + /// + public string RemoteAddressIP { get; set; } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808TrafficInfoDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808TrafficInfoDto.cs new file mode 100644 index 0000000..594c355 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808TrafficInfoDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808TrafficInfoDto + { + /// + /// 总接收大小 + /// 单位KB + /// + public double TotalReceiveSize { get; set; } + /// + /// 总发送大小 + /// 单位KB + /// + public double TotalSendSize { get; set; } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808UdpSessionInfoDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808UdpSessionInfoDto.cs new file mode 100644 index 0000000..a7d3f25 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808UdpSessionInfoDto.cs @@ -0,0 +1,24 @@ +using System; + +namespace JT808.DotNetty.Abstractions.Dtos +{ + public class JT808UdpSessionInfoDto + { + /// + /// 最后上线时间 + /// + public DateTime LastActiveTime { get; set; } + /// + /// 上线时间 + /// + public DateTime StartTime { get; set; } + /// + /// 终端手机号 + /// + public string TerminalPhoneNo { get; set; } + /// + /// 远程ip地址 + /// + public string RemoteAddressIP { get; set; } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Dtos/JT808UnificationSendRequestDto.cs b/src/JT808.DotNetty.Abstractions/Dtos/JT808UnificationSendRequestDto.cs new file mode 100644 index 0000000..8f32473 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Dtos/JT808UnificationSendRequestDto.cs @@ -0,0 +1,11 @@ +namespace JT808.DotNetty.Abstractions.Dtos +{ + /// + /// 统一下发请求参数 + /// + public class JT808UnificationSendRequestDto + { + public string TerminalPhoneNo { get; set; } + public byte[] Data { get; set; } + } +} diff --git a/src/JT808.DotNetty.Abstractions/Enums/JT808TransportProtocolType.cs b/src/JT808.DotNetty.Abstractions/Enums/JT808TransportProtocolType.cs new file mode 100644 index 0000000..0134bb8 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Enums/JT808TransportProtocolType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Abstractions.Enums +{ + /// + /// 传输协议类型 + /// + public enum JT808TransportProtocolType + { + tcp=1, + udp = 2 + } +} diff --git a/src/JT808.DotNetty.Abstractions/IJT808DownlinkPacket.cs b/src/JT808.DotNetty.Abstractions/IJT808DownlinkPacket.cs new file mode 100644 index 0000000..9969bd0 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/IJT808DownlinkPacket.cs @@ -0,0 +1,22 @@ +using JT808.DotNetty.Abstractions.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Abstractions +{ + /// + /// 下行数据包处理接口 + /// + public interface IJT808DownlinkPacket + { + /// + /// + /// + /// 数据包 + /// 协议类型:tcp/udp + /// + Task ProcessorAsync(byte[] data, JT808TransportProtocolType transportProtocolType); + } +} diff --git a/src/JT808.DotNetty.Abstractions/IJT808SessionPublishing.cs b/src/JT808.DotNetty.Abstractions/IJT808SessionPublishing.cs new file mode 100644 index 0000000..3a8e56c --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/IJT808SessionPublishing.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace JT808.DotNetty.Abstractions +{ + /// + /// 会话通知(在线/离线) + /// + public interface IJT808SessionPublishing + { + Task PublishAsync(string topicName, string value); + } +} diff --git a/src/JT808.DotNetty.Abstractions/IJT808SourcePackageDispatcher.cs b/src/JT808.DotNetty.Abstractions/IJT808SourcePackageDispatcher.cs new file mode 100644 index 0000000..f3fa1dd --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/IJT808SourcePackageDispatcher.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace JT808.DotNetty.Abstractions +{ + /// + /// 源包分发器 + /// 自定义源包分发器业务 + /// ConfigureServices: + /// services.Replace(new ServiceDescriptor(typeof(IJT808SourcePackageDispatcher),typeof(JT808SourcePackageDispatcherDefaultImpl),ServiceLifetime.Singleton)); + /// + public interface IJT808SourcePackageDispatcher + { + Task SendAsync(byte[] data); + } +} diff --git a/src/JT808.DotNetty.Abstractions/IJT808UplinkPacket.cs b/src/JT808.DotNetty.Abstractions/IJT808UplinkPacket.cs new file mode 100644 index 0000000..5198855 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/IJT808UplinkPacket.cs @@ -0,0 +1,19 @@ +using JT808.DotNetty.Abstractions.Enums; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Abstractions +{ + /// + /// 上行数据包处理接口 + /// + public interface IJT808UplinkPacket + { + /// + /// + /// + /// 数据包 + /// 传输协议类型 + /// + Task ProcessorAsync(byte[] data, JT808TransportProtocolType transportProtocolType); + } +} diff --git a/src/JT808.DotNetty.Abstractions/JT808.DotNetty.Abstractions.csproj b/src/JT808.DotNetty.Abstractions/JT808.DotNetty.Abstractions.csproj new file mode 100644 index 0000000..87166d8 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/JT808.DotNetty.Abstractions.csproj @@ -0,0 +1,10 @@ + + + + JT808.DotNetty.Abstractions + JT808.DotNetty.Abstractions + 基于DotNetty实现的JT808DotNetty的抽象库 + 基于DotNetty实现的JT808DotNetty的抽象库 + + + diff --git a/src/JT808.DotNetty.Abstractions/JT808Constants.cs b/src/JT808.DotNetty.Abstractions/JT808Constants.cs new file mode 100644 index 0000000..bc65121 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/JT808Constants.cs @@ -0,0 +1,84 @@ +namespace JT808.DotNetty.Abstractions +{ + public static class JT808Constants + { + public const string SessionOnline= "JT808SessionOnline"; + + public const string SessionOffline = "JT808SessionOffline"; + + public static class JT808WebApiRouteTable + { + public const string RouteTablePrefix = "/jt808api"; + + public const string SessionPrefix = "Session"; + + public const string TransmitPrefix = "Transmit"; + + public const string SystemCollectPrefix = "SystemCollect"; + + public const string TrafficPrefix = "Traffic"; + + public const string TcpPrefix = "Tcp"; + + public const string UdpPrefix = "Udp"; + + /// + ///获取当前系统进程使用率 + /// + public static string SystemCollectGet = $"{RouteTablePrefix}/{SystemCollectPrefix}/Get"; + /// + ///基于Tcp的添加转发过滤地址 + /// + public static string TransmitAdd = $"{RouteTablePrefix}/{TcpPrefix}/{TransmitPrefix}/Add"; + /// + /// 基于Tcp的删除转发过滤地址(不能删除在网关服务器配置文件配的地址) + /// + public static string TransmitRemove = $"{RouteTablePrefix}/{TcpPrefix}/{TransmitPrefix}/Remove"; + /// + ///基于Tcp的获取转发过滤地址信息集合 + /// + public static string TransmitGetAll = $"{RouteTablePrefix}/{TcpPrefix}/{TransmitPrefix}/GetAll"; + /// + /// 基于Tcp的包计数器 + /// + public static string GetTcpAtomicCounter = $"{RouteTablePrefix}/{TcpPrefix}/GetAtomicCounter"; + /// + /// 基于Tcp的会话服务集合 + /// + public static string SessionTcpGetAll = $"{RouteTablePrefix}/{TcpPrefix}/{SessionPrefix}/GetAll"; + /// + /// 基于Tcp的会话服务-通过设备终端号移除对应会话 + /// + public static string SessionTcpRemoveByTerminalPhoneNo = $"{RouteTablePrefix}/{TcpPrefix}/{SessionPrefix}/RemoveByTerminalPhoneNo"; + /// + /// 基于Tcp的统一下发信息 + /// + public static string UnificationTcpSend = $"{RouteTablePrefix}/{TcpPrefix}/UnificationSend"; + /// + /// 基于Tcp的流量服务获取 + /// + public static string TrafficTcpGet = $"{RouteTablePrefix}/{TcpPrefix}/{TrafficPrefix}/Get"; + + /// + /// 获取Udp包计数器 + /// + public static string GetUdpAtomicCounter = $"{RouteTablePrefix}/{UdpPrefix}/GetAtomicCounter"; + /// + /// 基于Udp的统一下发信息 + /// + public static string UnificationUdpSend = $"{RouteTablePrefix}/{UdpPrefix}/UnificationSend"; + /// + /// 基于Udp的会话服务集合 + /// + public static string SessionUdpGetAll = $"{RouteTablePrefix}/{UdpPrefix}/{SessionPrefix}/GetAll"; + /// + /// 基于Udp的会话服务-通过设备终端号移除对应会话 + /// + public static string SessionUdpRemoveByTerminalPhoneNo = $"{RouteTablePrefix}/{UdpPrefix}/{SessionPrefix}/RemoveByTerminalPhoneNo"; + /// + /// 基于Udp的流量服务获取 + /// + + + + FileSystem + Release + Any CPU + netstandard2.0 + ..\..\nupkgs + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Abstractions/Properties/PublishProfiles/FolderProfile.pubxml.user b/src/JT808.DotNetty.Abstractions/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/src/JT808.DotNetty.Abstractions/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.CleintBenchmark/Configs/ClientBenchmarkOptions.cs b/src/JT808.DotNetty.CleintBenchmark/Configs/ClientBenchmarkOptions.cs new file mode 100644 index 0000000..564da09 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Configs/ClientBenchmarkOptions.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.CleintBenchmark.Configs +{ + public class ClientBenchmarkOptions : IOptions + { + public string IP { get; set; } + public int Port { get; set; } + public int DeviceCount { get; set; } = 10; + /// + /// 5000ms毫秒 + /// + public int Interval { get; set; } = 5000; + public ClientBenchmarkOptions Value =>this; + } +} diff --git a/src/JT808.DotNetty.CleintBenchmark/Configs/NLog.xsd b/src/JT808.DotNetty.CleintBenchmark/Configs/NLog.xsd new file mode 100644 index 0000000..2f57d09 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Configs/NLog.xsd @@ -0,0 +1,3106 @@ + + + + + + + + + + + + + + + Watch config file for changes and reload automatically. + + + + + Print internal NLog messages to the console. Default value is: false + + + + + Print internal NLog messages to the console error output. Default value is: false + + + + + Write internal NLog messages to the specified file. + + + + + Log level threshold for internal log messages. Default value is: Info. + + + + + Global log level threshold for application log messages. Messages below this level won't be logged.. + + + + + Throw an exception when there is an internal error. Default value is: false. + + + + + Throw an exception when there is a configuration error. If not set, determined by throwExceptions. + + + + + Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. + + + + + Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. + + + + + Write timestamps for internal NLog messages. Default value is: true. + + + + + Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. + + + + + Perform mesage template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty. + + + + + + + + + + + + + + Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). + + + + + + + + + + + + + + + + + Prefix for targets/layout renderers/filters/conditions loaded from this assembly. + + + + + Load NLog extensions from the specified file (*.dll) + + + + + Load NLog extensions from the specified assembly. Assembly name should be fully qualified. + + + + + + + + + + Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name* + + + + + Comma separated list of levels that this rule matches. + + + + + Minimum level that this rule matches. + + + + + Maximum level that this rule matches. + + + + + Level that this rule matches. + + + + + Comma separated list of target names. + + + + + Ignore further rules if this one matches. + + + + + Enable or disable logging rule. Disabled rules are ignored. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. + + + + + Ignore any errors in the include file. + + + + + + + Variable name. + + + + + Variable value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events that should be processed in a batch by the lazy writer thread. + + + + + Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch + + + + + Action to be taken when the lazy writer thread request queue count exceeds the set limit. + + + + + Limit on the number of requests in the lazy writer thread request queue. + + + + + Time in milliseconds to sleep between batches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Delay the flush until the LogEvent has been confirmed as written + + + + + Condition expression. Log events who meet this condition will cause a flush on the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events to be buffered. + + + + + Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. + + + + + Indicates whether to use sliding timeout. + + + + + Action to take if the buffer overflows. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Maximum current connections. 0 = no maximum. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Network address. + + + + + Maximum queue size. + + + + + NDC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Viewer parameter name. + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to use default row highlighting rules. + + + + + Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Condition that must be met in order to set the specified foreground and background color. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + Indicates whether to ignore case when comparing texts. + + + + + Regular expression to be matched. You must specify either text or regex. + + + + + Text to be matched. You must specify either text or regex. + + + + + Indicates whether to match whole words only. + + + + + Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send the log messages to the standard error instead of the standard output. + + + + + Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. + + + + + Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. + + + + + Name of the database provider. + + + + + Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. + + + + + Indicates whether to keep the database connection open between the log events. + + + + + Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. + + + + + Name of the connection string (as specified in <connectionStrings> configuration section. + + + + + Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. + + + + + Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. + + + + + Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Text of the SQL command to be run on each log level. + + + + + Type of the SQL command to be run on each log level. + + + + + + + + + + + + + + + + + + + + + + + Type of the command. + + + + + Connection string to run the command against. If not provided, connection string from the target is used. + + + + + Indicates whether to ignore failures. + + + + + Command text. + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Database parameter name. + + + + + Database parameter precision. + + + + + Database parameter scale. + + + + + Database parameter size. + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Layout that renders event Category. + + + + + Layout that renders event ID. + + + + + Name of the Event Log to write to. This can be System, Application or any user-defined name. + + + + + Name of the machine on which Event Log service is running. + + + + + Value to be used as the event Source. + + + + + Action to take if the message is larger than the option. + + + + + Optional entrytype. When not set, or when not convertable to then determined by + + + + + Maximum Event log size in kilobytes. If null, the value won't be set. Default is 512 Kilobytes as specified by Eventlog API + + + + + Message length limit to write to the Event Log. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether to return to the first target after any successful write. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + File encoding. + + + + + Line ending mode. + + + + + Way file archives are numbered. + + + + + Name of the file to be used for an archive. + + + + + Indicates whether to automatically archive log files every time the specified time passes. + + + + + Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: + + + + + Indicates whether to compress archive files into the zip archive format. + + + + + Maximum number of archive files that should be kept. + + + + + Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. + + + + + Is the an absolute or relative path? + + + + + Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. + + + + + Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write + + + + + Is the an absolute or relative path? + + + + + Value indicationg whether file creation calls should be synchronized by a system global mutex. + + + + + Maximum number of log filenames that should be stored as existing. + + + + + Indicates whether the footer should be written only when the file is archived. + + + + + Name of the file to write to. + + + + + Value specifying the date format to use when archiving files. + + + + + Indicates whether to archive old log file on startup. + + + + + Indicates whether to create directories if they do not exist. + + + + + File attributes (Windows only). + + + + + Indicates whether to delete old log file on startup. + + + + + Indicates whether to replace file contents on each write instead of appending log message at the end. + + + + + Indicates whether to enable log file(s) to be deleted. + + + + + Number of times the write is appended on the file before NLog discards the log message. + + + + + Indicates whether concurrent writes to the log file by multiple processes on the same host. + + + + + Indicates whether to keep log file open instead of opening and closing it on each logging event. + + + + + Indicates whether concurrent writes to the log file by multiple processes on different network hosts. + + + + + Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). + + + + + Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Log file buffer size in bytes. + + + + + Indicates whether to automatically flush the file buffers after each log message. + + + + + Delay in milliseconds to wait before attempting to write to the file again. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Condition expression. Log events who meet this condition will be forwarded to the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Windows domain name to change context to. + + + + + Required impersonation level. + + + + + Type of the logon provider. + + + + + Logon Type. + + + + + User account password. + + + + + Indicates whether to revert to the credentials of the process instead of impersonating another user. + + + + + Username to change context to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Interval in which messages will be written up to the number of messages. + + + + + Maximum allowed number of messages written per . + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Endpoint address. + + + + + Name of the endpoint configuration in WCF configuration file. + + + + + Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) + + + + + Client ID. + + + + + Indicates whether to include per-event properties in the payload sent to the server. + + + + + Indicates whether to use binary message encoding. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Name of the parameter. + + + + + Type of the parameter. + + + + + Type of the parameter. Obsolete alias for + + + + + Parameter can combine multiple LogEvents into a single parameter value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send message as HTML instead of plain text. + + + + + Encoding to be used for sending e-mail. + + + + + Indicates whether to add new lines between log entries. + + + + + CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Mail message body (repeated for each log message send in one mail). + + + + + Mail subject. + + + + + Sender's email address (e.g. joe@domain.com). + + + + + Indicates the SMTP client timeout. + + + + + Priority used for sending mails. + + + + + Indicates whether NewLine characters in the body should be replaced with tags. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + SMTP Server to be used for sending. + + + + + SMTP Authentication mode. + + + + + Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. + + + + + Port number that SMTP Server is listening on. + + + + + Indicates whether the default Settings from System.Net.MailSettings should be used. + + + + + Folder where applications save mail messages to be processed by the local SMTP server. + + + + + Specifies how outgoing email messages will be handled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Class name. + + + + + Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Indicates whether to keep connection open whenever possible. + + + + + Maximum current connections. 0 = no maximum. + + + + + Maximum queue size. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Maximum current connections. 0 = no maximum. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Network address. + + + + + Maximum queue size. + + + + + NDC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to perform layout calculation. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether performance counter should be automatically created. + + + + + Name of the performance counter category. + + + + + Counter help text. + + + + + Name of the performance counter. + + + + + Performance counter type. + + + + + The value by which to increment the counter. + + + + + Performance counter instance name. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Default filter to be applied when no specific rule matches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + Condition to be tested. + + + + + Resulting filter to be applied when the condition matches. + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of times to repeat each log message. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of retries that should be attempted on the wrapped target in case of a failure. + + + + + Time to wait between retries in milliseconds. + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Always use independent of + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Encoding. + + + + + Value whether escaping be done according to the old NLog style (Very non-standard) + + + + + Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) + + + + + Web service method name. Only used with Soap. + + + + + Web service namespace. Only used with Soap. + + + + + Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters) + + + + + Protocol to be used when calling web service. + + + + + Web service URL. + + + + + Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). + + + + + (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). + + + + + Column delimiter. + + + + + Quote Character. + + + + + Quoting mode. + + + + + Indicates whether CVS should include header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout of the column. + + + + + Name of the column. + + + + + + + + + + + + + + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log events + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Option to render the empty object value {} + + + + + Option to suppress the extra spaces in the output json + + + + + + + + + + + + + + + Determines wether or not this attribute will be Json encoded. + + + + + Indicates whether to escape non-ascii characters + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + + + + + + + + + + + + + + Option to include all properties from the log events + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include contents of the stack. + + + + + + + + + + + + + + Layout text. + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Condition expression. + + + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Layout to be used to filter log messages. + + + + + Default number of unique filter values to expect, will automatically increase if needed + + + + + Append FilterCount to the when an event is no longer filtered + + + + + Insert FilterCount value into when an event is no longer filtered + + + + + Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout. + + + + + Max number of unique filter values to expect simultaneously + + + + + Max length of filter values, will truncate if above limit + + + + + Default buffer size for the internal buffers + + + + + Reuse internal buffers, and doesn't have to constantly allocate new buffers + + + + + How long before a filter expires, and logging is accepted again + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.CleintBenchmark/Configs/nlog.unix.config b/src/JT808.DotNetty.CleintBenchmark/Configs/nlog.unix.config new file mode 100644 index 0000000..2c8b777 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Configs/nlog.unix.config @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.CleintBenchmark/Configs/nlog.win.config b/src/JT808.DotNetty.CleintBenchmark/Configs/nlog.win.config new file mode 100644 index 0000000..14d4a10 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Configs/nlog.win.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.CleintBenchmark/JT808.DotNetty.CleintBenchmark.csproj b/src/JT808.DotNetty.CleintBenchmark/JT808.DotNetty.CleintBenchmark.csproj new file mode 100644 index 0000000..a77a0a4 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/JT808.DotNetty.CleintBenchmark.csproj @@ -0,0 +1,37 @@ + + + + Exe + netcoreapp2.2 + 7.3 + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + Designer + + + + diff --git a/src/JT808.DotNetty.CleintBenchmark/Program.cs b/src/JT808.DotNetty.CleintBenchmark/Program.cs new file mode 100644 index 0000000..de57c88 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Program.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; +using System; +using System.Threading.Tasks; +using JT808.DotNetty.Client; +using JT808.DotNetty.CleintBenchmark.Configs; +using JT808.DotNetty.CleintBenchmark.Services; +using Microsoft.Extensions.Configuration; + +namespace JT808.DotNetty.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(hostContext.Configuration.GetSection("ClientBenchmarkOptions")); + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Client(); + services.AddHostedService(); + services.AddHostedService(); + }); + await serverHostBuilder.RunConsoleAsync(); + } + } +} diff --git a/src/JT808.DotNetty.CleintBenchmark/Services/CleintBenchmarkHostedService.cs b/src/JT808.DotNetty.CleintBenchmark/Services/CleintBenchmarkHostedService.cs new file mode 100644 index 0000000..d20a92b --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Services/CleintBenchmarkHostedService.cs @@ -0,0 +1,83 @@ +using JT808.DotNetty.CleintBenchmark.Configs; +using JT808.DotNetty.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.DotNetty.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 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; + } + } +} diff --git a/src/JT808.DotNetty.CleintBenchmark/Services/CleintBenchmarkReportHostedService.cs b/src/JT808.DotNetty.CleintBenchmark/Services/CleintBenchmarkReportHostedService.cs new file mode 100644 index 0000000..6eddfdd --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/Services/CleintBenchmarkReportHostedService.cs @@ -0,0 +1,55 @@ +using JT808.DotNetty.CleintBenchmark.Configs; +using JT808.DotNetty.Client; +using JT808.DotNetty.Client.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.DotNetty.CleintBenchmark.Services +{ + public class CleintBenchmarkReportHostedService : IHostedService + { + private readonly JT808ReportService jT808ReportService; + + private CancellationTokenSource cts=new CancellationTokenSource(); + + private readonly ILogger logger; + public CleintBenchmarkReportHostedService( + ILoggerFactory loggerFactory, + JT808ReportService 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; + } + } +} diff --git a/src/JT808.DotNetty.CleintBenchmark/appsettings.json b/src/JT808.DotNetty.CleintBenchmark/appsettings.json new file mode 100644 index 0000000..0b507a3 --- /dev/null +++ b/src/JT808.DotNetty.CleintBenchmark/appsettings.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "ClientBenchmarkOptions": { + "IP": "127.0.0.1", + "Port": 808, + "DeviceCount": 10 + } +} diff --git a/src/JT808.DotNetty.Client/Codecs/JT808ClientTcpDecoder.cs b/src/JT808.DotNetty.Client/Codecs/JT808ClientTcpDecoder.cs new file mode 100644 index 0000000..9dff52c --- /dev/null +++ b/src/JT808.DotNetty.Client/Codecs/JT808ClientTcpDecoder.cs @@ -0,0 +1,20 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using System.Collections.Generic; +using JT808.Protocol; +using DotNetty.Transport.Channels; + +namespace JT808.DotNetty.Client.Codecs +{ + public class JT808ClientTcpDecoder : ByteToMessageDecoder + { + protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output) + { + byte[] buffer = new byte[input.Capacity + 2]; + input.ReadBytes(buffer, 1, input.Capacity); + buffer[0] = JT808Package.BeginFlag; + buffer[input.Capacity + 1] = JT808Package.EndFlag; + output.Add(buffer); + } + } +} diff --git a/src/JT808.DotNetty.Client/Codecs/JT808ClientTcpEncoder.cs b/src/JT808.DotNetty.Client/Codecs/JT808ClientTcpEncoder.cs new file mode 100644 index 0000000..741d2f9 --- /dev/null +++ b/src/JT808.DotNetty.Client/Codecs/JT808ClientTcpEncoder.cs @@ -0,0 +1,54 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using System.Collections.Generic; +using JT808.Protocol; +using DotNetty.Transport.Channels; +using Microsoft.Extensions.Logging; +using JT808.DotNetty.Client.Metadata; +using JT808.DotNetty.Client.Services; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.Client.Codecs +{ + public class JT808ClientTcpEncoder : MessageToByteEncoder + { + private readonly ILogger logger; + private readonly JT808SendAtomicCounterService jT808SendAtomicCounterService; + private readonly JT808Serializer JT808Serializer; + + public JT808ClientTcpEncoder( + IJT808Config jT808Config, + JT808SendAtomicCounterService jT808SendAtomicCounterService,ILoggerFactory loggerFactory) + { + logger=loggerFactory.CreateLogger(); + 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(); + } + } + } +} diff --git a/src/JT808.DotNetty.Client/DeviceConfig.cs b/src/JT808.DotNetty.Client/DeviceConfig.cs new file mode 100644 index 0000000..332d6cf --- /dev/null +++ b/src/JT808.DotNetty.Client/DeviceConfig.cs @@ -0,0 +1,28 @@ +using JT808.Protocol; +using JT808.Protocol.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.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; } + /// + /// 心跳时间(秒) + /// + public int Heartbeat { get; set; } = 30; + + public IJT808MsgSNDistributed MsgSNDistributed { get; } + } +} diff --git a/src/JT808.DotNetty.Client/Handlers/JT808TcpClientConnectionHandler.cs b/src/JT808.DotNetty.Client/Handlers/JT808TcpClientConnectionHandler.cs new file mode 100644 index 0000000..e495ffc --- /dev/null +++ b/src/JT808.DotNetty.Client/Handlers/JT808TcpClientConnectionHandler.cs @@ -0,0 +1,95 @@ +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Channels; +using JT808.Protocol.MessageBody; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Client.Handlers +{ + /// + /// JT808客户端连接通道处理程序 + /// + public class JT808TcpClientConnectionHandler : ChannelHandlerAdapter + { + private readonly ILogger logger; + private readonly JT808TcpClient jT808TcpClient; + + public JT808TcpClientConnectionHandler( + JT808TcpClient jT808TcpClient) + { + logger = jT808TcpClient.LoggerFactory.CreateLogger(); + this.jT808TcpClient = jT808TcpClient; + } + + /// + /// 通道激活 + /// + /// + public override void ChannelActive(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"<<<{ channelId } Successful client connection to server."); + base.ChannelActive(context); + } + + /// + /// 设备主动断开 + /// + /// + public override void ChannelInactive(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($">>>{ channelId } The client disconnects from the server."); + + base.ChannelInactive(context); + } + + /// + /// 服务器主动断开 + /// + /// + /// + public override Task CloseAsync(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"<<<{ channelId } The server disconnects from the client."); + + return base.CloseAsync(context); + } + + public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); + + /// + /// 超时策略 + /// + /// + /// + public override void UserEventTriggered(IChannelHandlerContext context, object evt) + { + IdleStateEvent idleStateEvent = evt as IdleStateEvent; + if (idleStateEvent != null) + { + if(idleStateEvent.State== IdleState.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(); + } + } +} + diff --git a/src/JT808.DotNetty.Client/Handlers/JT808TcpClientHandler.cs b/src/JT808.DotNetty.Client/Handlers/JT808TcpClientHandler.cs new file mode 100644 index 0000000..376a486 --- /dev/null +++ b/src/JT808.DotNetty.Client/Handlers/JT808TcpClientHandler.cs @@ -0,0 +1,30 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using System; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using JT808.DotNetty.Client.Services; + +namespace JT808.DotNetty.Client.Handlers +{ + /// + /// JT808客户端处理程序 + /// + internal class JT808TcpClientHandler : SimpleChannelInboundHandler + { + private readonly ILogger logger; + private readonly JT808ReceiveAtomicCounterService jT808ReceiveAtomicCounterService; + public JT808TcpClientHandler(JT808ReceiveAtomicCounterService jT808ReceiveAtomicCounterService,JT808TcpClient jT808TcpClient) + { + logger = jT808TcpClient.LoggerFactory.CreateLogger(); + 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(); + } + } +} diff --git a/src/JT808.DotNetty.Client/JT808.DotNetty.Client.csproj b/src/JT808.DotNetty.Client/JT808.DotNetty.Client.csproj new file mode 100644 index 0000000..f08d5ee --- /dev/null +++ b/src/JT808.DotNetty.Client/JT808.DotNetty.Client.csproj @@ -0,0 +1,31 @@ + + + + netstandard2.0 + 7.3 + Copyright 2018. + SmallChi + JT808.DotNetty.Client + JT808.DotNetty.Client + 基于DotNetty实现的JT808DotNetty的客户端工具 + 基于DotNetty实现的JT808DotNetty的客户端工具 + false + https://github.com/SmallChi/JT808DotNetty + https://github.com/SmallChi/JT808DotNetty + https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE + true + 2.1.2 + + + + + + + + + + + + + + diff --git a/src/JT808.DotNetty.Client/JT808ClientDotnettyExtensions.cs b/src/JT808.DotNetty.Client/JT808ClientDotnettyExtensions.cs new file mode 100644 index 0000000..2c8790d --- /dev/null +++ b/src/JT808.DotNetty.Client/JT808ClientDotnettyExtensions.cs @@ -0,0 +1,21 @@ +using JT808.DotNetty.Client.Services; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Client +{ + public static class JT808ClientDotnettyExtensions + { + public static IServiceCollection AddJT808Client(this IServiceCollection serviceDescriptors) + { + serviceDescriptors.AddSingleton(); + serviceDescriptors.AddSingleton(); + serviceDescriptors.AddSingleton(); + serviceDescriptors.AddSingleton(); + serviceDescriptors.AddHostedService(); + return serviceDescriptors; + } + } +} diff --git a/src/JT808.DotNetty.Client/JT808ClientMsgSNDistributedImpl.cs b/src/JT808.DotNetty.Client/JT808ClientMsgSNDistributedImpl.cs new file mode 100644 index 0000000..e15d703 --- /dev/null +++ b/src/JT808.DotNetty.Client/JT808ClientMsgSNDistributedImpl.cs @@ -0,0 +1,16 @@ +using JT808.Protocol; +using JT808.Protocol.Interfaces; +using System.Threading; + +namespace JT808.DotNetty.Client +{ + internal class JT808ClientMsgSNDistributedImpl : IJT808MsgSNDistributed + { + int _counter = 0; + + public ushort Increment() + { + return (ushort)Interlocked.Increment(ref _counter); + } + } +} diff --git a/src/JT808.DotNetty.Client/JT808TcpClient.cs b/src/JT808.DotNetty.Client/JT808TcpClient.cs new file mode 100644 index 0000000..9967add --- /dev/null +++ b/src/JT808.DotNetty.Client/JT808TcpClient.cs @@ -0,0 +1,116 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using DotNetty.Transport.Libuv; +using JT808.DotNetty.Client.Handlers; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Threading.Tasks; +using JT808.DotNetty.Client.Metadata; +using JT808.DotNetty.Client.Codecs; +using JT808.DotNetty.Client.Services; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.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(); + JT808SendAtomicCounterService jT808SendAtomicCounterService = serviceProvider.GetRequiredService(); + JT808ReceiveAtomicCounterService jT808ReceiveAtomicCounterService = serviceProvider.GetRequiredService(); + IJT808Config jT808Config = serviceProvider.GetRequiredService(); + group = new MultithreadEventLoopGroup(1); + Bootstrap bootstrap = new Bootstrap(); + bootstrap.Group(group); + bootstrap.Channel(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + bootstrap.Option(ChannelOption.SoReuseport, true); + } + bootstrap + .Option(ChannelOption.SoBacklog, 8192) + .Handler(new ActionChannelInitializer(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); + } + } +} diff --git a/src/JT808.DotNetty.Client/JT808TcpClientExtensions.cs b/src/JT808.DotNetty.Client/JT808TcpClientExtensions.cs new file mode 100644 index 0000000..3f886a0 --- /dev/null +++ b/src/JT808.DotNetty.Client/JT808TcpClientExtensions.cs @@ -0,0 +1,103 @@ +using JT808.DotNetty.Client.Metadata; +using JT808.Protocol; +using JT808.Protocol.MessageBody; +using System; +using System.Collections.Generic; +using System.Text; +using JT808.Protocol.Enums; +using JT808.Protocol.Extensions; + +namespace JT808.DotNetty.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); + } + + /// + /// 终端通用应答 + /// + /// + /// + /// + 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); + } + + /// + /// 终端心跳 + /// + /// + /// + /// + 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); + } + + /// + /// 终端注销 + /// + /// + /// + /// + 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); + } + + /// + /// 终端鉴权 + /// + /// + /// + /// + 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); + } + + /// + /// 终端注册 + /// + /// + /// + /// + 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); + } + + /// + /// 位置信息汇报 + /// + /// + /// + /// + 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); + } + } +} diff --git a/src/JT808.DotNetty.Client/JT808TcpClientFactory.cs b/src/JT808.DotNetty.Client/JT808TcpClientFactory.cs new file mode 100644 index 0000000..0f15ed3 --- /dev/null +++ b/src/JT808.DotNetty.Client/JT808TcpClientFactory.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace JT808.DotNetty.Client +{ + public interface IJT808TcpClientFactory : IDisposable + { + JT808TcpClient Create(DeviceConfig deviceConfig); + + List GetAll(); + } + + public class JT808TcpClientFactory: IJT808TcpClientFactory + { + private readonly ConcurrentDictionary dict; + + private readonly IServiceProvider serviceProvider; + + public JT808TcpClientFactory(IServiceProvider serviceProvider) + { + dict = new ConcurrentDictionary(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 GetAll() + { + return dict.Values.ToList(); + } + } +} diff --git a/src/JT808.DotNetty.Client/Metadata/JT808AtomicCounter.cs b/src/JT808.DotNetty.Client/Metadata/JT808AtomicCounter.cs new file mode 100644 index 0000000..1ffe64d --- /dev/null +++ b/src/JT808.DotNetty.Client/Metadata/JT808AtomicCounter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace JT808.DotNetty.Client.Metadata +{ + /// + /// + /// + /// + internal class JT808AtomicCounter + { + long counter = 0; + + public JT808AtomicCounter(long initialCount = 0) + { + this.counter = initialCount; + } + + public void Reset() + { + Interlocked.Exchange(ref counter, 0); + } + + public long Increment() + { + return Interlocked.Increment(ref counter); + } + + public long Add(long len) + { + return Interlocked.Add(ref counter,len); + } + + public long Decrement() + { + return Interlocked.Decrement(ref counter); + } + + public long Count + { + get + { + return Interlocked.Read(ref counter); + } + } + } +} diff --git a/src/JT808.DotNetty.Client/Metadata/JT808ClientRequest.cs b/src/JT808.DotNetty.Client/Metadata/JT808ClientRequest.cs new file mode 100644 index 0000000..00c1991 --- /dev/null +++ b/src/JT808.DotNetty.Client/Metadata/JT808ClientRequest.cs @@ -0,0 +1,30 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Client.Metadata +{ + public class JT808ClientRequest + { + public JT808Package Package { get; } + + public byte[] HexData { get; } + + /// + /// 根据实际情况适当调整包的大小 + /// + public int MinBufferSize { get;} + + public JT808ClientRequest(JT808Package package,int minBufferSize=1024) + { + Package = package; + MinBufferSize = minBufferSize; + } + + public JT808ClientRequest(byte[] hexData) + { + HexData = hexData; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Client/Metadata/JT808Report.cs b/src/JT808.DotNetty.Client/Metadata/JT808Report.cs new file mode 100644 index 0000000..78e695e --- /dev/null +++ b/src/JT808.DotNetty.Client/Metadata/JT808Report.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Client.Metadata +{ + public class JT808Report + { + public long SendTotalCount { get; set; } + public long ReceiveTotalCount { get; set; } + public DateTime CurrentDate { get; set; } + public int Connections { get; set; } + public int OnlineConnections { get; set; } + public int OfflineConnections { get; set; } + } +} diff --git a/src/JT808.DotNetty.Client/Services/JT808ReceiveAtomicCounterService.cs b/src/JT808.DotNetty.Client/Services/JT808ReceiveAtomicCounterService.cs new file mode 100644 index 0000000..d7cec1d --- /dev/null +++ b/src/JT808.DotNetty.Client/Services/JT808ReceiveAtomicCounterService.cs @@ -0,0 +1,35 @@ +using JT808.DotNetty.Client.Metadata; + +namespace JT808.DotNetty.Client.Services +{ + /// + /// 接收计数包服务 + /// + public class JT808ReceiveAtomicCounterService + { + private readonly JT808AtomicCounter MsgSuccessCounter; + + public JT808ReceiveAtomicCounterService() + { + MsgSuccessCounter=new JT808AtomicCounter(); + } + + public void Reset() + { + MsgSuccessCounter.Reset(); + } + + public long MsgSuccessIncrement() + { + return MsgSuccessCounter.Increment(); + } + + public long MsgSuccessCount + { + get + { + return MsgSuccessCounter.Count; + } + } + } +} diff --git a/src/JT808.DotNetty.Client/Services/JT808ReportHostedService.cs b/src/JT808.DotNetty.Client/Services/JT808ReportHostedService.cs new file mode 100644 index 0000000..063afa1 --- /dev/null +++ b/src/JT808.DotNetty.Client/Services/JT808ReportHostedService.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Client.Services +{ + public class JT808ReportHostedService : IHostedService + { + private readonly JT808ReportService jT808ReportService; + private CancellationTokenSource cts = new CancellationTokenSource(); + public JT808ReportHostedService(JT808ReportService jT808ReportService) + { + this.jT808ReportService = jT808ReportService; + } + public Task StartAsync(CancellationToken cancellationToken) + { + Task.Run(() => + { + while (!cts.IsCancellationRequested) + { + jT808ReportService.Create(); + Thread.Sleep(1000); + //Task.Delay(TimeSpan.FromSeconds(1), cts.Token); + } + }, cts.Token); + return Task.CompletedTask; + } + public Task StopAsync(CancellationToken cancellationToken) + { + cts.Cancel(); + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Client/Services/JT808ReportService.cs b/src/JT808.DotNetty.Client/Services/JT808ReportService.cs new file mode 100644 index 0000000..ef94b7b --- /dev/null +++ b/src/JT808.DotNetty.Client/Services/JT808ReportService.cs @@ -0,0 +1,42 @@ +using JT808.DotNetty.Client.Metadata; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace JT808.DotNetty.Client.Services +{ + public class JT808ReportService + { + private readonly JT808ReceiveAtomicCounterService jT808ReceiveAtomicCounterService; + private readonly JT808SendAtomicCounterService jT808SendAtomicCounterService; + private readonly IJT808TcpClientFactory jT808TcpClientFactory; + + public List JT808Reports { get; private set; } + + public JT808ReportService( + JT808ReceiveAtomicCounterService jT808ReceiveAtomicCounterService, + JT808SendAtomicCounterService jT808SendAtomicCounterService, + IJT808TcpClientFactory jT808TcpClientFactory) + { + this.jT808ReceiveAtomicCounterService = jT808ReceiveAtomicCounterService; + this.jT808SendAtomicCounterService = jT808SendAtomicCounterService; + this.jT808TcpClientFactory = jT808TcpClientFactory; + JT808Reports = new List(); + } + + public void Create() + { + var clients = jT808TcpClientFactory.GetAll(); + JT808Reports.Add(new JT808Report() + { + SendTotalCount= jT808SendAtomicCounterService.MsgSuccessCount, + ReceiveTotalCount= jT808ReceiveAtomicCounterService.MsgSuccessCount, + CurrentDate=DateTime.Now, + Connections= clients.Count, + OnlineConnections= clients.Where(w => w.IsOpen).Count(), + OfflineConnections= clients.Where(w => !w.IsOpen).Count(), + }); + } + } +} diff --git a/src/JT808.DotNetty.Client/Services/JT808SendAtomicCounterService.cs b/src/JT808.DotNetty.Client/Services/JT808SendAtomicCounterService.cs new file mode 100644 index 0000000..7642bc0 --- /dev/null +++ b/src/JT808.DotNetty.Client/Services/JT808SendAtomicCounterService.cs @@ -0,0 +1,35 @@ +using JT808.DotNetty.Client.Metadata; + +namespace JT808.DotNetty.Client.Services +{ + /// + /// 发送计数包服务 + /// + public class JT808SendAtomicCounterService + { + private readonly JT808AtomicCounter MsgSuccessCounter; + + public JT808SendAtomicCounterService() + { + MsgSuccessCounter=new JT808AtomicCounter(); + } + + public void Reset() + { + MsgSuccessCounter.Reset(); + } + + public long MsgSuccessIncrement() + { + return MsgSuccessCounter.Increment(); + } + + public long MsgSuccessCount + { + get + { + return MsgSuccessCounter.Count; + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Codecs/JT808TcpDecoder.cs b/src/JT808.DotNetty.Core/Codecs/JT808TcpDecoder.cs new file mode 100644 index 0000000..3eb3006 --- /dev/null +++ b/src/JT808.DotNetty.Core/Codecs/JT808TcpDecoder.cs @@ -0,0 +1,20 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using System.Collections.Generic; +using JT808.Protocol; +using DotNetty.Transport.Channels; + +namespace JT808.DotNetty.Core.Codecs +{ + public class JT808TcpDecoder : ByteToMessageDecoder + { + protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output) + { + byte[] buffer = new byte[input.Capacity + 2]; + input.ReadBytes(buffer, 1, input.Capacity); + buffer[0] = JT808Package.BeginFlag; + buffer[input.Capacity + 1] = JT808Package.EndFlag; + output.Add(buffer); + } + } +} diff --git a/src/JT808.DotNetty.Core/Codecs/JT808TcpEncoder.cs b/src/JT808.DotNetty.Core/Codecs/JT808TcpEncoder.cs new file mode 100644 index 0000000..80bee07 --- /dev/null +++ b/src/JT808.DotNetty.Core/Codecs/JT808TcpEncoder.cs @@ -0,0 +1,67 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using JT808.Protocol; +using DotNetty.Transport.Channels; +using JT808.DotNetty.Core.Interfaces; +using Microsoft.Extensions.Logging; +using JT808.DotNetty.Core.Services; +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Abstractions.Enums; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.Core.Codecs +{ + /// + /// tcp统一下发出口 + /// + public class JT808TcpEncoder : MessageToByteEncoder + { + private readonly ILogger logger; + + private readonly JT808TrafficService jT808TrafficService; + + private readonly IJT808DownlinkPacket jT808DownlinkPacket; + + private readonly JT808Serializer JT808Serializer; + + public JT808TcpEncoder( + IJT808Config jT808Config, + ILoggerFactory loggerFactory, + JT808TrafficServiceFactory jT808TrafficServiceFactory, + IJT808DownlinkPacket jT808DownlinkPacket) + { + logger = loggerFactory.CreateLogger(); + this.jT808TrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.tcp); + this.jT808DownlinkPacket = jT808DownlinkPacket; + 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); + jT808TrafficService.SendSize(sendData.Length); + jT808DownlinkPacket.ProcessorAsync(sendData, JT808TransportProtocolType.tcp); + 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) + { + jT808TrafficService.SendSize(message.HexData.Length); + jT808DownlinkPacket.ProcessorAsync(message.HexData, JT808TransportProtocolType.tcp); + output.WriteBytes(Unpooled.WrappedBuffer(message.HexData)); + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Codecs/JT808UdpDecoder.cs b/src/JT808.DotNetty.Core/Codecs/JT808UdpDecoder.cs new file mode 100644 index 0000000..df27984 --- /dev/null +++ b/src/JT808.DotNetty.Core/Codecs/JT808UdpDecoder.cs @@ -0,0 +1,21 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Transport.Channels; +using System.Collections.Generic; +using DotNetty.Transport.Channels.Sockets; +using JT808.DotNetty.Core.Metadata; + +namespace JT808.DotNetty.Core.Codecs +{ + public class JT808UdpDecoder : MessageToMessageDecoder + { + protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List output) + { + if (!message.Content.IsReadable()) return; + IByteBuffer byteBuffer = message.Content; + byte[] buffer = new byte[byteBuffer.ReadableBytes]; + byteBuffer.ReadBytes(buffer); + output.Add(new JT808UdpPackage(buffer, message.Sender)); + } + } +} diff --git a/src/JT808.DotNetty.Core/Configurations/JT808ClientConfiguration.cs b/src/JT808.DotNetty.Core/Configurations/JT808ClientConfiguration.cs new file mode 100644 index 0000000..8288fd4 --- /dev/null +++ b/src/JT808.DotNetty.Core/Configurations/JT808ClientConfiguration.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT808.DotNetty.Core.Configurations +{ + public class JT808ClientConfiguration + { + public string Host { get; set; } + + public int Port { get; set; } + + private EndPoint endPoint; + + public EndPoint EndPoint + { + get + { + if (endPoint == null) + { + if (IPAddress.TryParse(Host, out IPAddress ip)) + { + endPoint = new IPEndPoint(ip, Port); + } + } + return endPoint; + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Configurations/JT808Configuration.cs b/src/JT808.DotNetty.Core/Configurations/JT808Configuration.cs new file mode 100644 index 0000000..8375363 --- /dev/null +++ b/src/JT808.DotNetty.Core/Configurations/JT808Configuration.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.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; + + /// + /// WebApi服务 + /// 默认828端口 + /// + public int WebApiPort { get; set; } = 828; + + /// + /// 转发远程地址 (可选项)知道转发的地址有利于提升性能 + /// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: + // 1.消息的序列化 + // 2.消息的下发 + // 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle + // 就跟神兽貔貅一样。。。 + /// + public List ForwardingRemoteIPAddress { get; set; } + } +} diff --git a/src/JT808.DotNetty.Core/Converters/JsonByteArrayHexConverter.cs b/src/JT808.DotNetty.Core/Converters/JsonByteArrayHexConverter.cs new file mode 100644 index 0000000..14875fb --- /dev/null +++ b/src/JT808.DotNetty.Core/Converters/JsonByteArrayHexConverter.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Converters +{ + class ByteArrayHexConverter : JsonConverter + { + public override bool CanConvert(Type objectType) => objectType == typeof(byte[]); + + public override bool CanRead => false; + public override bool CanWrite => true; + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException(); + + private readonly string _separator; + + public ByteArrayHexConverter(string separator = " ") => _separator = separator; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var hexString = string.Join(_separator, ((byte[])value).Select(p => p.ToString("X2"))); + writer.WriteValue(hexString); + } + } +} diff --git a/src/JT808.DotNetty.Core/Converters/JsonIPAddressConverter.cs b/src/JT808.DotNetty.Core/Converters/JsonIPAddressConverter.cs new file mode 100644 index 0000000..955ed04 --- /dev/null +++ b/src/JT808.DotNetty.Core/Converters/JsonIPAddressConverter.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT808.DotNetty.Core.Converters +{ + public class JsonIPAddressConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(IPAddress)); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return IPAddress.Parse((string)reader.Value); + } + } +} diff --git a/src/JT808.DotNetty.Core/Converters/JsonIPEndPointConverter.cs b/src/JT808.DotNetty.Core/Converters/JsonIPEndPointConverter.cs new file mode 100644 index 0000000..8bed28f --- /dev/null +++ b/src/JT808.DotNetty.Core/Converters/JsonIPEndPointConverter.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Net; + +namespace JT808.DotNetty.Core.Converters +{ + public class JsonIPEndPointConverter: JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(IPEndPoint)); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + IPEndPoint ep = (IPEndPoint)value; + JObject jo = new JObject(); + jo.Add("Host", JToken.FromObject(ep.Address, serializer)); + jo.Add("Port", ep.Port); + jo.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject jo = JObject.Load(reader); + IPAddress address = jo["Host"].ToObject(serializer); + int port = (int)jo["Port"]; + return new IPEndPoint(address, port); + } + } +} diff --git a/src/JT808.DotNetty.Core/Handlers/JT808MsgIdHttpHandlerBase.cs b/src/JT808.DotNetty.Core/Handlers/JT808MsgIdHttpHandlerBase.cs new file mode 100644 index 0000000..fc7ef74 --- /dev/null +++ b/src/JT808.DotNetty.Core/Handlers/JT808MsgIdHttpHandlerBase.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text; +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Core.Metadata; +using Newtonsoft.Json; + +namespace JT808.DotNetty.Core.Handlers +{ + /// + /// 基于webapi http模式抽象消息处理业务 + /// 自定义消息处理业务 + /// 注意: + /// 1.ConfigureServices: + /// services.Replace(new ServiceDescriptor(typeof(JT808MsgIdHttpHandlerBase),typeof(JT808MsgIdCustomHttpHandlerImpl),ServiceLifetime.Singleton)); + /// 2.解析具体的消息体,具体消息调用具体的JT808Serializer.Deserialize + /// + public abstract class JT808MsgIdHttpHandlerBase + { + /// + /// 初始化消息处理业务 + /// + protected JT808MsgIdHttpHandlerBase() + { + HandlerDict = new Dictionary>(); + } + + protected void CreateRoute(string url, Func func) + { + if (!HandlerDict.ContainsKey(url)) + { + HandlerDict.Add(url, func); + } + else + { + // 替换 + HandlerDict[url] = func; + } + } + + public Dictionary> HandlerDict { get; } + + protected JT808HttpResponse CreateJT808HttpResponse(dynamic dynamicObject) + { + byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dynamicObject)); + return new JT808HttpResponse() + { + Data = data + }; + } + + public JT808HttpResponse DefaultHttpResponse() + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808DefaultResultDto())); + return new JT808HttpResponse(json); + } + + public JT808HttpResponse EmptyHttpResponse() + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto() + { + Code = JT808ResultCode.Empty, + Message = "内容为空", + Data = "Content Empty" + })); + return new JT808HttpResponse(json); + } + + public JT808HttpResponse NotFoundHttpResponse() + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto() + { + Code = JT808ResultCode.NotFound, + Message = "没有该服务", + Data = "没有该服务" + })); + return new JT808HttpResponse(json); + } + + public JT808HttpResponse ErrorHttpResponse(Exception ex) + { + byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto() + { + Code = JT808ResultCode.Error, + Message = JsonConvert.SerializeObject(ex), + Data = ex.Message + })); + return new JT808HttpResponse(json); + } + } +} diff --git a/src/JT808.DotNetty.Core/Handlers/JT808MsgIdTcpHandlerBase.cs b/src/JT808.DotNetty.Core/Handlers/JT808MsgIdTcpHandlerBase.cs new file mode 100644 index 0000000..8729013 --- /dev/null +++ b/src/JT808.DotNetty.Core/Handlers/JT808MsgIdTcpHandlerBase.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using JT808.Protocol.Enums; +using JT808.Protocol.Extensions; +using JT808.Protocol.MessageBody; + +namespace JT808.DotNetty.Core.Handlers +{ + /// + /// 基于Tcp模式抽象消息处理业务 + /// 自定义消息处理业务 + /// 注意: + /// 1.ConfigureServices: + /// services.Replace(new ServiceDescriptor(typeof(JT808MsgIdTcpHandlerBase),typeof(JT808MsgIdCustomTcpHandlerImpl),ServiceLifetime.Singleton)); + /// 2.解析具体的消息体,具体消息调用具体的JT808Serializer.Deserialize + /// + public abstract class JT808MsgIdTcpHandlerBase + { + protected JT808TcpSessionManager sessionManager { get; } + /// + /// 初始化消息处理业务 + /// + protected JT808MsgIdTcpHandlerBase(JT808TcpSessionManager sessionManager) + { + this.sessionManager = sessionManager; + HandlerDict = new Dictionary> + { + {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 Dictionary> HandlerDict { get; protected set; } + /// + /// 终端通用应答 + /// 平台无需回复 + /// 实现自己的业务 + /// + /// + /// + public virtual IJT808Reply Msg0x0001(JT808Request request) + { + return null; + } + /// + /// 终端心跳 + /// + /// + /// + public virtual IJT808Reply Msg0x0002(JT808Request request) + { + sessionManager.Heartbeat(request.Package.Header.TerminalPhoneNo); + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 终端注销 + /// + /// + /// + public virtual IJT808Reply Msg0x0003(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 终端注册 + /// + /// + /// + public virtual IJT808Reply Msg0x0100(JT808Request request) + { + return new JT808Response(JT808MsgId.终端注册应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8100() + { + Code = "J" + request.Package.Header.TerminalPhoneNo, + JT808TerminalRegisterResult = JT808TerminalRegisterResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 终端鉴权 + /// + /// + /// + public virtual IJT808Reply Msg0x0102(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 位置信息汇报 + /// + /// + /// + public virtual IJT808Reply Msg0x0200(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 定位数据批量上传 + /// + /// + /// + public virtual IJT808Reply Msg0x0704(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId =request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 数据上行透传 + /// + /// + /// + public virtual IJT808Reply Msg0x0900(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId =request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + } +} diff --git a/src/JT808.DotNetty.Core/Handlers/JT808MsgIdUdpHandlerBase.cs b/src/JT808.DotNetty.Core/Handlers/JT808MsgIdUdpHandlerBase.cs new file mode 100644 index 0000000..19a072b --- /dev/null +++ b/src/JT808.DotNetty.Core/Handlers/JT808MsgIdUdpHandlerBase.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using JT808.Protocol.Enums; +using JT808.Protocol.Extensions; +using JT808.Protocol.MessageBody; + +namespace JT808.DotNetty.Core.Handlers +{ + /// + /// 基于Udp模式的抽象消息处理业务 + /// 自定义消息处理业务 + /// 注意: + /// 1.ConfigureServices: + /// services.Replace(new ServiceDescriptor(typeof(JT808MsgIdUdpHandlerBase),typeof(JT808MsgIdCustomUdpHandlerImpl),ServiceLifetime.Singleton)); + /// 2.解析具体的消息体,具体消息调用具体的JT808Serializer.Deserialize + /// + public abstract class JT808MsgIdUdpHandlerBase + { + protected JT808UdpSessionManager sessionManager { get; } + /// + /// 初始化消息处理业务 + /// + protected JT808MsgIdUdpHandlerBase(JT808UdpSessionManager sessionManager) + { + this.sessionManager = sessionManager; + HandlerDict = new Dictionary> + { + {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 Dictionary> HandlerDict { get; protected set; } + /// + /// 终端通用应答 + /// 平台无需回复 + /// 实现自己的业务 + /// + /// + /// + public virtual IJT808Reply Msg0x0001(JT808Request request) + { + return null; + } + /// + /// 终端心跳 + /// + /// + /// + public virtual IJT808Reply Msg0x0002(JT808Request request) + { + sessionManager.Heartbeat(request.Package.Header.TerminalPhoneNo); + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 终端注销 + /// + /// + /// + public virtual IJT808Reply Msg0x0003(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 终端注册 + /// + /// + /// + public virtual IJT808Reply Msg0x0100(JT808Request request) + { + return new JT808Response(JT808MsgId.终端注册应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8100() + { + Code = "J" + request.Package.Header.TerminalPhoneNo, + JT808TerminalRegisterResult = JT808TerminalRegisterResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 终端鉴权 + /// + /// + /// + public virtual IJT808Reply Msg0x0102(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 位置信息汇报 + /// + /// + /// + /// + public virtual IJT808Reply Msg0x0200(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId = request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 定位数据批量上传 + /// + /// + /// + public virtual IJT808Reply Msg0x0704(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId =request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + /// + /// 数据上行透传 + /// + /// + /// + public virtual IJT808Reply Msg0x0900(JT808Request request) + { + return new JT808Response(JT808MsgId.平台通用应答.Create(request.Package.Header.TerminalPhoneNo, new JT808_0x8001() + { + MsgId =request.Package.Header.MsgId, + JT808PlatformResult = JT808PlatformResult.成功, + MsgNum = request.Package.Header.MsgNum + })); + } + } +} diff --git a/src/JT808.DotNetty.Core/Impls/JT808DatagramPacketImpl.cs b/src/JT808.DotNetty.Core/Impls/JT808DatagramPacketImpl.cs new file mode 100644 index 0000000..71421f2 --- /dev/null +++ b/src/JT808.DotNetty.Core/Impls/JT808DatagramPacketImpl.cs @@ -0,0 +1,33 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels.Sockets; +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Services; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT808.DotNetty.Core.Impls +{ + class JT808DatagramPacketImpl : IJT808DatagramPacket + { + private readonly JT808TrafficService jT808TrafficService; + private readonly IJT808DownlinkPacket jT808DownlinkPacket; + public JT808DatagramPacketImpl( + JT808TrafficServiceFactory jT808TrafficServiceFactory, + IJT808DownlinkPacket jT808DownlinkPacket) + { + this.jT808DownlinkPacket = jT808DownlinkPacket; + this.jT808TrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.udp); + } + + public DatagramPacket Create(byte[] message, EndPoint recipient) + { + jT808TrafficService.SendSize(message.Length); + jT808DownlinkPacket.ProcessorAsync(message, JT808TransportProtocolType.udp); + return new DatagramPacket(Unpooled.WrappedBuffer(message), recipient); + } + } +} diff --git a/src/JT808.DotNetty.Core/Impls/JT808DownlinkPacketEmptyImpl.cs b/src/JT808.DotNetty.Core/Impls/JT808DownlinkPacketEmptyImpl.cs new file mode 100644 index 0000000..7ba5593 --- /dev/null +++ b/src/JT808.DotNetty.Core/Impls/JT808DownlinkPacketEmptyImpl.cs @@ -0,0 +1,17 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Abstractions.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Impls +{ + class JT808DownlinkPacketEmptyImpl : IJT808DownlinkPacket + { + public Task ProcessorAsync(byte[] data, JT808TransportProtocolType transportProtocolType) + { + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Core/Impls/JT808NettyBuilderDefault.cs b/src/JT808.DotNetty.Core/Impls/JT808NettyBuilderDefault.cs new file mode 100644 index 0000000..237bb6d --- /dev/null +++ b/src/JT808.DotNetty.Core/Impls/JT808NettyBuilderDefault.cs @@ -0,0 +1,56 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Core.Interfaces; +using JT808.Protocol; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Impls +{ + public class JT808NettyBuilderDefault : IJT808NettyBuilder + { + public IJT808Builder JT808Builder { get; } + + public JT808NettyBuilderDefault(IJT808Builder builder) + { + JT808Builder = builder; + } + + public IJT808NettyBuilder Replace() where T : IJT808SourcePackageDispatcher + { + JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808SourcePackageDispatcher), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808Builder Builder() + { + return JT808Builder; + } + + public IJT808NettyBuilder ReplaceSourcePackageDispatcher() where T : IJT808SourcePackageDispatcher + { + JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808SourcePackageDispatcher), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808NettyBuilder ReplaceDownlinkPacket() where T : IJT808DownlinkPacket + { + JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808DownlinkPacket), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808NettyBuilder ReplaceUplinkPacket() where T : IJT808UplinkPacket + { + JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808UplinkPacket), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808NettyBuilder ReplaceSessionPublishing() where T : IJT808SessionPublishing + { + JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808SessionPublishing), typeof(T), ServiceLifetime.Singleton)); + return this; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Impls/JT808SessionPublishingEmptyImpl.cs b/src/JT808.DotNetty.Core/Impls/JT808SessionPublishingEmptyImpl.cs new file mode 100644 index 0000000..af7aa6c --- /dev/null +++ b/src/JT808.DotNetty.Core/Impls/JT808SessionPublishingEmptyImpl.cs @@ -0,0 +1,13 @@ +using JT808.DotNetty.Abstractions; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core +{ + internal class JT808SessionPublishingEmptyImpl : IJT808SessionPublishing + { + public Task PublishAsync(string topicName, string value) + { + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Core/Impls/JT808SourcePackageDispatcherEmptyImpl.cs b/src/JT808.DotNetty.Core/Impls/JT808SourcePackageDispatcherEmptyImpl.cs new file mode 100644 index 0000000..f17ae75 --- /dev/null +++ b/src/JT808.DotNetty.Core/Impls/JT808SourcePackageDispatcherEmptyImpl.cs @@ -0,0 +1,16 @@ +using JT808.DotNetty.Abstractions; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Impls +{ + /// + /// 原包分发器默认空实现 + /// + public class JT808SourcePackageDispatcherEmptyImpl : IJT808SourcePackageDispatcher + { + public Task SendAsync(byte[] data) + { + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Core/Impls/JT808UplinkPacketEmptyImpl.cs b/src/JT808.DotNetty.Core/Impls/JT808UplinkPacketEmptyImpl.cs new file mode 100644 index 0000000..11a1b04 --- /dev/null +++ b/src/JT808.DotNetty.Core/Impls/JT808UplinkPacketEmptyImpl.cs @@ -0,0 +1,17 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Abstractions.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Impls +{ + class JT808UplinkPacketEmptyImpl : IJT808UplinkPacket + { + public Task ProcessorAsync(byte[] data, JT808TransportProtocolType transportProtocolType) + { + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808DatagramPacket.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808DatagramPacket.cs new file mode 100644 index 0000000..70e2a4b --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808DatagramPacket.cs @@ -0,0 +1,13 @@ +using DotNetty.Transport.Channels.Sockets; +using System.Net; + +namespace JT808.DotNetty.Core.Interfaces +{ + /// + /// 基于udp的创建发送包 + /// + interface IJT808DatagramPacket + { + DatagramPacket Create(byte[] message, EndPoint recipient); + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808NettyBuilder.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808NettyBuilder.cs new file mode 100644 index 0000000..5f1600d --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808NettyBuilder.cs @@ -0,0 +1,19 @@ +using JT808.DotNetty.Abstractions; +using JT808.Protocol; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808NettyBuilder + { + IJT808Builder JT808Builder { get; } + IJT808NettyBuilder ReplaceSourcePackageDispatcher() where T : IJT808SourcePackageDispatcher; + IJT808NettyBuilder ReplaceDownlinkPacket() where T: IJT808DownlinkPacket; + IJT808NettyBuilder ReplaceUplinkPacket() where T : IJT808UplinkPacket; + IJT808NettyBuilder ReplaceSessionPublishing() where T : IJT808SessionPublishing; + IJT808Builder Builder(); + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808Reply.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808Reply.cs new file mode 100644 index 0000000..99a2a74 --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808Reply.cs @@ -0,0 +1,17 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808Reply + { + JT808Package Package { get; set; } + byte[] HexData { get; set; } + /// + /// 根据实际情况适当调整包的大小 + /// + int MinBufferSize { get; set; } + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808TcpCustomMsgIdHandler.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808TcpCustomMsgIdHandler.cs new file mode 100644 index 0000000..42fb845 --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808TcpCustomMsgIdHandler.cs @@ -0,0 +1,20 @@ +using JT808.DotNetty.Core.Metadata; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808TcpCustomMsgIdHandler + { + IJT808Reply Proccesser(JT808Request request); + } + + public class JT808TcpCustomMsgIdHandlerEmpty : IJT808TcpCustomMsgIdHandler + { + public IJT808Reply Proccesser(JT808Request request) + { + return default; + } + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808TcpNettyBuilder.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808TcpNettyBuilder.cs new file mode 100644 index 0000000..0e67228 --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808TcpNettyBuilder.cs @@ -0,0 +1,17 @@ +using JT808.DotNetty.Core.Handlers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808TcpNettyBuilder + { + IJT808NettyBuilder Instance { get; } + IJT808NettyBuilder Builder(); + IJT808TcpNettyBuilder ReplaceCustomMsgIdHandler() where T : IJT808TcpCustomMsgIdHandler; + IJT808TcpNettyBuilder ReplaceMsgIdHandler() where T : JT808MsgIdTcpHandlerBase; + IJT808TcpNettyBuilder ReplaceSessionService() where T : IJT808TcpSessionService; + IJT808TcpNettyBuilder ReplaceUnificationSendService() where T : IJT808UnificationTcpSendService; + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808TcpSessionService.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808TcpSessionService.cs new file mode 100644 index 0000000..e18982b --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808TcpSessionService.cs @@ -0,0 +1,25 @@ +using JT808.DotNetty.Abstractions.Dtos; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + /// + /// JT808 Tcp会话服务 + /// + public interface IJT808TcpSessionService + { + /// + /// 获取会话集合 + /// + /// + JT808ResultDto> GetAll(); + /// + /// 通过设备终端号移除对应会话 + /// + /// + /// + JT808ResultDto RemoveByTerminalPhoneNo(string terminalPhoneNo); + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808UdpCustomMsgIdHandler.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808UdpCustomMsgIdHandler.cs new file mode 100644 index 0000000..f9c4698 --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808UdpCustomMsgIdHandler.cs @@ -0,0 +1,20 @@ +using JT808.DotNetty.Core.Metadata; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808UdpCustomMsgIdHandler + { + IJT808Reply Proccesser(JT808Request request); + } + + public class JT808UdpCustomMsgIdHandlerEmpty : IJT808UdpCustomMsgIdHandler + { + public IJT808Reply Proccesser(JT808Request request) + { + return default; + } + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808UdpNettyBuilder.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808UdpNettyBuilder.cs new file mode 100644 index 0000000..8efc02b --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808UdpNettyBuilder.cs @@ -0,0 +1,17 @@ +using JT808.DotNetty.Core.Handlers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808UdpNettyBuilder + { + IJT808NettyBuilder Instance { get; } + IJT808NettyBuilder Builder(); + IJT808UdpNettyBuilder ReplaceCustomMsgIdHandler() where T : IJT808UdpCustomMsgIdHandler; + IJT808UdpNettyBuilder ReplaceMsgIdHandler() where T : JT808MsgIdUdpHandlerBase; + IJT808UdpNettyBuilder ReplaceSessionService() where T : IJT808UdpSessionService; + IJT808UdpNettyBuilder ReplaceUnificationSendService() where T : IJT808UnificationUdpSendService; + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808UdpSessionService.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808UdpSessionService.cs new file mode 100644 index 0000000..8e9052c --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808UdpSessionService.cs @@ -0,0 +1,25 @@ +using JT808.DotNetty.Abstractions.Dtos; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + /// + /// JT808 Udp会话服务 + /// + public interface IJT808UdpSessionService + { + /// + /// 获取会话集合 + /// + /// + JT808ResultDto> GetAll(); + /// + /// 通过设备终端号移除对应会话 + /// + /// + /// + JT808ResultDto RemoveByTerminalPhoneNo(string terminalPhoneNo); + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808UnificationTcpSendService.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808UnificationTcpSendService.cs new file mode 100644 index 0000000..528edcf --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808UnificationTcpSendService.cs @@ -0,0 +1,12 @@ +using JT808.DotNetty.Abstractions.Dtos; + +namespace JT808.DotNetty.Core.Interfaces +{ + /// + /// JT808基于tcp的统一下发命令服务 + /// + public interface IJT808UnificationTcpSendService + { + JT808ResultDto Send(string terminalPhoneNo, byte[] data); + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808UnificationUdpSendService.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808UnificationUdpSendService.cs new file mode 100644 index 0000000..fffbd94 --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808UnificationUdpSendService.cs @@ -0,0 +1,12 @@ +using JT808.DotNetty.Abstractions.Dtos; + +namespace JT808.DotNetty.Core.Interfaces +{ + /// + /// JT808基于udp的统一下发命令服务 + /// + public interface IJT808UnificationUdpSendService + { + JT808ResultDto Send(string terminalPhoneNo, byte[] data); + } +} diff --git a/src/JT808.DotNetty.Core/Interfaces/IJT808WebApiNettyBuilder.cs b/src/JT808.DotNetty.Core/Interfaces/IJT808WebApiNettyBuilder.cs new file mode 100644 index 0000000..09bf34a --- /dev/null +++ b/src/JT808.DotNetty.Core/Interfaces/IJT808WebApiNettyBuilder.cs @@ -0,0 +1,14 @@ +using JT808.DotNetty.Core.Handlers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Interfaces +{ + public interface IJT808WebApiNettyBuilder + { + IJT808NettyBuilder Instance { get; } + IJT808NettyBuilder Builder(); + IJT808WebApiNettyBuilder ReplaceMsgIdHandler() where T : JT808MsgIdHttpHandlerBase; + } +} diff --git a/src/JT808.DotNetty.Core/JT808.DotNetty.Core.csproj b/src/JT808.DotNetty.Core/JT808.DotNetty.Core.csproj new file mode 100644 index 0000000..d538217 --- /dev/null +++ b/src/JT808.DotNetty.Core/JT808.DotNetty.Core.csproj @@ -0,0 +1,29 @@ + + + + JT808.DotNetty.Core + JT808.DotNetty.Core + 基于DotNetty实现的JT808DotNetty的核心库 + 基于DotNetty实现的JT808DotNetty的核心库 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/JT808.DotNetty.Core/JT808BackgroundService.cs b/src/JT808.DotNetty.Core/JT808BackgroundService.cs new file mode 100644 index 0000000..cda2139 --- /dev/null +++ b/src/JT808.DotNetty.Core/JT808BackgroundService.cs @@ -0,0 +1,74 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core +{ + /// + /// + /// + /// + public abstract class JT808BackgroundService : IHostedService, IDisposable + { + /// + /// 默认次日过期 + /// + public virtual TimeSpan DelayTimeSpan + { + get + { + DateTime current = DateTime.Now; + DateTime tmp = current.Date.AddDays(1).AddSeconds(-1); + return tmp.Subtract(current); + } + set { } + } + + private Task _executingTask; + + public abstract string ServiceName { get; } + + private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource(); + + protected abstract Task ExecuteAsync(CancellationToken stoppingToken); + + public void Dispose() + { + _stoppingCts.Cancel(); + } + + public virtual Task StartAsync(CancellationToken cancellationToken) + { + // Store the task we're executing + _executingTask = ExecuteAsync(_stoppingCts.Token); + // If the task is completed then return it, + // this will bubble cancellation and failure to the caller + if (_executingTask.IsCompleted) + { + return _executingTask; + } + // Otherwise it's running + return Task.CompletedTask; + } + + public virtual async Task StopAsync(CancellationToken cancellationToken) + { + // Stop called without start + if (_executingTask == null) + { + return; + } + try + { + // Signal cancellation to the executing method + _stoppingCts.Cancel(); + } + finally + { + // Wait until the task completes or the stop token triggers + await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,cancellationToken)); + } + } + } +} diff --git a/src/JT808.DotNetty.Core/JT808CoreDotnettyExtensions.cs b/src/JT808.DotNetty.Core/JT808CoreDotnettyExtensions.cs new file mode 100644 index 0000000..818fb72 --- /dev/null +++ b/src/JT808.DotNetty.Core/JT808CoreDotnettyExtensions.cs @@ -0,0 +1,96 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Core.Configurations; +using JT808.DotNetty.Core.Converters; +using JT808.DotNetty.Core.Impls; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Services; +using JT808.DotNetty.Internal; +using JT808.Protocol; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("JT808.DotNetty.Core.Test")] +[assembly: InternalsVisibleTo("JT808.DotNetty.Tcp.Test")] +[assembly: InternalsVisibleTo("JT808.DotNetty.Udp.Test")] +[assembly: InternalsVisibleTo("JT808.DotNetty.WebApi.Test")] +[assembly: InternalsVisibleTo("JT808.DotNetty.Tcp")] +[assembly: InternalsVisibleTo("JT808.DotNetty.Udp")] +[assembly: InternalsVisibleTo("JT808.DotNetty.WebApi")] +namespace JT808.DotNetty.Core +{ + public static class JT808CoreDotnettyExtensions + { + static JT808CoreDotnettyExtensions() + { + JsonConvert.DefaultSettings = new Func(() => + { + Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings(); + //日期类型默认格式化处理 + settings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat; + settings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + //空值处理 + settings.NullValueHandling = NullValueHandling.Ignore; + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + settings.Converters.Add(new JsonIPAddressConverter()); + settings.Converters.Add(new JsonIPEndPointConverter()); + settings.Converters.Add(new ByteArrayHexConverter()); + return settings; + }); + } + + public static IJT808NettyBuilder AddJT808NettyCore(this IJT808Builder jt808Builder, IConfiguration configuration, Newtonsoft.Json.JsonSerializerSettings settings=null) + { + if (settings != null) + { + JsonConvert.DefaultSettings = new Func(() => + { + settings.Converters.Add(new JsonIPAddressConverter()); + settings.Converters.Add(new JsonIPEndPointConverter()); + settings.Converters.Add(new ByteArrayHexConverter()); + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + return settings; + }); + } + IJT808NettyBuilder nettyBuilder = new JT808NettyBuilderDefault(jt808Builder); + nettyBuilder.JT808Builder.Services.Configure(configuration.GetSection("JT808Configuration")); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + return nettyBuilder; + } + + public static IJT808NettyBuilder AddJT808NettyCore(this IJT808Builder jt808Builder, Action jt808Options, Newtonsoft.Json.JsonSerializerSettings settings = null) + { + if (settings != null) + { + JsonConvert.DefaultSettings = new Func(() => + { + settings.Converters.Add(new JsonIPAddressConverter()); + settings.Converters.Add(new JsonIPEndPointConverter()); + settings.Converters.Add(new ByteArrayHexConverter()); + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + return settings; + }); + } + IJT808NettyBuilder nettyBuilder = new JT808NettyBuilderDefault(jt808Builder); + nettyBuilder.JT808Builder.Services.Configure(jt808Options); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + nettyBuilder.JT808Builder.Services.TryAddSingleton(); + return nettyBuilder; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/JT808SimpleTcpClient.cs b/src/JT808.DotNetty.Core/JT808SimpleTcpClient.cs new file mode 100644 index 0000000..8887a27 --- /dev/null +++ b/src/JT808.DotNetty.Core/JT808SimpleTcpClient.cs @@ -0,0 +1,50 @@ +using System; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core +{ + internal class JT808SimpleTcpClient + { + private TcpClient tcpClient; + + public JT808SimpleTcpClient(IPEndPoint remoteAddress) + { + tcpClient = new TcpClient(); + tcpClient.Connect(remoteAddress); + Task.Run(()=> { + while (true) + { + try + { + byte[] buffer = new byte[100]; + tcpClient.GetStream().Read(buffer, 0, 100); + Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + string.Join(" ", buffer)); + + } + catch + { + + + } + Thread.Sleep(1000); + } + }); + } + + + + public void WriteAsync(byte[] data) + { + tcpClient.GetStream().WriteAsync(data, 0, data.Length); + } + + public void Down() + { + tcpClient.Close(); + } + } +} diff --git a/src/JT808.DotNetty.Core/JT808SimpleUdpClient.cs b/src/JT808.DotNetty.Core/JT808SimpleUdpClient.cs new file mode 100644 index 0000000..568658f --- /dev/null +++ b/src/JT808.DotNetty.Core/JT808SimpleUdpClient.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core +{ + internal class JT808SimpleUdpClient + { + private UdpClient udpClient; + + public JT808SimpleUdpClient(IPEndPoint remoteAddress) + { + udpClient = new UdpClient(); + udpClient.Connect(remoteAddress); + Task.Run(() => + { + while (true) + { + try + { + string tmp = string.Join(" ", udpClient.Receive(ref remoteAddress)); + Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + tmp); + Thread.Sleep(1000); + } + catch + { + + + } + Thread.Sleep(1000); + } + }); + } + + public void WriteAsync(byte[] data) + { + udpClient.SendAsync(data, data.Length); + } + + public void Down() + { + udpClient.Close(); + } + } +} diff --git a/src/JT808.DotNetty.Core/Jobs/JT808TcpAtomicCouterResetDailyJob.cs b/src/JT808.DotNetty.Core/Jobs/JT808TcpAtomicCouterResetDailyJob.cs new file mode 100644 index 0000000..54b438b --- /dev/null +++ b/src/JT808.DotNetty.Core/Jobs/JT808TcpAtomicCouterResetDailyJob.cs @@ -0,0 +1,38 @@ +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Services; +using Microsoft.Extensions.Logging; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Jobs +{ + internal class JT808TcpAtomicCouterResetDailyJob : JT808BackgroundService + { + private readonly ILogger _logger; + + private readonly JT808AtomicCounterService _jT808AtomicCounterService; + + public JT808TcpAtomicCouterResetDailyJob( + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory, + ILoggerFactory loggerFactory) + { + _jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.tcp); + _logger =loggerFactory.CreateLogger(); + } + + public override string ServiceName => nameof(JT808TcpAtomicCouterResetDailyJob); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation($"{ServiceName} is starting."); + stoppingToken.Register(() => _logger.LogInformation($"{ServiceName} background task is stopping.")); + while (!stoppingToken.IsCancellationRequested) + { + _logger.LogInformation($"{ServiceName} task doing background work."); + _jT808AtomicCounterService.Reset(); + await Task.Delay(DelayTimeSpan, stoppingToken); + } + _logger.LogInformation($"{ServiceName} background task is stopping."); + } + } +} diff --git a/src/JT808.DotNetty.Core/Jobs/JT808TcpTrafficResetDailyJob.cs b/src/JT808.DotNetty.Core/Jobs/JT808TcpTrafficResetDailyJob.cs new file mode 100644 index 0000000..5e5dcad --- /dev/null +++ b/src/JT808.DotNetty.Core/Jobs/JT808TcpTrafficResetDailyJob.cs @@ -0,0 +1,38 @@ +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Services; +using Microsoft.Extensions.Logging; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Jobs +{ + internal class JT808TcpTrafficResetDailyJob : JT808BackgroundService + { + private readonly ILogger _logger; + + private readonly JT808TrafficService _jT808TrafficService; + + public JT808TcpTrafficResetDailyJob( + JT808TrafficServiceFactory jT808TrafficServiceFactory, + ILoggerFactory loggerFactory) + { + _jT808TrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.tcp); + _logger =loggerFactory.CreateLogger(); + } + + public override string ServiceName => nameof(JT808TcpTrafficResetDailyJob); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation($"{ServiceName} is starting."); + stoppingToken.Register(() => _logger.LogInformation($"{ServiceName} background task is stopping.")); + while (!stoppingToken.IsCancellationRequested) + { + _logger.LogInformation($"{ServiceName} task doing background work."); + _jT808TrafficService.ResetSize(); + await Task.Delay(DelayTimeSpan, stoppingToken); + } + _logger.LogInformation($"{ServiceName} background task is stopping."); + } + } +} diff --git a/src/JT808.DotNetty.Core/Jobs/JT808UdpAtomicCouterResetDailyJob.cs b/src/JT808.DotNetty.Core/Jobs/JT808UdpAtomicCouterResetDailyJob.cs new file mode 100644 index 0000000..1cd668f --- /dev/null +++ b/src/JT808.DotNetty.Core/Jobs/JT808UdpAtomicCouterResetDailyJob.cs @@ -0,0 +1,38 @@ +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Services; +using Microsoft.Extensions.Logging; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Jobs +{ + internal class JT808UdpAtomicCouterResetDailyJob : JT808BackgroundService + { + private readonly ILogger _logger; + + private readonly JT808AtomicCounterService _jT808AtomicCounterService; + + public JT808UdpAtomicCouterResetDailyJob( + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory, + ILoggerFactory loggerFactory) + { + _jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.udp); + _logger =loggerFactory.CreateLogger(); + } + + public override string ServiceName => nameof(JT808UdpAtomicCouterResetDailyJob); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation($"{ServiceName} is starting."); + stoppingToken.Register(() => _logger.LogInformation($"{ServiceName} background task is stopping.")); + while (!stoppingToken.IsCancellationRequested) + { + _logger.LogInformation($"{ServiceName} task doing background work."); + _jT808AtomicCounterService.Reset(); + await Task.Delay(DelayTimeSpan, stoppingToken); + } + _logger.LogInformation($"{ServiceName} background task is stopping."); + } + } +} diff --git a/src/JT808.DotNetty.Core/Jobs/JT808UdpTrafficResetDailyJob.cs b/src/JT808.DotNetty.Core/Jobs/JT808UdpTrafficResetDailyJob.cs new file mode 100644 index 0000000..620a80f --- /dev/null +++ b/src/JT808.DotNetty.Core/Jobs/JT808UdpTrafficResetDailyJob.cs @@ -0,0 +1,38 @@ +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Services; +using Microsoft.Extensions.Logging; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Core.Jobs +{ + internal class JT808UdpTrafficResetDailyJob : JT808BackgroundService + { + private readonly ILogger _logger; + + private readonly JT808TrafficService _jT808TrafficService; + + public JT808UdpTrafficResetDailyJob( + JT808TrafficServiceFactory jT808TrafficServiceFactory, + ILoggerFactory loggerFactory) + { + _jT808TrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.udp); + _logger =loggerFactory.CreateLogger(); + } + + public override string ServiceName => nameof(JT808UdpTrafficResetDailyJob); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation($"{ServiceName} is starting."); + stoppingToken.Register(() => _logger.LogInformation($"{ServiceName} background task is stopping.")); + while (!stoppingToken.IsCancellationRequested) + { + _logger.LogInformation($"{ServiceName} task doing background work."); + _jT808TrafficService.ResetSize(); + await Task.Delay(DelayTimeSpan, stoppingToken); + } + _logger.LogInformation($"{ServiceName} background task is stopping."); + } + } +} diff --git a/src/JT808.DotNetty.Core/Metadata/JT808AtomicCounter.cs b/src/JT808.DotNetty.Core/Metadata/JT808AtomicCounter.cs new file mode 100644 index 0000000..25b8cca --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808AtomicCounter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace JT808.DotNetty.Core.Metadata +{ + /// + /// + /// + /// + internal class JT808AtomicCounter + { + long counter = 0; + + public JT808AtomicCounter(long initialCount = 0) + { + this.counter = initialCount; + } + + public void Reset() + { + Interlocked.Exchange(ref counter, 0); + } + + public long Increment() + { + return Interlocked.Increment(ref counter); + } + + public long Add(long len) + { + return Interlocked.Add(ref counter,len); + } + + public long Decrement() + { + return Interlocked.Decrement(ref counter); + } + + public long Count + { + get + { + return Interlocked.Read(ref counter); + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Metadata/JT808HttpRequest.cs b/src/JT808.DotNetty.Core/Metadata/JT808HttpRequest.cs new file mode 100644 index 0000000..274226b --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808HttpRequest.cs @@ -0,0 +1,22 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808HttpRequest + { + public string Json { get; set; } + + public JT808HttpRequest() + { + + } + + public JT808HttpRequest(string json) + { + Json = json; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Metadata/JT808HttpResponse.cs b/src/JT808.DotNetty.Core/Metadata/JT808HttpResponse.cs new file mode 100644 index 0000000..3bd3379 --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808HttpResponse.cs @@ -0,0 +1,22 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808HttpResponse + { + public byte[] Data { get; set; } + + public JT808HttpResponse() + { + + } + + public JT808HttpResponse(byte[] data) + { + this.Data = data; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Metadata/JT808Request.cs b/src/JT808.DotNetty.Core/Metadata/JT808Request.cs new file mode 100644 index 0000000..84aa29e --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808Request.cs @@ -0,0 +1,23 @@ +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808Request + { + public JT808HeaderPackage Package { get; } + + /// + /// 用于消息发送 + /// + public byte[] OriginalPackage { get;} + + public JT808Request(JT808HeaderPackage package, byte[] originalPackage) + { + Package = package; + OriginalPackage = originalPackage; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Metadata/JT808Response.cs b/src/JT808.DotNetty.Core/Metadata/JT808Response.cs new file mode 100644 index 0000000..ef75f3c --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808Response.cs @@ -0,0 +1,31 @@ +using JT808.DotNetty.Core.Interfaces; +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808Response: IJT808Reply + { + public JT808Package Package { get; set; } + public byte[] HexData { get; set; } + public int MinBufferSize { get; set; } + + public JT808Response() + { + + } + + public JT808Response(JT808Package package, int minBufferSize = 1024) + { + Package = package; + MinBufferSize = minBufferSize; + } + + public JT808Response(byte[] hexData) + { + HexData = hexData; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Metadata/JT808TcpSession.cs b/src/JT808.DotNetty.Core/Metadata/JT808TcpSession.cs new file mode 100644 index 0000000..6d0e651 --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808TcpSession.cs @@ -0,0 +1,29 @@ +using DotNetty.Transport.Channels; +using System; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808TcpSession + { + public JT808TcpSession(IChannel channel, string terminalPhoneNo) + { + Channel = channel; + TerminalPhoneNo = terminalPhoneNo; + StartTime = DateTime.Now; + LastActiveTime = DateTime.Now; + } + + public JT808TcpSession() { } + + /// + /// 终端手机号 + /// + public string TerminalPhoneNo { get; set; } + + public IChannel Channel { get; set; } + + public DateTime LastActiveTime { get; set; } + + public DateTime StartTime { get; set; } + } +} diff --git a/src/JT808.DotNetty.Core/Metadata/JT808UdpPackage.cs b/src/JT808.DotNetty.Core/Metadata/JT808UdpPackage.cs new file mode 100644 index 0000000..939f93f --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808UdpPackage.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808UdpPackage + { + public JT808UdpPackage(byte[] buffer, EndPoint sender) + { + Buffer = buffer; + Sender = sender; + } + + public byte[] Buffer { get; } + + public EndPoint Sender { get; } + } +} diff --git a/src/JT808.DotNetty.Core/Metadata/JT808UdpSession.cs b/src/JT808.DotNetty.Core/Metadata/JT808UdpSession.cs new file mode 100644 index 0000000..7984202 --- /dev/null +++ b/src/JT808.DotNetty.Core/Metadata/JT808UdpSession.cs @@ -0,0 +1,35 @@ +using DotNetty.Transport.Channels; +using System; +using System.Net; + +namespace JT808.DotNetty.Core.Metadata +{ + public class JT808UdpSession + { + public JT808UdpSession(IChannel channel, + EndPoint sender, + string terminalPhoneNo) + { + Channel = channel; + TerminalPhoneNo = terminalPhoneNo; + StartTime = DateTime.Now; + LastActiveTime = DateTime.Now; + Sender = sender; + } + + public EndPoint Sender { get; set; } + + public JT808UdpSession() { } + + /// + /// 终端手机号 + /// + public string TerminalPhoneNo { get; set; } + + public IChannel Channel { get; set; } + + public DateTime LastActiveTime { get; set; } + + public DateTime StartTime { get; set; } + } +} diff --git a/src/JT808.DotNetty.Core/Properties/PublishProfiles/FolderProfile.pubxml b/src/JT808.DotNetty.Core/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..e572661 --- /dev/null +++ b/src/JT808.DotNetty.Core/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + FileSystem + Release + Any CPU + netstandard2.0 + ..\..\nupkgs + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Properties/PublishProfiles/FolderProfile.pubxml.user b/src/JT808.DotNetty.Core/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/src/JT808.DotNetty.Core/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Core/Services/JT808AtomicCounterService.cs b/src/JT808.DotNetty.Core/Services/JT808AtomicCounterService.cs new file mode 100644 index 0000000..85cf0d5 --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808AtomicCounterService.cs @@ -0,0 +1,52 @@ +using JT808.DotNetty.Core.Metadata; + +namespace JT808.DotNetty.Core.Services +{ + /// + /// 计数包服务 + /// + public class JT808AtomicCounterService + { + private readonly JT808AtomicCounter MsgSuccessCounter; + + private readonly JT808AtomicCounter MsgFailCounter; + + public JT808AtomicCounterService() + { + MsgSuccessCounter=new JT808AtomicCounter(); + MsgFailCounter = new JT808AtomicCounter(); + } + + public void Reset() + { + MsgSuccessCounter.Reset(); + MsgFailCounter.Reset(); + } + + public long MsgSuccessIncrement() + { + return MsgSuccessCounter.Increment(); + } + + public long MsgSuccessCount + { + get + { + return MsgSuccessCounter.Count; + } + } + + public long MsgFailIncrement() + { + return MsgFailCounter.Increment(); + } + + public long MsgFailCount + { + get + { + return MsgFailCounter.Count; + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808AtomicCounterServiceFactory.cs b/src/JT808.DotNetty.Core/Services/JT808AtomicCounterServiceFactory.cs new file mode 100644 index 0000000..916befb --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808AtomicCounterServiceFactory.cs @@ -0,0 +1,30 @@ +using JT808.DotNetty.Abstractions.Enums; +using System; +using System.Collections.Concurrent; + +namespace JT808.DotNetty.Core.Services +{ + public class JT808AtomicCounterServiceFactory + { + private readonly ConcurrentDictionary cache; + + public JT808AtomicCounterServiceFactory() + { + cache = new ConcurrentDictionary(); + } + + public JT808AtomicCounterService Create(JT808TransportProtocolType type) + { + if(cache.TryGetValue(type,out var service)) + { + return service; + } + else + { + var serviceNew = new JT808AtomicCounterService(); + cache.TryAdd(type, serviceNew); + return serviceNew; + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808SimpleSystemCollectService.cs b/src/JT808.DotNetty.Core/Services/JT808SimpleSystemCollectService.cs new file mode 100644 index 0000000..2482cff --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808SimpleSystemCollectService.cs @@ -0,0 +1,30 @@ +using JT808.DotNetty.Abstractions.Dtos; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace JT808.DotNetty.Core.Services +{ + /// + /// 简单系统收集服务 + /// + public class JT808SimpleSystemCollectService + { + /// + /// 获取系统当前进程使用情况 + /// + /// + public JT808SystemCollectInfoDto Get() + { + JT808SystemCollectInfoDto jT808SystemCollectInfoDto = new JT808SystemCollectInfoDto(); + var proc = Process.GetCurrentProcess(); + jT808SystemCollectInfoDto.ProcessId = proc.Id; + jT808SystemCollectInfoDto.WorkingSet64 = proc.WorkingSet64 / 1024.0 / 1024.0; + jT808SystemCollectInfoDto.PeakWorkingSet64 = proc.PeakWorkingSet64 / 1024.0 / 1024.0; + jT808SystemCollectInfoDto.PrivateMemorySize64 = proc.PrivateMemorySize64 / 1024.0 / 1024.0; + jT808SystemCollectInfoDto.CPUTotalProcessorTime = proc.TotalProcessorTime; + return jT808SystemCollectInfoDto; + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808TcpSessionService.cs b/src/JT808.DotNetty.Core/Services/JT808TcpSessionService.cs new file mode 100644 index 0000000..7ff872d --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808TcpSessionService.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Core.Interfaces; + +namespace JT808.DotNetty.Core.Services +{ + internal class JT808TcpSessionService : IJT808TcpSessionService + { + private readonly JT808TcpSessionManager jT808SessionManager; + + public JT808TcpSessionService( + JT808TcpSessionManager jT808SessionManager) + { + this.jT808SessionManager = jT808SessionManager; + } + + public JT808ResultDto> GetAll() + { + JT808ResultDto> resultDto = new JT808ResultDto>(); + try + { + resultDto.Data = jT808SessionManager.GetAll().Select(s => new JT808TcpSessionInfoDto + { + LastActiveTime = s.LastActiveTime, + StartTime = s.StartTime, + TerminalPhoneNo = s.TerminalPhoneNo, + RemoteAddressIP = s.Channel.RemoteAddress.ToString(), + }).ToList(); + resultDto.Code = JT808ResultCode.Ok; + } + catch (Exception ex) + { + resultDto.Data = null; + resultDto.Code = JT808ResultCode.Error; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + return resultDto; + } + + public JT808ResultDto RemoveByTerminalPhoneNo(string terminalPhoneNo) + { + JT808ResultDto resultDto = new JT808ResultDto(); + try + { + var session = jT808SessionManager.RemoveSession(terminalPhoneNo); + if (session != null) + { + if(session.Channel.Open) + { + session.Channel.CloseAsync(); + } + } + resultDto.Code = JT808ResultCode.Ok; + resultDto.Data = true; + } + catch (AggregateException ex) + { + resultDto.Data = false; + resultDto.Code = 500; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + catch (Exception ex) + { + resultDto.Data = false; + resultDto.Code = JT808ResultCode.Error; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + return resultDto; + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808TrafficService.cs b/src/JT808.DotNetty.Core/Services/JT808TrafficService.cs new file mode 100644 index 0000000..6740ff8 --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808TrafficService.cs @@ -0,0 +1,46 @@ +using JT808.DotNetty.Core.Metadata; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Services +{ + public class JT808TrafficService + { + public JT808TrafficService() + { + receiveCounter = new JT808AtomicCounter(); + sendCounter = new JT808AtomicCounter(); + } + + private readonly JT808AtomicCounter receiveCounter; + + private readonly JT808AtomicCounter sendCounter; + + public void ReceiveSize(long size) + { + receiveCounter.Add(size); + } + + public void SendSize(long size) + { + sendCounter.Add(size); + } + + public long TotalReceiveSize + { + get { return receiveCounter.Count; } + } + + public long TotalSendSize + { + get { return sendCounter.Count; } + } + + public void ResetSize() + { + receiveCounter.Reset(); + sendCounter.Reset(); + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808TrafficServiceFactory.cs b/src/JT808.DotNetty.Core/Services/JT808TrafficServiceFactory.cs new file mode 100644 index 0000000..1e3bc3d --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808TrafficServiceFactory.cs @@ -0,0 +1,33 @@ +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Metadata; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Services +{ + public class JT808TrafficServiceFactory + { + private readonly ConcurrentDictionary cache; + + public JT808TrafficServiceFactory() + { + cache = new ConcurrentDictionary(); + } + + public JT808TrafficService Create(JT808TransportProtocolType type) + { + if (cache.TryGetValue(type, out var service)) + { + return service; + } + else + { + var serviceNew = new JT808TrafficService(); + cache.TryAdd(type, serviceNew); + return serviceNew; + } + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808TransmitAddressFilterService.cs b/src/JT808.DotNetty.Core/Services/JT808TransmitAddressFilterService.cs new file mode 100644 index 0000000..7fd1078 --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808TransmitAddressFilterService.cs @@ -0,0 +1,102 @@ +using JT808.DotNetty.Abstractions.Dtos; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using JT808.DotNetty.Core.Configurations; + +namespace JT808.DotNetty.Core.Services +{ + /// + /// JT808转发地址过滤服务 + /// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: + // 1.消息的序列化 + // 2.消息的下发 + // 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle + // 就跟神兽貔貅一样。。。 + /// + public class JT808TransmitAddressFilterService : IDisposable + { + private readonly IOptionsMonitor jT808ConfigurationOptionsMonitor; + + private ConcurrentDictionary ForwardingRemoteAddresssDict; + + private IDisposable jT808ConfigurationOptionsMonitorDisposable; + + public JT808TransmitAddressFilterService( + IOptionsMonitor jT808ConfigurationOptionsMonitor) + { + this.jT808ConfigurationOptionsMonitor = jT808ConfigurationOptionsMonitor; + ForwardingRemoteAddresssDict = new ConcurrentDictionary(); + InitForwardingRemoteAddress(jT808ConfigurationOptionsMonitor.CurrentValue.ForwardingRemoteIPAddress); + //OnChange 源码多播委托 + jT808ConfigurationOptionsMonitorDisposable = this.jT808ConfigurationOptionsMonitor.OnChange(options => + { + ChangeForwardingRemoteAddress(options.ForwardingRemoteIPAddress); + }); + } + + private void InitForwardingRemoteAddress(List jT808ClientConfigurations) + { + if (jT808ClientConfigurations != null && jT808ClientConfigurations.Count > 0) + { + foreach (var item in jT808ClientConfigurations) + { + ForwardingRemoteAddresssDict.TryAdd(item, 0); + } + } + } + + private void ChangeForwardingRemoteAddress(List jT808ClientConfigurations) + { + if (jT808ClientConfigurations != null && jT808ClientConfigurations.Count > 0) + { + ForwardingRemoteAddresssDict.Clear(); + foreach (var item in jT808ClientConfigurations) + { + ForwardingRemoteAddresssDict.TryAdd(item, 0); + } + } + else + { + ForwardingRemoteAddresssDict.Clear(); + } + } + + public bool ContainsKey(EndPoint endPoint) + { + IPAddress ip = ((IPEndPoint)endPoint).Address; + return ForwardingRemoteAddresssDict.ContainsKey(ip.ToString()); + } + + public JT808ResultDto Add(string host) + { + return new JT808ResultDto() { Code = JT808ResultCode.Ok, Data = ForwardingRemoteAddresssDict.TryAdd(host,0) }; + } + + public JT808ResultDto Remove(string host) + { + if(jT808ConfigurationOptionsMonitor.CurrentValue.ForwardingRemoteIPAddress!=null && + jT808ConfigurationOptionsMonitor.CurrentValue.ForwardingRemoteIPAddress.Any(w=>w== host)) + { + return new JT808ResultDto() { Code = JT808ResultCode.Ok, Data = false,Message="不能删除服务器配置的地址" }; + } + else + { + return new JT808ResultDto() { Code = JT808ResultCode.Ok, Data = ForwardingRemoteAddresssDict.TryRemove(host,out var temp) }; + } + } + + public JT808ResultDto> GetAll() + { + return new JT808ResultDto>(){ Code = JT808ResultCode.Ok, Data = ForwardingRemoteAddresssDict.Select(s=>s.Key).ToList() }; + } + + public void Dispose() + { + jT808ConfigurationOptionsMonitorDisposable.Dispose(); + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808UdpSessionService.cs b/src/JT808.DotNetty.Core/Services/JT808UdpSessionService.cs new file mode 100644 index 0000000..975108c --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808UdpSessionService.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Core.Interfaces; + +namespace JT808.DotNetty.Core.Services +{ + internal class JT808UdpSessionService : IJT808UdpSessionService + { + private readonly JT808UdpSessionManager jT808SessionManager; + + public JT808UdpSessionService( + JT808UdpSessionManager jT808SessionManager) + { + this.jT808SessionManager = jT808SessionManager; + } + + public JT808ResultDto> GetAll() + { + JT808ResultDto> resultDto = new JT808ResultDto>(); + try + { + resultDto.Data = jT808SessionManager.GetAll().Select(s => new JT808UdpSessionInfoDto + { + LastActiveTime = s.LastActiveTime, + StartTime = s.StartTime, + TerminalPhoneNo = s.TerminalPhoneNo, + RemoteAddressIP = s.Sender.ToString() + }).ToList(); + resultDto.Code = JT808ResultCode.Ok; + } + catch (Exception ex) + { + resultDto.Data = null; + resultDto.Code = JT808ResultCode.Error; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + return resultDto; + } + + public JT808ResultDto RemoveByTerminalPhoneNo(string terminalPhoneNo) + { + JT808ResultDto resultDto = new JT808ResultDto(); + try + { + var session = jT808SessionManager.RemoveSession(terminalPhoneNo); + if (session != null) + { + if(session.Channel.Open) + { + session.Channel.CloseAsync(); + } + } + resultDto.Code = JT808ResultCode.Ok; + resultDto.Data = true; + } + catch (AggregateException ex) + { + resultDto.Data = false; + resultDto.Code = 500; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + catch (Exception ex) + { + resultDto.Data = false; + resultDto.Code = JT808ResultCode.Error; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + return resultDto; + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808UnificationTcpSendService.cs b/src/JT808.DotNetty.Core/Services/JT808UnificationTcpSendService.cs new file mode 100644 index 0000000..0082c9f --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808UnificationTcpSendService.cs @@ -0,0 +1,49 @@ +using DotNetty.Buffers; +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using JT808.DotNetty.Core.Services; +using System; +using System.Linq; + +namespace JT808.DotNetty.Internal +{ + internal class JT808UnificationTcpSendService : IJT808UnificationTcpSendService + { + private readonly JT808TcpSessionManager jT808SessionManager; + + public JT808UnificationTcpSendService( + JT808TcpSessionManager jT808SessionManager) + { + this.jT808SessionManager = jT808SessionManager; + } + + public JT808ResultDto Send(string terminalPhoneNo, byte[] data) + { + JT808ResultDto resultDto = new JT808ResultDto(); + try + { + if(jT808SessionManager.TrySend(terminalPhoneNo, data, out var message)) + { + resultDto.Code = JT808ResultCode.Ok; + resultDto.Data = true; + resultDto.Message = message; + } + else + { + resultDto.Code = JT808ResultCode.Ok; + resultDto.Data = false; + resultDto.Message = message; + } + } + catch (Exception ex) + { + resultDto.Data = false; + resultDto.Code = JT808ResultCode.Error; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + return resultDto; + } + } +} diff --git a/src/JT808.DotNetty.Core/Services/JT808UnificationUdpSendService.cs b/src/JT808.DotNetty.Core/Services/JT808UnificationUdpSendService.cs new file mode 100644 index 0000000..89d6a99 --- /dev/null +++ b/src/JT808.DotNetty.Core/Services/JT808UnificationUdpSendService.cs @@ -0,0 +1,54 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels.Sockets; +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Services; +using System; + +namespace JT808.DotNetty.Internal +{ + internal class JT808UnificationUdpSendService : IJT808UnificationUdpSendService + { + private readonly JT808UdpSessionManager jT808SessionManager; + + private readonly IJT808DatagramPacket jT808DatagramPacket; + + public JT808UnificationUdpSendService( + IJT808DatagramPacket jT808DatagramPacket, + JT808UdpSessionManager jT808SessionManager) + { + this.jT808DatagramPacket = jT808DatagramPacket; + this.jT808SessionManager = jT808SessionManager; + } + + public JT808ResultDto Send(string terminalPhoneNo, byte[] data) + { + JT808ResultDto resultDto = new JT808ResultDto(); + try + { + var session = jT808SessionManager.GetSession(terminalPhoneNo); + if (session != null) + { + session.Channel.WriteAndFlushAsync(jT808DatagramPacket.Create(data, session.Sender)); + resultDto.Code = JT808ResultCode.Ok; + resultDto.Data = true; + resultDto.Message = "Ok"; + } + else + { + resultDto.Code = JT808ResultCode.Ok; + resultDto.Data = false; + resultDto.Message = "offline"; + } + } + catch (Exception ex) + { + resultDto.Data = false; + resultDto.Code = JT808ResultCode.Error; + resultDto.Message = Newtonsoft.Json.JsonConvert.SerializeObject(ex); + } + return resultDto; + } + } +} diff --git a/src/JT808.DotNetty.Core/Session/JT808TcpSessionManager.cs b/src/JT808.DotNetty.Core/Session/JT808TcpSessionManager.cs new file mode 100644 index 0000000..c122832 --- /dev/null +++ b/src/JT808.DotNetty.Core/Session/JT808TcpSessionManager.cs @@ -0,0 +1,211 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using DotNetty.Transport.Channels; +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Core.Metadata; +using JT808.DotNetty.Core.Interfaces; + +namespace JT808.DotNetty.Core +{ + /// + /// JT808 Tcp会话管理 + /// + public class JT808TcpSessionManager + { + private readonly ILogger logger; + + private readonly IJT808SessionPublishing jT808SessionPublishing; + + public JT808TcpSessionManager( + IJT808SessionPublishing jT808SessionPublishing, + ILoggerFactory loggerFactory) + { + this.jT808SessionPublishing = jT808SessionPublishing; + logger = loggerFactory.CreateLogger(); + } + + private ConcurrentDictionary SessionIdDict = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + public int SessionCount + { + get + { + return SessionIdDict.Count; + } + } + + public JT808TcpSession GetSession(string terminalPhoneNo) + { + if (string.IsNullOrEmpty(terminalPhoneNo)) + return default; + if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession targetSession)) + { + return targetSession; + } + else + { + return default; + } + } + + public void Heartbeat(string terminalPhoneNo) + { + if (string.IsNullOrEmpty(terminalPhoneNo)) return; + if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession oldjT808Session)) + { + oldjT808Session.LastActiveTime = DateTime.Now; + SessionIdDict.TryUpdate(terminalPhoneNo, oldjT808Session, oldjT808Session); + } + } + + public bool TrySend(string terminalPhoneNo, byte[] data, out string message) + { + bool isSuccessed; + var session = GetSession(terminalPhoneNo); + if (session != null) + { + //判断转发数据是下发不了消息的 + if (SessionIdDict.Select(s=>s.Value).Count(c => c.Channel.Id == session.Channel.Id) > 1) + { + isSuccessed = false; + message = "not support transmit data send."; + } + else + { + session.Channel.WriteAndFlushAsync(new JT808Response(data)); + isSuccessed = true; + message = "ok"; + } + } + else + { + isSuccessed = false; + message = "offline"; + } + return isSuccessed; + } + + public bool TrySend(string terminalPhoneNo, IJT808Reply reply, out string message) + { + bool isSuccessed; + var session = GetSession(terminalPhoneNo); + if (session != null) + { + //判断转发数据是下发不了消息的 + if (SessionIdDict.Select(s => s.Value).Count(c => c.Channel.Id == session.Channel.Id) > 1) + { + isSuccessed = false; + message = "not support transmit data send."; + } + else + { + session.Channel.WriteAndFlushAsync(reply); + isSuccessed = true; + message = "ok"; + } + } + else + { + isSuccessed = false; + message = "offline"; + } + return isSuccessed; + } + + public void TryAdd(string terminalPhoneNo,IChannel channel) + { + // 解决了设备号跟通道绑定到一起,不需要用到通道本身的SessionId + // 不管设备下发更改了设备终端号,只要是没有在内存中就当是新的 + // 存在的问题: + // 1.原先老的如何销毁 + // 2.这时候用的通道是相同的,设备终端是不同的 + // 当设备主动或者服务器断开以后,可以释放,这点内存忽略不计,况且更改设备号不是很频繁。 + + //修复第一次通过转发过来的数据,再次通过直连后通道没有改变导致下发不成功,所以每次进行通道的更新操作。 + if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession oldJT808Session)) + { + oldJT808Session.LastActiveTime = DateTime.Now; + oldJT808Session.Channel = channel; + SessionIdDict.TryUpdate(terminalPhoneNo, oldJT808Session, oldJT808Session); + } + else + { + JT808TcpSession jT808TcpSession = new JT808TcpSession(channel, terminalPhoneNo); + if (SessionIdDict.TryAdd(terminalPhoneNo, jT808TcpSession)) + { + //使用场景: + //部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接, + //这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。 + //有设备关联上来可以进行通知 例如:使用Redis发布订阅 + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, jT808TcpSession.TerminalPhoneNo); + } + } + } + + public JT808TcpSession RemoveSession(string terminalPhoneNo) + { + //设备离线可以进行通知 + //使用Redis 发布订阅 + if (string.IsNullOrEmpty(terminalPhoneNo)) return default; + if (!SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession jT808Session)) + { + return default; + } + // 处理转发过来的是数据 这时候通道对设备是1对多关系,需要清理垃圾数据 + //1.用当前会话的通道Id找出通过转发过来的其他设备的终端号 + var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == jT808Session.Channel.Id).Select(s => s.Key).ToList(); + //2.存在则一个个移除 + if (terminalPhoneNos.Count > 1) + { + //3.移除包括当前的设备号 + foreach (var key in terminalPhoneNos) + { + SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove); + } + string nos = string.Join(",", terminalPhoneNos); + logger.LogInformation($">>>{terminalPhoneNo}-{nos} 1-n Session Remove."); + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos); + return jT808Session; + } + else + { + if (SessionIdDict.TryRemove(terminalPhoneNo, out JT808TcpSession jT808SessionRemove)) + { + logger.LogInformation($">>>{terminalPhoneNo} Session Remove."); + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline,terminalPhoneNo); + return jT808SessionRemove; + } + else + { + return default; + } + } + } + + public void RemoveSessionByChannel(IChannel channel) + { + //设备离线可以进行通知 + //使用Redis 发布订阅 + var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList(); + if (terminalPhoneNos.Count > 0) + { + foreach (var key in terminalPhoneNos) + { + SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove); + } + string nos = string.Join(",", terminalPhoneNos); + logger.LogInformation($">>>{nos} Channel Remove."); + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos); + } + } + + public IEnumerable GetAll() + { + return SessionIdDict.Select(s => s.Value).ToList(); + } + } +} + diff --git a/src/JT808.DotNetty.Core/Session/JT808UdpSessionManager.cs b/src/JT808.DotNetty.Core/Session/JT808UdpSessionManager.cs new file mode 100644 index 0000000..4a202f6 --- /dev/null +++ b/src/JT808.DotNetty.Core/Session/JT808UdpSessionManager.cs @@ -0,0 +1,139 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Core.Metadata; +using DotNetty.Transport.Channels; +using JT808.DotNetty.Core.Configurations; +using Microsoft.Extensions.Options; +using System.Net; + +namespace JT808.DotNetty.Core +{ + /// + /// JT808 udp会话管理 + /// 估计要轮询下 + /// + public class JT808UdpSessionManager + { + private readonly ILogger logger; + + private readonly IJT808SessionPublishing jT808SessionPublishing; + private readonly IOptionsMonitor jT808ConfigurationAccessor; + public JT808UdpSessionManager( + IJT808SessionPublishing jT808SessionPublishing, + IOptionsMonitor jT808ConfigurationAccessor, + ILoggerFactory loggerFactory) + { + this.jT808SessionPublishing = jT808SessionPublishing; + logger = loggerFactory.CreateLogger(); + this.jT808ConfigurationAccessor = jT808ConfigurationAccessor; + } + + private ConcurrentDictionary SessionIdDict = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + public int SessionCount + { + get + { + return SessionIdDict.Count; + } + } + + public JT808UdpSession GetSession(string terminalPhoneNo) + { + if (string.IsNullOrEmpty(terminalPhoneNo)) + return default; + if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808UdpSession targetSession)) + { + return targetSession; + } + else + { + return default; + } + } + + public void TryAdd(IChannel channel,EndPoint sender,string terminalPhoneNo) + { + //1.先判断是否在缓存里面 + if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808UdpSession jT808UdpSession)) + { + jT808UdpSession.LastActiveTime=DateTime.Now; + jT808UdpSession.Sender = sender; + jT808UdpSession.Channel = channel; + SessionIdDict.TryUpdate(terminalPhoneNo, jT808UdpSession, jT808UdpSession); + } + else + { + //添加缓存 + //使用场景: + //部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接, + //这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。 + //有设备关联上来可以进行通知 例如:使用Redis发布订阅 + SessionIdDict.TryAdd(terminalPhoneNo, new JT808UdpSession(channel, sender, terminalPhoneNo)); + } + //移动是个大的内网,不跟随下发,根本就发不出来 + //移动很多卡,存储的那个socket地址端口,有效期非常短 + //不速度快点下发,那个socket地址端口就可能映射到别的对应卡去了 + //所以此处采用跟随设备消息下发指令 + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, terminalPhoneNo); + } + + public void Heartbeat(string terminalPhoneNo) + { + if (string.IsNullOrEmpty(terminalPhoneNo)) return; + if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808UdpSession oldjT808Session)) + { + oldjT808Session.LastActiveTime = DateTime.Now; + SessionIdDict.TryUpdate(terminalPhoneNo, oldjT808Session, oldjT808Session); + } + } + + public JT808UdpSession RemoveSession(string terminalPhoneNo) + { + //设备离线可以进行通知 + //使用Redis 发布订阅 + if (string.IsNullOrEmpty(terminalPhoneNo)) return default; + if (!SessionIdDict.TryGetValue(terminalPhoneNo, out JT808UdpSession jT808Session)) + { + return default; + } + if (SessionIdDict.TryRemove(terminalPhoneNo, out JT808UdpSession jT808SessionRemove)) + { + logger.LogInformation($">>>{terminalPhoneNo} Session Remove."); + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline,terminalPhoneNo); + return jT808SessionRemove; + } + else + { + return default; + } + } + + public void RemoveSessionByChannel(IChannel channel) + { + //设备离线可以进行通知 + //使用Redis 发布订阅 + var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList(); + if (terminalPhoneNos.Count > 0) + { + foreach (var key in terminalPhoneNos) + { + SessionIdDict.TryRemove(key, out JT808UdpSession jT808SessionRemove); + } + string nos = string.Join(",", terminalPhoneNos); + logger.LogInformation($">>>{nos} Channel Remove."); + jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos); + } + } + + public IEnumerable GetAll() + { + return SessionIdDict.Select(s => s.Value).ToList(); + } + } +} + diff --git a/src/JT808.DotNetty.Tcp/Handlers/JT808MsgIdDefaultTcpHandler.cs b/src/JT808.DotNetty.Tcp/Handlers/JT808MsgIdDefaultTcpHandler.cs new file mode 100644 index 0000000..fcebc15 --- /dev/null +++ b/src/JT808.DotNetty.Tcp/Handlers/JT808MsgIdDefaultTcpHandler.cs @@ -0,0 +1,18 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Tcp.Handlers +{ + /// + /// 默认消息处理业务实现 + /// + internal class JT808MsgIdDefaultTcpHandler : JT808MsgIdTcpHandlerBase + { + public JT808MsgIdDefaultTcpHandler(JT808TcpSessionManager sessionManager) : base(sessionManager) + { + } + } +} diff --git a/src/JT808.DotNetty.Tcp/Handlers/JT808TcpConnectionHandler.cs b/src/JT808.DotNetty.Tcp/Handlers/JT808TcpConnectionHandler.cs new file mode 100644 index 0000000..33e4d13 --- /dev/null +++ b/src/JT808.DotNetty.Tcp/Handlers/JT808TcpConnectionHandler.cs @@ -0,0 +1,104 @@ +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Channels; +using JT808.DotNetty.Core; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Tcp.Handlers +{ + /// + /// JT808服务通道处理程序 + /// + internal class JT808TcpConnectionHandler : ChannelHandlerAdapter + { + private readonly ILogger logger; + + private readonly JT808TcpSessionManager jT808SessionManager; + + public JT808TcpConnectionHandler( + JT808TcpSessionManager jT808SessionManager, + ILoggerFactory loggerFactory) + { + this.jT808SessionManager = jT808SessionManager; + logger = loggerFactory.CreateLogger(); + } + + /// + /// 通道激活 + /// + /// + public override void ChannelActive(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"<<<{ channelId } Successful client connection to server."); + base.ChannelActive(context); + } + + /// + /// 设备主动断开 + /// + /// + public override void ChannelInactive(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($">>>{ channelId } The client disconnects from the server."); + jT808SessionManager.RemoveSessionByChannel(context.Channel); + base.ChannelInactive(context); + } + + /// + /// 服务器主动断开 + /// + /// + /// + public override Task CloseAsync(IChannelHandlerContext context) + { + string channelId = context.Channel.Id.AsShortText(); + if (logger.IsEnabled(LogLevel.Debug)) + logger.LogDebug($"<<<{ channelId } The server disconnects from the client."); + jT808SessionManager.RemoveSessionByChannel(context.Channel); + return base.CloseAsync(context); + } + + public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); + + /// + /// 超时策略 + /// + /// + /// + public override void UserEventTriggered(IChannelHandlerContext context, object evt) + { + IdleStateEvent idleStateEvent = evt as IdleStateEvent; + if (idleStateEvent != null) + { + if(idleStateEvent.State== IdleState.ReaderIdle) + { + string channelId = context.Channel.Id.AsShortText(); + logger.LogInformation($"{idleStateEvent.State.ToString()}>>>{channelId}"); + // 由于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(); + } + } +} + diff --git a/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs b/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs new file mode 100644 index 0000000..ed23021 --- /dev/null +++ b/src/JT808.DotNetty.Tcp/Handlers/JT808TcpServerHandler.cs @@ -0,0 +1,139 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using JT808.Protocol; +using System; +using JT808.DotNetty.Core; +using JT808.DotNetty.Abstractions; +using Microsoft.Extensions.Logging; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Services; +using JT808.DotNetty.Core.Metadata; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Abstractions.Enums; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.Tcp.Handlers +{ + /// + /// JT808服务端处理程序 + /// + internal class JT808TcpServerHandler : SimpleChannelInboundHandler + { + private readonly JT808MsgIdTcpHandlerBase handler; + + private readonly JT808TcpSessionManager jT808SessionManager; + + private readonly JT808TransmitAddressFilterService jT808TransmitAddressFilterService; + + private readonly IJT808SourcePackageDispatcher jT808SourcePackageDispatcher; + + private readonly JT808AtomicCounterService jT808AtomicCounterService; + + private readonly JT808TrafficService jT808TrafficService; + + private readonly IJT808UplinkPacket jT808UplinkPacket; + + private readonly IJT808TcpCustomMsgIdHandler jT808TcpCustomMsgIdHandler; + + private readonly ILogger logger; + + private readonly ILogger unknownLogger; + + private readonly JT808Serializer JT808Serializer; + + public JT808TcpServerHandler( + IJT808Config jT808Config, + JT808TrafficServiceFactory jT808TrafficServiceFactory, + ILoggerFactory loggerFactory, + JT808TransmitAddressFilterService jT808TransmitAddressFilterService, + IJT808SourcePackageDispatcher jT808SourcePackageDispatcher, + IJT808UplinkPacket jT808UplinkPacket, + JT808MsgIdTcpHandlerBase handler, + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory, + IJT808TcpCustomMsgIdHandler jT808TcpCustomMsgIdHandler, + JT808TcpSessionManager jT808SessionManager) + { + this.jT808TcpCustomMsgIdHandler = jT808TcpCustomMsgIdHandler; + this.jT808TrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.tcp); + this.jT808TransmitAddressFilterService = jT808TransmitAddressFilterService; + this.handler = handler; + this.jT808SessionManager = jT808SessionManager; + this.jT808SourcePackageDispatcher = jT808SourcePackageDispatcher; + this.jT808UplinkPacket = jT808UplinkPacket; + this.jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.tcp); + this.JT808Serializer = jT808Config.GetSerializer(); + logger = loggerFactory.CreateLogger(); + unknownLogger = loggerFactory.CreateLogger("tcp_unknown_msgid"); + } + + + protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg) + { + try + { + jT808SourcePackageDispatcher.SendAsync(msg); + jT808UplinkPacket.ProcessorAsync(msg, JT808TransportProtocolType.tcp); + //解析到头部,然后根据具体的消息Id通过队列去进行消费 + //要是一定要解析到数据体可以在JT808MsgIdHandlerBase类中根据具体的消息, + //解析具体的消息体,具体调用JT808Serializer.Deserialize + JT808HeaderPackage jT808HeaderPackage = JT808Serializer.Deserialize(msg); + jT808AtomicCounterService.MsgSuccessIncrement(); + jT808TrafficService.ReceiveSize(msg.Length); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("accept package success count<<<" + jT808AtomicCounterService.MsgSuccessCount.ToString()); + logger.LogTrace("accept msg <<< " + ByteBufferUtil.HexDump(msg)); + } + jT808SessionManager.TryAdd(jT808HeaderPackage.Header.TerminalPhoneNo,ctx.Channel); + if (handler.HandlerDict.TryGetValue(jT808HeaderPackage.Header.MsgId, out var handlerFunc)) + { + IJT808Reply jT808Response = handlerFunc(new JT808Request(jT808HeaderPackage, msg)); + if (jT808Response != null) + { + if (!jT808TransmitAddressFilterService.ContainsKey(ctx.Channel.RemoteAddress)) + { + ctx.WriteAndFlushAsync(jT808Response); + } + } + } + else + { + IJT808Reply jT808CustomMsgIdResponse = jT808TcpCustomMsgIdHandler.Proccesser(new JT808Request(jT808HeaderPackage, msg)); + if (jT808CustomMsgIdResponse != null) + { + if (!jT808TransmitAddressFilterService.ContainsKey(ctx.Channel.RemoteAddress)) + { + ctx.WriteAndFlushAsync(jT808CustomMsgIdResponse); + } + } + else + { + //未知的消息类型已日志形式输出 + if (unknownLogger.IsEnabled(LogLevel.Debug)) + { + unknownLogger.LogDebug(ByteBufferUtil.HexDump(msg)); + } + } + } + } + catch (JT808.Protocol.Exceptions.JT808Exception ex) + { + jT808AtomicCounterService.MsgFailIncrement(); + if (logger.IsEnabled(LogLevel.Error)) + { + logger.LogError("accept package fail count<<<" + jT808AtomicCounterService.MsgFailCount.ToString()); + logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg)); + } + } + catch (Exception ex) + { + jT808AtomicCounterService.MsgFailIncrement(); + if (logger.IsEnabled(LogLevel.Error)) + { + logger.LogError("accept package fail count<<<" + jT808AtomicCounterService.MsgFailCount.ToString()); + logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg)); + } + } + } + } +} diff --git a/src/JT808.DotNetty.Tcp/JT808.DotNetty.Tcp.csproj b/src/JT808.DotNetty.Tcp/JT808.DotNetty.Tcp.csproj new file mode 100644 index 0000000..28ac10a --- /dev/null +++ b/src/JT808.DotNetty.Tcp/JT808.DotNetty.Tcp.csproj @@ -0,0 +1,20 @@ + + + + JT808.DotNetty.Tcp + JT808.DotNetty.Tcp + 基于DotNetty实现的JT808DotNetty的Tcp服务 + 基于DotNetty实现的JT808DotNetty的Tcp服务 + + + + + + + + + + + + + diff --git a/src/JT808.DotNetty.Tcp/JT808TcpBuilderDefault.cs b/src/JT808.DotNetty.Tcp/JT808TcpBuilderDefault.cs new file mode 100644 index 0000000..369071d --- /dev/null +++ b/src/JT808.DotNetty.Tcp/JT808TcpBuilderDefault.cs @@ -0,0 +1,49 @@ +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Tcp +{ + public class JT808TcpBuilderDefault : IJT808TcpNettyBuilder + { + public IJT808NettyBuilder Instance { get; } + + public JT808TcpBuilderDefault(IJT808NettyBuilder builder) + { + Instance = builder; + } + + public IJT808NettyBuilder Builder() + { + return Instance; + } + + public IJT808TcpNettyBuilder ReplaceCustomMsgIdHandler() where T : IJT808TcpCustomMsgIdHandler + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808TcpCustomMsgIdHandler), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808TcpNettyBuilder ReplaceMsgIdHandler() where T : JT808MsgIdTcpHandlerBase + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(JT808MsgIdTcpHandlerBase), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808TcpNettyBuilder ReplaceSessionService() where T : IJT808TcpSessionService + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808TcpSessionService), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808TcpNettyBuilder ReplaceUnificationSendService() where T : IJT808UnificationTcpSendService + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808UnificationTcpSendService), typeof(T), ServiceLifetime.Singleton)); + return this; + } + } +} diff --git a/src/JT808.DotNetty.Tcp/JT808TcpDotnettyExtensions.cs b/src/JT808.DotNetty.Tcp/JT808TcpDotnettyExtensions.cs new file mode 100644 index 0000000..43ffb3f --- /dev/null +++ b/src/JT808.DotNetty.Tcp/JT808TcpDotnettyExtensions.cs @@ -0,0 +1,41 @@ +using JT808.DotNetty.Core.Codecs; +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Services; +using JT808.DotNetty.Tcp.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using JT808.DotNetty.Core.Jobs; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Internal; + +[assembly: InternalsVisibleTo("JT808.DotNetty.Tcp.Test")] + +namespace JT808.DotNetty.Tcp +{ + public static class JT808TcpDotnettyExtensions + { + public static IJT808TcpNettyBuilder AddJT808TcpNettyHost(this IJT808NettyBuilder jT808NettyBuilder) + { + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + return new JT808TcpBuilderDefault(jT808NettyBuilder); + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Tcp/JT808TcpServerHost.cs b/src/JT808.DotNetty.Tcp/JT808TcpServerHost.cs new file mode 100644 index 0000000..c19f31d --- /dev/null +++ b/src/JT808.DotNetty.Tcp/JT808TcpServerHost.cs @@ -0,0 +1,95 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Libuv; +using JT808.DotNetty.Core.Codecs; +using JT808.DotNetty.Core.Configurations; +using JT808.DotNetty.Tcp.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Tcp +{ + /// + /// JT808 Tcp网关服务 + /// + internal class JT808TcpServerHost : IHostedService + { + private readonly IServiceProvider serviceProvider; + private readonly JT808Configuration configuration; + private readonly ILogger logger; + private DispatcherEventLoopGroup bossGroup; + private WorkerEventLoopGroup workerGroup; + private IChannel bootstrapChannel; + private IByteBufferAllocator serverBufferAllocator; + + public JT808TcpServerHost( + IServiceProvider provider, + ILoggerFactory loggerFactory, + IOptions jT808ConfigurationAccessor) + { + serviceProvider = provider; + configuration = jT808ConfigurationAccessor.Value; + logger=loggerFactory.CreateLogger(); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + bossGroup = new DispatcherEventLoopGroup(); + workerGroup = new WorkerEventLoopGroup(bossGroup, configuration.EventLoopCount); + serverBufferAllocator = new PooledByteBufferAllocator(); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.Group(bossGroup, workerGroup); + bootstrap.Channel(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + bootstrap + .Option(ChannelOption.SoReuseport, true) + .ChildOption(ChannelOption.SoReuseaddr, true); + } + bootstrap + .Option(ChannelOption.SoBacklog, configuration.SoBacklog) + .ChildOption(ChannelOption.Allocator, serverBufferAllocator) + .ChildHandler(new ActionChannelInitializer(channel => + { + IChannelPipeline pipeline = channel.Pipeline; + using (var scope = serviceProvider.CreateScope()) + { + channel.Pipeline.AddLast("jt808TcpBuffer", new DelimiterBasedFrameDecoder(int.MaxValue, + Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), + Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); + channel.Pipeline.AddLast("jt808TcpDecode", scope.ServiceProvider.GetRequiredService()); + channel.Pipeline.AddLast("jt808TcpEncode", scope.ServiceProvider.GetRequiredService()); + channel.Pipeline.AddLast("systemIdleState", new IdleStateHandler( + configuration.ReaderIdleTimeSeconds, + configuration.WriterIdleTimeSeconds, + configuration.AllIdleTimeSeconds)); + channel.Pipeline.AddLast("jt808TcpConnection", scope.ServiceProvider.GetRequiredService()); + channel.Pipeline.AddLast("jt808TcpService", scope.ServiceProvider.GetRequiredService()); + } + })); + logger.LogInformation($"JT808 TCP Server start at {IPAddress.Any}:{configuration.TcpPort}."); + return bootstrap.BindAsync(configuration.TcpPort) + .ContinueWith(i => bootstrapChannel = i.Result); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + await bootstrapChannel.CloseAsync(); + var quietPeriod = configuration.QuietPeriodTimeSpan; + var shutdownTimeout = configuration.ShutdownTimeoutTimeSpan; + await workerGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + await bossGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + } + } +} diff --git a/src/JT808.DotNetty.Tcp/Properties/PublishProfiles/FolderProfile.pubxml b/src/JT808.DotNetty.Tcp/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..e572661 --- /dev/null +++ b/src/JT808.DotNetty.Tcp/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + FileSystem + Release + Any CPU + netstandard2.0 + ..\..\nupkgs + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Tcp/Properties/PublishProfiles/FolderProfile.pubxml.user b/src/JT808.DotNetty.Tcp/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/src/JT808.DotNetty.Tcp/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808.DotNetty.Core.Test.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808.DotNetty.Core.Test.csproj new file mode 100644 index 0000000..2de938d --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808.DotNetty.Core.Test.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808DefaultChannelId.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808DefaultChannelId.cs new file mode 100644 index 0000000..c60dc1d --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808DefaultChannelId.cs @@ -0,0 +1,36 @@ +using DotNetty.Transport.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Test +{ + public class JT808DefaultChannelId : IChannelId + { + private string Id { + get + { + return Guid.NewGuid().ToString("N"); + } + } + + public string AsLongText() + { + return Id; + } + + public string AsShortText() + { + return Id; + } + + public int CompareTo(IChannelId other) + { + if(other.AsShortText()== Id) + { + return 1; + } + return 0; + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808TcpSessionManagerTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808TcpSessionManagerTest.cs new file mode 100644 index 0000000..cdec189 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808TcpSessionManagerTest.cs @@ -0,0 +1,83 @@ +using DotNetty.Transport.Channels.Embedded; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading; + + +namespace JT808.DotNetty.Core.Test +{ + [TestClass] + public class JT808SessionManagerTest: SeedTcpSession + { + [TestMethod] + public void Test1() + { + var no = "test150"; + var channel = new EmbeddedChannel(new JT808DefaultChannelId()); + jT80TcpSessionManager.TryAdd(no,channel); + Thread.Sleep(1000); + jT80TcpSessionManager.Heartbeat(no); + } + + [TestMethod] + public void Test2() + { + var no = "test151"; + var channel = new EmbeddedChannel(new JT808DefaultChannelId()); + jT80TcpSessionManager.TryAdd(no, channel); + var sessionInfo = jT80TcpSessionManager.RemoveSession(no); + Assert.AreEqual(no, sessionInfo.TerminalPhoneNo); + } + + [TestMethod] + public void Test3() + { + var realSessionInfos = jT80TcpSessionManager.GetAll(); + } + + [TestMethod] + public void Test4() + { + var realSessionCount = jT80TcpSessionManager.SessionCount; + } + + [TestMethod] + public void Test5() + { + //转发过来的数据 1:n 一个通道对应多个设备 + var no = "test1"; + var no1 = "test2"; + var no2 = "test3"; + var no3 = "test4"; + var no4 = "test5"; + var channel = new EmbeddedChannel(new JT808DefaultChannelId()); + jT80TcpSessionManager.TryAdd(no,channel); + jT80TcpSessionManager.TryAdd(no1,channel); + jT80TcpSessionManager.TryAdd(no2,channel); + jT80TcpSessionManager.TryAdd(no3,channel); + jT80TcpSessionManager.TryAdd(no4,channel); + var removeSession = jT80TcpSessionManager.RemoveSession(no); + Assert.AreEqual(no, removeSession.TerminalPhoneNo); + Assert.AreEqual(channel, removeSession.Channel); + Assert.AreEqual(channel.Id, removeSession.Channel.Id); + } + + [TestMethod] + public void Test6() + { + //转发过来的数据 1:n 一个通道对应多个设备 + var no = "test61"; + var no1 = "test62"; + var no2 = "test63"; + var no3 = "test64"; + var no4 = "test65"; + var channel1 = new EmbeddedChannel(new JT808DefaultChannelId()); + var channel2 = new EmbeddedChannel(new JT808DefaultChannelId()); + jT80TcpSessionManager.TryAdd(no,channel1); + jT80TcpSessionManager.TryAdd(no1,channel1); + jT80TcpSessionManager.TryAdd(no2,channel1); + jT80TcpSessionManager.TryAdd(no3,channel2); + jT80TcpSessionManager.TryAdd(no4,channel2); + jT80TcpSessionManager.RemoveSessionByChannel(channel1); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808TransmitAddressFilterServiceTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808TransmitAddressFilterServiceTest.cs new file mode 100644 index 0000000..0cf5c65 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/JT808TransmitAddressFilterServiceTest.cs @@ -0,0 +1,75 @@ +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Core.Configurations; +using JT808.DotNetty.Core.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; + +namespace JT808.DotNetty.Core.Test +{ + [TestClass] + public class JT808TransmitAddressFilterServiceTest + { + private JT808TransmitAddressFilterService jT808TransmitAddressFilterService; + + public JT808TransmitAddressFilterServiceTest() + { + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.Configure(hostContext.Configuration.GetSection("JT808Configuration")); + services.AddSingleton(); + }); + var serviceProvider = serverHostBuilder.Build().Services; + jT808TransmitAddressFilterService = serviceProvider.GetService(); + jT808TransmitAddressFilterService.Add("127.0.0.1"); + } + + [TestMethod] + public void Test1() + { + Assert.IsTrue(jT808TransmitAddressFilterService.ContainsKey(new JT808IPAddressDto + { + Host = "127.0.0.1", + Port = 12348 + }.EndPoint)); + } + + [TestMethod] + public void Test2() + { + var result = jT808TransmitAddressFilterService.GetAll(); + } + + [TestMethod] + public void Test3() + { + var result1= jT808TransmitAddressFilterService.Add("127.0.0.1"); + Assert.AreEqual(JT808ResultCode.Ok, result1.Code); + Assert.IsTrue(result1.Data); + var result2 = jT808TransmitAddressFilterService.Remove("127.0.0.1"); + Assert.AreEqual(JT808ResultCode.Ok, result2.Code); + Assert.IsTrue(result2.Data); + } + + [TestMethod] + public void Test4() + { + var result2 = jT808TransmitAddressFilterService.Remove("127.0.0.1"); + Assert.AreEqual(JT808ResultCode.Ok, result2.Code); + Assert.IsFalse(result2.Data); + Assert.AreEqual("不能删除服务器配置的地址", result2.Message); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/SeedTcpSession.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/SeedTcpSession.cs new file mode 100644 index 0000000..352a1ca --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/SeedTcpSession.cs @@ -0,0 +1,37 @@ +using DotNetty.Transport.Channels.Embedded; +using JT808.DotNetty.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Test +{ + [TestClass] + public class SeedTcpSession + { + public JT808TcpSessionManager jT80TcpSessionManager = new JT808TcpSessionManager( + new JT808SessionPublishingEmptyImpl(), + new LoggerFactory()); + + public SeedTcpSession() + { + for (var i = 0; i < 10; i++) + { + var channel = new EmbeddedChannel(new JT808DefaultChannelId()); + jT80TcpSessionManager.TryAdd(i.ToString(),channel); + } + } + + [TestMethod] + public void Init() + { + for (var i = 0; i < 10; i++) + { + var channel = new EmbeddedChannel(new JT808DefaultChannelId()); + jT80TcpSessionManager.TryAdd(i.ToString(), channel); + } + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/TestBase.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/TestBase.cs new file mode 100644 index 0000000..5e325fb --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/TestBase.cs @@ -0,0 +1,37 @@ +using JT808.Protocol; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Core.Test +{ + public class TestBase + { + public static IServiceProvider ServiceProvider; + + static TestBase() + { + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration); + }); + var build = serverHostBuilder.Build(); + build.Start(); + ServiceProvider = build.Services; + + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/appsettings.json b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/appsettings.json new file mode 100644 index 0000000..28e73d8 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Core.Test/appsettings.json @@ -0,0 +1,39 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "JT808Configuration": { + "Port": 6565, + "SourcePackageDispatcherClientConfigurations": [ + { + "Host": "127.0.0.1", + "Port": 6655 + }, + { + "Host": "127.0.0.1", + "Port": 6656 + } + ], + "ForwardingRemoteAddress": [ + { + "Host": "127.0.0.1", + "Port": 6561 + }, + { + "Host": "127.0.0.1", + "Port": 6562 + } + ], + "RedisHost": "127.0.0.1:6379" + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/NLog.xsd b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/NLog.xsd new file mode 100644 index 0000000..2f57d09 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/NLog.xsd @@ -0,0 +1,3106 @@ + + + + + + + + + + + + + + + Watch config file for changes and reload automatically. + + + + + Print internal NLog messages to the console. Default value is: false + + + + + Print internal NLog messages to the console error output. Default value is: false + + + + + Write internal NLog messages to the specified file. + + + + + Log level threshold for internal log messages. Default value is: Info. + + + + + Global log level threshold for application log messages. Messages below this level won't be logged.. + + + + + Throw an exception when there is an internal error. Default value is: false. + + + + + Throw an exception when there is a configuration error. If not set, determined by throwExceptions. + + + + + Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. + + + + + Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. + + + + + Write timestamps for internal NLog messages. Default value is: true. + + + + + Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. + + + + + Perform mesage template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty. + + + + + + + + + + + + + + Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). + + + + + + + + + + + + + + + + + Prefix for targets/layout renderers/filters/conditions loaded from this assembly. + + + + + Load NLog extensions from the specified file (*.dll) + + + + + Load NLog extensions from the specified assembly. Assembly name should be fully qualified. + + + + + + + + + + Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name* + + + + + Comma separated list of levels that this rule matches. + + + + + Minimum level that this rule matches. + + + + + Maximum level that this rule matches. + + + + + Level that this rule matches. + + + + + Comma separated list of target names. + + + + + Ignore further rules if this one matches. + + + + + Enable or disable logging rule. Disabled rules are ignored. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. + + + + + Ignore any errors in the include file. + + + + + + + Variable name. + + + + + Variable value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events that should be processed in a batch by the lazy writer thread. + + + + + Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch + + + + + Action to be taken when the lazy writer thread request queue count exceeds the set limit. + + + + + Limit on the number of requests in the lazy writer thread request queue. + + + + + Time in milliseconds to sleep between batches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Delay the flush until the LogEvent has been confirmed as written + + + + + Condition expression. Log events who meet this condition will cause a flush on the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events to be buffered. + + + + + Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. + + + + + Indicates whether to use sliding timeout. + + + + + Action to take if the buffer overflows. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Maximum current connections. 0 = no maximum. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Network address. + + + + + Maximum queue size. + + + + + NDC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Viewer parameter name. + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to use default row highlighting rules. + + + + + Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Condition that must be met in order to set the specified foreground and background color. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + Indicates whether to ignore case when comparing texts. + + + + + Regular expression to be matched. You must specify either text or regex. + + + + + Text to be matched. You must specify either text or regex. + + + + + Indicates whether to match whole words only. + + + + + Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send the log messages to the standard error instead of the standard output. + + + + + Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. + + + + + Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. + + + + + Name of the database provider. + + + + + Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. + + + + + Indicates whether to keep the database connection open between the log events. + + + + + Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. + + + + + Name of the connection string (as specified in <connectionStrings> configuration section. + + + + + Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. + + + + + Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. + + + + + Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Text of the SQL command to be run on each log level. + + + + + Type of the SQL command to be run on each log level. + + + + + + + + + + + + + + + + + + + + + + + Type of the command. + + + + + Connection string to run the command against. If not provided, connection string from the target is used. + + + + + Indicates whether to ignore failures. + + + + + Command text. + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Database parameter name. + + + + + Database parameter precision. + + + + + Database parameter scale. + + + + + Database parameter size. + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Layout that renders event Category. + + + + + Layout that renders event ID. + + + + + Name of the Event Log to write to. This can be System, Application or any user-defined name. + + + + + Name of the machine on which Event Log service is running. + + + + + Value to be used as the event Source. + + + + + Action to take if the message is larger than the option. + + + + + Optional entrytype. When not set, or when not convertable to then determined by + + + + + Maximum Event log size in kilobytes. If null, the value won't be set. Default is 512 Kilobytes as specified by Eventlog API + + + + + Message length limit to write to the Event Log. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether to return to the first target after any successful write. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + File encoding. + + + + + Line ending mode. + + + + + Way file archives are numbered. + + + + + Name of the file to be used for an archive. + + + + + Indicates whether to automatically archive log files every time the specified time passes. + + + + + Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: + + + + + Indicates whether to compress archive files into the zip archive format. + + + + + Maximum number of archive files that should be kept. + + + + + Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. + + + + + Is the an absolute or relative path? + + + + + Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. + + + + + Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write + + + + + Is the an absolute or relative path? + + + + + Value indicationg whether file creation calls should be synchronized by a system global mutex. + + + + + Maximum number of log filenames that should be stored as existing. + + + + + Indicates whether the footer should be written only when the file is archived. + + + + + Name of the file to write to. + + + + + Value specifying the date format to use when archiving files. + + + + + Indicates whether to archive old log file on startup. + + + + + Indicates whether to create directories if they do not exist. + + + + + File attributes (Windows only). + + + + + Indicates whether to delete old log file on startup. + + + + + Indicates whether to replace file contents on each write instead of appending log message at the end. + + + + + Indicates whether to enable log file(s) to be deleted. + + + + + Number of times the write is appended on the file before NLog discards the log message. + + + + + Indicates whether concurrent writes to the log file by multiple processes on the same host. + + + + + Indicates whether to keep log file open instead of opening and closing it on each logging event. + + + + + Indicates whether concurrent writes to the log file by multiple processes on different network hosts. + + + + + Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). + + + + + Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Log file buffer size in bytes. + + + + + Indicates whether to automatically flush the file buffers after each log message. + + + + + Delay in milliseconds to wait before attempting to write to the file again. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Condition expression. Log events who meet this condition will be forwarded to the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Windows domain name to change context to. + + + + + Required impersonation level. + + + + + Type of the logon provider. + + + + + Logon Type. + + + + + User account password. + + + + + Indicates whether to revert to the credentials of the process instead of impersonating another user. + + + + + Username to change context to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Interval in which messages will be written up to the number of messages. + + + + + Maximum allowed number of messages written per . + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Endpoint address. + + + + + Name of the endpoint configuration in WCF configuration file. + + + + + Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) + + + + + Client ID. + + + + + Indicates whether to include per-event properties in the payload sent to the server. + + + + + Indicates whether to use binary message encoding. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Name of the parameter. + + + + + Type of the parameter. + + + + + Type of the parameter. Obsolete alias for + + + + + Parameter can combine multiple LogEvents into a single parameter value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send message as HTML instead of plain text. + + + + + Encoding to be used for sending e-mail. + + + + + Indicates whether to add new lines between log entries. + + + + + CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Mail message body (repeated for each log message send in one mail). + + + + + Mail subject. + + + + + Sender's email address (e.g. joe@domain.com). + + + + + Indicates the SMTP client timeout. + + + + + Priority used for sending mails. + + + + + Indicates whether NewLine characters in the body should be replaced with tags. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + SMTP Server to be used for sending. + + + + + SMTP Authentication mode. + + + + + Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. + + + + + Port number that SMTP Server is listening on. + + + + + Indicates whether the default Settings from System.Net.MailSettings should be used. + + + + + Folder where applications save mail messages to be processed by the local SMTP server. + + + + + Specifies how outgoing email messages will be handled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Class name. + + + + + Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Indicates whether to keep connection open whenever possible. + + + + + Maximum current connections. 0 = no maximum. + + + + + Maximum queue size. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Maximum current connections. 0 = no maximum. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Network address. + + + + + Maximum queue size. + + + + + NDC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to perform layout calculation. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether performance counter should be automatically created. + + + + + Name of the performance counter category. + + + + + Counter help text. + + + + + Name of the performance counter. + + + + + Performance counter type. + + + + + The value by which to increment the counter. + + + + + Performance counter instance name. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Default filter to be applied when no specific rule matches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + Condition to be tested. + + + + + Resulting filter to be applied when the condition matches. + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of times to repeat each log message. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of retries that should be attempted on the wrapped target in case of a failure. + + + + + Time to wait between retries in milliseconds. + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Always use independent of + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Encoding. + + + + + Value whether escaping be done according to the old NLog style (Very non-standard) + + + + + Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) + + + + + Web service method name. Only used with Soap. + + + + + Web service namespace. Only used with Soap. + + + + + Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters) + + + + + Protocol to be used when calling web service. + + + + + Web service URL. + + + + + Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). + + + + + (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). + + + + + Column delimiter. + + + + + Quote Character. + + + + + Quoting mode. + + + + + Indicates whether CVS should include header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout of the column. + + + + + Name of the column. + + + + + + + + + + + + + + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log events + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Option to render the empty object value {} + + + + + Option to suppress the extra spaces in the output json + + + + + + + + + + + + + + + Determines wether or not this attribute will be Json encoded. + + + + + Indicates whether to escape non-ascii characters + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + + + + + + + + + + + + + + Option to include all properties from the log events + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include contents of the stack. + + + + + + + + + + + + + + Layout text. + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Condition expression. + + + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Layout to be used to filter log messages. + + + + + Default number of unique filter values to expect, will automatically increase if needed + + + + + Append FilterCount to the when an event is no longer filtered + + + + + Insert FilterCount value into when an event is no longer filtered + + + + + Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout. + + + + + Max number of unique filter values to expect simultaneously + + + + + Max length of filter values, will truncate if above limit + + + + + Default buffer size for the internal buffers + + + + + Reuse internal buffers, and doesn't have to constantly allocate new buffers + + + + + How long before a filter expires, and logging is accepted again + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/nlog.unix.config b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/nlog.unix.config new file mode 100644 index 0000000..f35bd45 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/nlog.unix.config @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/nlog.win.config b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/nlog.win.config new file mode 100644 index 0000000..14d4a10 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Configs/nlog.win.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Handlers/JT808MsgIdTcpCustomHandler.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Handlers/JT808MsgIdTcpCustomHandler.cs new file mode 100644 index 0000000..845b220 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Handlers/JT808MsgIdTcpCustomHandler.cs @@ -0,0 +1,59 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Hosting.Handlers +{ + public class JT808MsgIdTcpCustomHandler : JT808MsgIdTcpHandlerBase + { + public JT808MsgIdTcpCustomHandler( + ILoggerFactory loggerFactory, + JT808TcpSessionManager sessionManager) : base(sessionManager) + { + logger = loggerFactory.CreateLogger(); + } + + private readonly ILogger logger; + + public override IJT808Reply Msg0x0200(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0200"); + return base.Msg0x0200(request); + } + + public override IJT808Reply Msg0x0001(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0001"); + return base.Msg0x0001(request); + } + + public override IJT808Reply Msg0x0002(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0002"); + return base.Msg0x0002(request); + } + + public override IJT808Reply Msg0x0003(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0003"); + return base.Msg0x0003(request); + } + + public override IJT808Reply Msg0x0100(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0100"); + return base.Msg0x0100(request); + } + + public override IJT808Reply Msg0x0102(JT808Request request) + { + logger.LogDebug("Tcp_Msg0x0102"); + return base.Msg0x0102(request); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Handlers/JT808MsgIdUdpCustomHandler.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Handlers/JT808MsgIdUdpCustomHandler.cs new file mode 100644 index 0000000..7280a9d --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Handlers/JT808MsgIdUdpCustomHandler.cs @@ -0,0 +1,59 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Hosting.Handlers +{ + public class JT808MsgIdUdpCustomHandler : JT808MsgIdUdpHandlerBase + { + public JT808MsgIdUdpCustomHandler( + ILoggerFactory loggerFactory, + JT808UdpSessionManager sessionManager) : base(sessionManager) + { + logger = loggerFactory.CreateLogger(); + } + + private readonly ILogger logger; + + public override IJT808Reply Msg0x0200(JT808Request request) + { + logger.LogDebug("Udp_Msg0x0200"); + return base.Msg0x0200(request); + } + + public override IJT808Reply Msg0x0001(JT808Request request) + { + logger.LogDebug("Udp_Msg0x0001"); + return base.Msg0x0001(request); + } + + public override IJT808Reply Msg0x0002(JT808Request request) + { + logger.LogDebug("Udp_Msg0x0002"); + return base.Msg0x0002(request); + } + + public override IJT808Reply Msg0x0003(JT808Request request) + { + logger.LogDebug("Udp_Msg0x0003"); + return base.Msg0x0003(request); + } + + public override IJT808Reply Msg0x0100(JT808Request request) + { + logger.LogDebug("Udp_Msg0x0100"); + return base.Msg0x0100(request); + } + + public override IJT808Reply Msg0x0102(JT808Request request) + { + logger.LogDebug("Udp_Msg0x0102"); + return base.Msg0x0102(request); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Impls/JT808DownlinkPacketLogging.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Impls/JT808DownlinkPacketLogging.cs new file mode 100644 index 0000000..af0abb1 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Impls/JT808DownlinkPacketLogging.cs @@ -0,0 +1,25 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Abstractions.Enums; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using JT808.Protocol.Extensions; + +namespace JT808.DotNetty.Hosting.Impls +{ + public class JT808DownlinkPacketLogging : IJT808DownlinkPacket + { + private readonly ILogger logger; + public JT808DownlinkPacketLogging(ILoggerFactory loggerFactory ) + { + logger = loggerFactory.CreateLogger("JT808DownlinkPacketLogging"); + } + public Task ProcessorAsync(byte[] data, JT808TransportProtocolType transportProtocolType) + { + logger.LogInformation("send >>>"+data.ToHexString()); + return Task.CompletedTask; + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.csproj new file mode 100644 index 0000000..ee41510 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.csproj @@ -0,0 +1,42 @@ + + + + Exe + netcoreapp2.2 + true + latest + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.ini b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.ini new file mode 100644 index 0000000..75ec140 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/JT808.DotNetty.Hosting.ini @@ -0,0 +1,6 @@ +[program:JT808.DotNetty.Hosting] +command=dotnet JT808.DotNetty.Hosting.dll +directory=/data/dotnetty_test +user=root +stdout_logfile=/data/supervisordlog/JT808.DotNetty.Hosting.log +redirect_stderr=true \ No newline at end of file diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Program.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Program.cs new file mode 100644 index 0000000..30c39f5 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/Program.cs @@ -0,0 +1,87 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Hosting.Handlers; +using JT808.DotNetty.Hosting.Impls; +using JT808.DotNetty.Tcp; +using JT808.DotNetty.Udp; +using JT808.DotNetty.WebApi; +using JT808.DotNetty.WebApiClientTool; +using JT808.Protocol; +using JT808.Protocol.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; +using WebApiClient.Extensions.DependencyInjection; + +namespace JT808.DotNetty.Hosting +{ + class Program + { + static async Task Main(string[] args) + { + //7E020000220138123456780085000000010000000101EA2A3F08717931000C015400201901032000020104000000E6F87E + 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(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration) + //自定义日志下发包 + .ReplaceDownlinkPacket() + //自定义会话通知(在线/离线)使用异步方式 + //.ReplaceSessionPublishing() + //自定义原包转发 使用异步方式 + //.ReplaceSourcePackageDispatcher + .AddJT808TcpNettyHost() + // 自定义Tcp消息处理业务 + .ReplaceMsgIdHandler() + .Builder() + .AddJT808UdpNettyHost() + // 自定义Udp消息处理业务 + .ReplaceMsgIdHandler() + .Builder() + .AddJT808WebApiNettyHost() + .Builder(); + //webapi客户端调用 + services.AddHttpApi().ConfigureHttpApiConfig((c, p) => + { + c.HttpHost = new Uri("http://localhost:828/jt808api/"); + c.FormatOptions.DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff"; + c.LoggerFactory = p.GetRequiredService(); + }); + var client = services.BuildServiceProvider().GetRequiredService(); + var result = client.GetTcpAtomicCounter().InvokeAsync().Result; + }); + + await serverHostBuilder.RunConsoleAsync(); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/appsettings.json b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/appsettings.json new file mode 100644 index 0000000..6603db6 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Hosting/appsettings.json @@ -0,0 +1,23 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "JT808Configuration": { + "TcpPort": 808, + "UdpPort": 808, + "WebApiPort": 828, + "ForwardingRemoteIPAddress": [ + //"172.16.19.209" + ] + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808.DotNetty.Tcp.Test.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808.DotNetty.Tcp.Test.csproj new file mode 100644 index 0000000..c72c6a7 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808.DotNetty.Tcp.Test.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808SessionServiceTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808SessionServiceTest.cs new file mode 100644 index 0000000..3a483d8 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808SessionServiceTest.cs @@ -0,0 +1,94 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Interfaces; +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using JT808.Protocol.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JT808.DotNetty.Tcp.Test +{ + [TestClass] + public class JT808SessionServiceTest:TestBase,IDisposable + { + static IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); + + JT808SimpleTcpClient SimpleTcpClient1; + JT808SimpleTcpClient SimpleTcpClient2; + JT808SimpleTcpClient SimpleTcpClient3; + JT808SimpleTcpClient SimpleTcpClient4; + JT808SimpleTcpClient SimpleTcpClient5; + + public JT808SessionServiceTest() + { + SimpleTcpClient1 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient2 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient3 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient4 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient5 = new JT808SimpleTcpClient(endPoint); + // 心跳会话包 + JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001"); + SimpleTcpClient1.WriteAsync(JT808Serializer.Serialize(jT808Package1)); + + // 心跳会话包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789002"); + SimpleTcpClient2.WriteAsync(JT808Serializer.Serialize(jT808Package2)); + + // 心跳会话包 + JT808Package jT808Package3 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789003"); + SimpleTcpClient3.WriteAsync(JT808Serializer.Serialize(jT808Package3)); + + // 心跳会话包 + JT808Package jT808Package4 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789004"); + SimpleTcpClient4.WriteAsync(JT808Serializer.Serialize(jT808Package4)); + + // 心跳会话包 + JT808Package jT808Package5 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789005"); + SimpleTcpClient5.WriteAsync(JT808Serializer.Serialize(jT808Package5)); + Thread.Sleep(1000); + } + + public void Dispose() + { + SimpleTcpClient1.Down(); + SimpleTcpClient2.Down(); + SimpleTcpClient3.Down(); + SimpleTcpClient4.Down(); + SimpleTcpClient5.Down(); + } + + [TestMethod] + public void Test1() + { + IJT808TcpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + var result = jT808SessionServiceDefaultImpl.GetAll(); + Thread.Sleep(5000); + } + + [TestMethod] + public void Test2() + { + IJT808TcpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + var result1 = jT808SessionServiceDefaultImpl.GetAll(); + var result2 = jT808SessionServiceDefaultImpl.RemoveByTerminalPhoneNo("123456789001"); + var result3 = jT808SessionServiceDefaultImpl.GetAll(); + } + + [TestMethod] + public void Test3() + { + // 判断通道是否关闭 + IJT808TcpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + JT808TcpSessionManager jT808TcpSessionManager = ServiceProvider.GetService(); + var result1 = jT808SessionServiceDefaultImpl.GetAll(); + SimpleTcpClient1.Down(); + Thread.Sleep(5000); + var session = jT808TcpSessionManager.GetSession("123456789001"); + Thread.Sleep(100000); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808UnificationTcpSendServiceTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808UnificationTcpSendServiceTest.cs new file mode 100644 index 0000000..d2070e7 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/JT808UnificationTcpSendServiceTest.cs @@ -0,0 +1,76 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Interfaces; +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using JT808.Protocol.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using JT808.Protocol.MessageBody; +using JT808.DotNetty.Abstractions.Dtos; + +namespace JT808.DotNetty.Tcp.Test +{ + [TestClass] + public class JT808UnificationTcpSendServiceTest: TestBase + { + static IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); + + private IJT808UnificationTcpSendService jT808UnificationTcpSendService; + private IJT808TcpSessionService jT808SessionServiceDefaultImpl; + + public JT808UnificationTcpSendServiceTest() + { + + JT808SimpleTcpClient SimpleTcpClient1 = new JT808SimpleTcpClient(endPoint); + JT808SimpleTcpClient SimpleTcpClient2 = new JT808SimpleTcpClient(endPoint); + JT808SimpleTcpClient SimpleTcpClient3 = new JT808SimpleTcpClient(endPoint); + JT808SimpleTcpClient SimpleTcpClient4 = new JT808SimpleTcpClient(endPoint); + JT808SimpleTcpClient SimpleTcpClient5 = new JT808SimpleTcpClient(endPoint); + // 心跳会话包 + JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001"); + SimpleTcpClient1.WriteAsync(JT808Serializer.Serialize(jT808Package1)); + + // 心跳会话包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789002"); + SimpleTcpClient2.WriteAsync(JT808Serializer.Serialize(jT808Package2)); + + // 心跳会话包 + JT808Package jT808Package3 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789003"); + SimpleTcpClient3.WriteAsync(JT808Serializer.Serialize(jT808Package3)); + + // 心跳会话包 + JT808Package jT808Package4 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789004"); + SimpleTcpClient4.WriteAsync(JT808Serializer.Serialize(jT808Package4)); + + // 心跳会话包 + JT808Package jT808Package5 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789005"); + SimpleTcpClient5.WriteAsync(JT808Serializer.Serialize(jT808Package5)); + + Thread.Sleep(300); + } + + [TestMethod] + public void Test1() + { + jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + jT808UnificationTcpSendService = ServiceProvider.GetService(); + jT808SessionServiceDefaultImpl.GetAll(); + string no = "123456789001"; + // 文本信息包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.文本信息下发.Create(no, new JT808_0x8300 + { + TextFlag = 5, + TextInfo = "smallchi 518" + }); + var data = JT808Serializer.Serialize(jT808Package2); + JT808ResultDto jt808Result = jT808UnificationTcpSendService.Send(no, data); + Thread.Sleep(1000); + Assert.AreEqual(200, jt808Result.Code); + Assert.IsTrue(jt808Result.Data); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/TestBase.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/TestBase.cs new file mode 100644 index 0000000..fa57faa --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/TestBase.cs @@ -0,0 +1,42 @@ +using JT808.DotNetty.Core; +using JT808.Protocol; +using JT808.Protocol.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Tcp.Test +{ + public class TestBase + { + public static IServiceProvider ServiceProvider; + public static JT808Serializer JT808Serializer; + static TestBase() + { + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration) + .AddJT808TcpNettyHost() + .Builder(); + //.Replace<>; + }); + var build = serverHostBuilder.Build(); + build.Start(); + ServiceProvider = build.Services; + JT808Serializer = ServiceProvider.GetRequiredService().GetSerializer(); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/appsettings.json b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/appsettings.json new file mode 100644 index 0000000..38418be --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Tcp.Test/appsettings.json @@ -0,0 +1,21 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "JT808Configuration": { + "TcpPort": 6565, + "ForwardingRemoteIPAddress": [ + "127.0.0.1" + ] + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808.DotNetty.Udp.Test.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808.DotNetty.Udp.Test.csproj new file mode 100644 index 0000000..64634d8 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808.DotNetty.Udp.Test.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + + + + Always + + + + + + Always + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808SessionServiceTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808SessionServiceTest.cs new file mode 100644 index 0000000..d9ab3fd --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808SessionServiceTest.cs @@ -0,0 +1,92 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Interfaces; +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using JT808.Protocol.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JT808.DotNetty.Udp.Test +{ + [TestClass] + public class JT808SessionServiceTest:TestBase,IDisposable + { + static IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 818); + JT808SimpleUdpClient SimpleUdpClient1; + JT808SimpleUdpClient SimpleUdpClient2; + JT808SimpleUdpClient SimpleUdpClient3; + JT808SimpleUdpClient SimpleUdpClient4; + JT808SimpleUdpClient SimpleUdpClient5; + + public void Dispose() + { + SimpleUdpClient1.Down(); + SimpleUdpClient2.Down(); + SimpleUdpClient3.Down(); + SimpleUdpClient4.Down(); + SimpleUdpClient5.Down(); + } + + public JT808SessionServiceTest() + { + SimpleUdpClient1 = new JT808SimpleUdpClient(endPoint); + SimpleUdpClient2 = new JT808SimpleUdpClient(endPoint); + SimpleUdpClient3 = new JT808SimpleUdpClient(endPoint); + SimpleUdpClient4 = new JT808SimpleUdpClient(endPoint); + SimpleUdpClient5 = new JT808SimpleUdpClient(endPoint); + // 心跳会话包 + JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001"); + SimpleUdpClient1.WriteAsync(JT808Serializer.Serialize(jT808Package1)); + + // 心跳会话包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789002"); + SimpleUdpClient2.WriteAsync(JT808Serializer.Serialize(jT808Package2)); + + // 心跳会话包 + JT808Package jT808Package3 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789003"); + SimpleUdpClient3.WriteAsync(JT808Serializer.Serialize(jT808Package3)); + + // 心跳会话包 + JT808Package jT808Package4 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789004"); + SimpleUdpClient4.WriteAsync(JT808Serializer.Serialize(jT808Package4)); + + // 心跳会话包 + JT808Package jT808Package5 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789005"); + SimpleUdpClient5.WriteAsync(JT808Serializer.Serialize(jT808Package5)); + Thread.Sleep(1000); + } + + [TestMethod] + public void Test1() + { + IJT808UdpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + var result = jT808SessionServiceDefaultImpl.GetAll(); + } + + [TestMethod] + public void Test2() + { + IJT808UdpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + var result1 = jT808SessionServiceDefaultImpl.GetAll(); + var result2 = jT808SessionServiceDefaultImpl.RemoveByTerminalPhoneNo("123456789001"); + var result3 = jT808SessionServiceDefaultImpl.GetAll(); + } + + [TestMethod] + public void Test3() + { + // 判断通道是否关闭 + IJT808UdpSessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + JT808UdpSessionManager jT808UdpSessionManager = ServiceProvider.GetService(); + var result1 = jT808SessionServiceDefaultImpl.GetAll(); + SimpleUdpClient1.Down(); + var session = jT808UdpSessionManager.GetSession("123456789001"); + var result3 = jT808UdpSessionManager.GetAll(); + Thread.Sleep(100000); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808UnificationUdpSendServiceTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808UnificationUdpSendServiceTest.cs new file mode 100644 index 0000000..e64fd9a --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/JT808UnificationUdpSendServiceTest.cs @@ -0,0 +1,76 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Interfaces; +using JT808.Protocol; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using JT808.Protocol.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using JT808.Protocol.MessageBody; +using JT808.DotNetty.Abstractions.Dtos; + +namespace JT808.DotNetty.Udp.Test +{ + [TestClass] + public class JT808UnificationUdpSendServiceTest : TestBase + { + static IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 818); + + private IJT808UnificationUdpSendService jT808UnificationUdpSendService; + private IJT808UdpSessionService jT808SessionServiceDefaultImpl; + + public JT808UnificationUdpSendServiceTest() + { + JT808SimpleUdpClient SimpleUdpClient1 = new JT808SimpleUdpClient(endPoint); + JT808SimpleUdpClient SimpleUdpClient2 = new JT808SimpleUdpClient(endPoint); + JT808SimpleUdpClient SimpleUdpClient3 = new JT808SimpleUdpClient(endPoint); + JT808SimpleUdpClient SimpleUdpClient4 = new JT808SimpleUdpClient(endPoint); + JT808SimpleUdpClient SimpleUdpClient5 = new JT808SimpleUdpClient(endPoint); + // 心跳会话包 + JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001"); + SimpleUdpClient1.WriteAsync(JT808Serializer.Serialize(jT808Package1)); + + // 心跳会话包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789002"); + SimpleUdpClient2.WriteAsync(JT808Serializer.Serialize(jT808Package2)); + + // 心跳会话包 + JT808Package jT808Package3 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789003"); + SimpleUdpClient3.WriteAsync(JT808Serializer.Serialize(jT808Package3)); + + // 心跳会话包 + JT808Package jT808Package4 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789004"); + SimpleUdpClient4.WriteAsync(JT808Serializer.Serialize(jT808Package4)); + + // 心跳会话包 + JT808Package jT808Package5 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789005"); + SimpleUdpClient5.WriteAsync(JT808Serializer.Serialize(jT808Package5)); + + Thread.Sleep(300); + } + + [TestMethod] + public void Test1() + { + //"126 131 0 0 13 18 52 86 120 144 1 0 11 5 115 109 97 108 108 99 104 105 32 53 49 56 24 126" + jT808SessionServiceDefaultImpl = ServiceProvider.GetService(); + jT808UnificationUdpSendService = ServiceProvider.GetService(); + jT808SessionServiceDefaultImpl.GetAll(); + string no = "123456789001"; + // 文本信息包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.文本信息下发.Create(no, new JT808_0x8300 + { + TextFlag = 5, + TextInfo = "smallchi 518" + }); + var data = JT808Serializer.Serialize(jT808Package2); + JT808ResultDto jt808Result = jT808UnificationUdpSendService.Send(no, data); + Thread.Sleep(1000); + Assert.AreEqual(200, jt808Result.Code); + Assert.IsTrue(jt808Result.Data); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/TestBase.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/TestBase.cs new file mode 100644 index 0000000..82b48ae --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/TestBase.cs @@ -0,0 +1,40 @@ +using JT808.DotNetty.Core; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using JT808.Protocol; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.Udp.Test +{ + public class TestBase + { + public static IServiceProvider ServiceProvider; + public static JT808Serializer JT808Serializer; + static TestBase() + { + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration) + .AddJT808UdpNettyHost(); + }); + var build = serverHostBuilder.Build(); + build.Start(); + ServiceProvider = build.Services; + JT808Serializer = ServiceProvider.GetRequiredService().GetSerializer(); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/appsettings.json b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/appsettings.json new file mode 100644 index 0000000..df938a4 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.Udp.Test/appsettings.json @@ -0,0 +1,22 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + "JT808Configuration": { + "TcpPort": 6565, + "ForwardingRemoteIPAddress": [ + "127.0.0.1" + ], + "RedisHost": "127.0.0.1:6379" + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/JT808.DotNetty.WebApi.Test.csproj b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/JT808.DotNetty.WebApi.Test.csproj new file mode 100644 index 0000000..1b35530 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/JT808.DotNetty.WebApi.Test.csproj @@ -0,0 +1,32 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/JT808DotNettyWebApiTest.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/JT808DotNettyWebApiTest.cs new file mode 100644 index 0000000..bbb9014 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/JT808DotNettyWebApiTest.cs @@ -0,0 +1,125 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.WebApiClientTool; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using WebApiClient; + +namespace JT808.DotNetty.WebApi.Test +{ + [TestClass] + public class JT808DotNettyWebApiTest: TestBase + { + IJT808DotNettyWebApi jT808DotNettyWebApi; + + public JT808DotNettyWebApiTest() + { + HttpApi.Register().ConfigureHttpApiConfig(c => + { + c.HttpHost = new Uri("http://127.0.0.1:12828" + JT808Constants.JT808WebApiRouteTable.RouteTablePrefix + "/"); + c.LoggerFactory = new LoggerFactory(); + }); + var api = HttpApi.Resolve(); + } + + [TestMethod] + public void GetSystemCollectTest() + { + var result = jT808DotNettyWebApi.GetSystemCollect().GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetUdpAtomicCounterTest() + { + var result = jT808DotNettyWebApi.GetUdpAtomicCounter().GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetUdpTrafficTest() + { + var result = jT808DotNettyWebApi.GetUdpTraffic().GetAwaiter().GetResult(); + } + + [TestMethod] + public void UnificationUdpSendTest() + { + var result = jT808DotNettyWebApi.UnificationUdpSend(new Abstractions.Dtos.JT808UnificationSendRequestDto { + TerminalPhoneNo= "123456789014", + Data=new byte[] {1,2,3,4} + }).GetAwaiter().GetResult(); + } + + [TestMethod] + public void RemoveUdpSessionByTerminalPhoneNoTest() + { + var result = jT808DotNettyWebApi.RemoveUdpSessionByTerminalPhoneNo("123456789014").GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetUdpSessionAllTest() + { + var result = jT808DotNettyWebApi.GetUdpSessionAll().GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetTcpTrafficTest() + { + var result = jT808DotNettyWebApi.GetTcpTraffic().GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetTcpAtomicCounterTest() + { + var result = jT808DotNettyWebApi.GetTcpAtomicCounter().GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetTransmitAllTest() + { + var result = jT808DotNettyWebApi.GetTransmitAll().GetAwaiter().GetResult(); + } + + [TestMethod] + public void RemoveTransmitAddressTest() + { + var result = jT808DotNettyWebApi.RemoveTransmitAddress(new Abstractions.Dtos.JT808IPAddressDto + { + Host = "127.0.0.1", + Port = 6561 + }).GetAwaiter().GetResult(); + } + + [TestMethod] + public void AddTransmitAddressTest() + { + var result = jT808DotNettyWebApi.AddTransmitAddress(new Abstractions.Dtos.JT808IPAddressDto + { + Host = "127.0.0.1", + Port = 6553 + }).GetAwaiter().GetResult(); + } + + [TestMethod] + public void UnificationTcpSendTest() + { + var result = jT808DotNettyWebApi.UnificationTcpSend(new Abstractions.Dtos.JT808UnificationSendRequestDto + { + TerminalPhoneNo = "123456789002", + Data = new byte[] { 1, 2, 3, 4 } + }).GetAwaiter().GetResult(); + } + + [TestMethod] + public void RemoveTcpSessionByTerminalPhoneNoTest() + { + var result = jT808DotNettyWebApi.RemoveTcpSessionByTerminalPhoneNo("123456789002").GetAwaiter().GetResult(); + } + + [TestMethod] + public void GetTcpSessionAllTest() + { + var result = jT808DotNettyWebApi.GetTcpSessionAll().GetAwaiter().GetResult(); + + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/TestBase.cs b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/TestBase.cs new file mode 100644 index 0000000..8309c83 --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/TestBase.cs @@ -0,0 +1,142 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Udp; +using JT808.DotNetty.Tcp; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using JT808.DotNetty.WebApiClientTool; +using System.Net; +using JT808.Protocol; +using JT808.Protocol.Extensions; +using System.Threading; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.WebApi.Test +{ + public class TestBase + { + public static IServiceProvider ServiceProvider; + public static JT808Serializer JT808Serializer; + static TestBase() + { + var serverHostBuilder = new HostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.Configure(hostContext.Configuration.GetSection("JT808DotNettyWebApiOptions")); + services.AddSingleton(); + services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddJT808Configure() + .AddJT808NettyCore(hostContext.Configuration) + .AddJT808TcpNettyHost() + .Builder() + .AddJT808UdpNettyHost() + .Builder() + .AddJT808WebApiNettyHost(); + }); + var build = serverHostBuilder.Build(); + build.Start(); + ServiceProvider = build.Services; + JT808Serializer = ServiceProvider.GetRequiredService().GetSerializer(); + } + + static IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12808); + + static IPEndPoint endPoint1 = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12818); + + JT808SimpleTcpClient SimpleTcpClient1; + JT808SimpleTcpClient SimpleTcpClient2; + JT808SimpleTcpClient SimpleTcpClient3; + JT808SimpleTcpClient SimpleTcpClient4; + JT808SimpleTcpClient SimpleTcpClient5; + + + JT808SimpleUdpClient SimpleUdpClient1; + JT808SimpleUdpClient SimpleUdpClient2; + JT808SimpleUdpClient SimpleUdpClient3; + JT808SimpleUdpClient SimpleUdpClient4; + JT808SimpleUdpClient SimpleUdpClient5; + + + + + public TestBase() + { + SimpleTcpClient1 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient2 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient3 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient4 = new JT808SimpleTcpClient(endPoint); + SimpleTcpClient5 = new JT808SimpleTcpClient(endPoint); + // 心跳会话包 + JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001"); + SimpleTcpClient1.WriteAsync(JT808Serializer.Serialize(jT808Package1)); + + // 心跳会话包 + JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789002"); + SimpleTcpClient2.WriteAsync(JT808Serializer.Serialize(jT808Package2)); + + // 心跳会话包 + JT808Package jT808Package3 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789003"); + SimpleTcpClient3.WriteAsync(JT808Serializer.Serialize(jT808Package3)); + + // 心跳会话包 + JT808Package jT808Package4 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789004"); + SimpleTcpClient4.WriteAsync(JT808Serializer.Serialize(jT808Package4)); + + // 心跳会话包 + JT808Package jT808Package5 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789005"); + SimpleTcpClient5.WriteAsync(JT808Serializer.Serialize(jT808Package5)); + + SimpleUdpClient1 = new JT808SimpleUdpClient(endPoint1); + SimpleUdpClient2 = new JT808SimpleUdpClient(endPoint1); + SimpleUdpClient3 = new JT808SimpleUdpClient(endPoint1); + SimpleUdpClient4 = new JT808SimpleUdpClient(endPoint1); + SimpleUdpClient5 = new JT808SimpleUdpClient(endPoint1); + // 心跳会话包 + JT808Package jT808Package12 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789011"); + SimpleUdpClient1.WriteAsync(JT808Serializer.Serialize(jT808Package12)); + Thread.Sleep(300); + // 心跳会话包 + JT808Package jT808Package23 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789012"); + SimpleUdpClient2.WriteAsync(JT808Serializer.Serialize(jT808Package23)); + Thread.Sleep(300); + // 心跳会话包 + JT808Package jT808Package34 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789013"); + SimpleUdpClient3.WriteAsync(JT808Serializer.Serialize(jT808Package34)); + Thread.Sleep(300); + // 心跳会话包 + JT808Package jT808Package45 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789014"); + SimpleUdpClient4.WriteAsync(JT808Serializer.Serialize(jT808Package45)); + Thread.Sleep(300); + // 心跳会话包 + JT808Package jT808Package56 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789015"); + SimpleUdpClient5.WriteAsync(JT808Serializer.Serialize(jT808Package56)); + + Thread.Sleep(300); + } + + public void Dispose() + { + SimpleTcpClient1.Down(); + SimpleTcpClient2.Down(); + SimpleTcpClient3.Down(); + SimpleTcpClient4.Down(); + SimpleTcpClient5.Down(); + + SimpleUdpClient1.Down(); + SimpleUdpClient2.Down(); + SimpleUdpClient3.Down(); + SimpleUdpClient4.Down(); + SimpleUdpClient5.Down(); + } + } +} diff --git a/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/appsettings.json b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/appsettings.json new file mode 100644 index 0000000..66be72e --- /dev/null +++ b/src/JT808.DotNetty.Tests/JT808.DotNetty.WebApi.Test/appsettings.json @@ -0,0 +1,27 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Trace" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace" + } + } + }, + + "JT808Configuration": { + "TcpPort": 12808, + "UdpPort": 12818, + "WebApiPort": 12828, + "ForwardingRemoteIPAddress": [ + "127.0.0.1" + ] + }, + "JT808DotNettyWebApiOptions": { + "WebApiHosts": [ "127.0.0.1:12828" ] + } +} diff --git a/src/JT808.DotNetty.Udp/Handlers/JT808MsgIdDefaultUdpHandler.cs b/src/JT808.DotNetty.Udp/Handlers/JT808MsgIdDefaultUdpHandler.cs new file mode 100644 index 0000000..0092851 --- /dev/null +++ b/src/JT808.DotNetty.Udp/Handlers/JT808MsgIdDefaultUdpHandler.cs @@ -0,0 +1,15 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; + +namespace JT808.DotNetty.Udp.Handlers +{ + /// + /// 默认消息处理业务实现 + /// + internal class JT808MsgIdDefaultUdpHandler : JT808MsgIdUdpHandlerBase + { + public JT808MsgIdDefaultUdpHandler(JT808UdpSessionManager sessionManager) : base(sessionManager) + { + } + } +} diff --git a/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs b/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs new file mode 100644 index 0000000..41bb207 --- /dev/null +++ b/src/JT808.DotNetty.Udp/Handlers/JT808UdpServerHandler.cs @@ -0,0 +1,139 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using JT808.Protocol; +using System; +using Microsoft.Extensions.Logging; +using DotNetty.Transport.Channels.Sockets; +using JT808.DotNetty.Core.Metadata; +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Core.Services; +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Handlers; +using System.Threading.Tasks; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Abstractions.Enums; +using JT808.Protocol.Interfaces; + +namespace JT808.DotNetty.Udp.Handlers +{ + /// + /// JT808 Udp服务端处理程序 + /// + internal class JT808UdpServerHandler : SimpleChannelInboundHandler + { + private readonly IJT808SourcePackageDispatcher jT808SourcePackageDispatcher; + + private readonly JT808AtomicCounterService jT808AtomicCounterService; + + private readonly ILogger logger; + + private readonly JT808UdpSessionManager jT808UdpSessionManager; + + private readonly JT808MsgIdUdpHandlerBase handler; + + private readonly JT808TrafficService jT808TrafficService; + + private readonly IJT808UplinkPacket jT808UplinkPacket; + + private readonly IJT808UdpCustomMsgIdHandler jT808UdpCustomMsgIdHandler; + + private readonly IJT808DatagramPacket jT808DatagramPacket; + + private readonly ILogger unknownLogger; + + private readonly JT808Serializer JT808Serializer; + + public JT808UdpServerHandler( + IJT808Config jT808Config, + IJT808DatagramPacket jT808DatagramPacket, + JT808TrafficServiceFactory jT808TrafficServiceFactory, + ILoggerFactory loggerFactory, + IJT808SourcePackageDispatcher jT808SourcePackageDispatcher, + IJT808UplinkPacket jT808UplinkPacket, + JT808MsgIdUdpHandlerBase handler, + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory, + IJT808UdpCustomMsgIdHandler jT808UdpCustomMsgIdHandler, + JT808UdpSessionManager jT808UdpSessionManager) + { + this.jT808UdpCustomMsgIdHandler = jT808UdpCustomMsgIdHandler; + this.jT808DatagramPacket = jT808DatagramPacket; + this.jT808TrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.udp); + this.handler = handler; + this.jT808SourcePackageDispatcher = jT808SourcePackageDispatcher; + this.jT808AtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.udp); + this.jT808UplinkPacket = jT808UplinkPacket; + this.jT808UdpSessionManager = jT808UdpSessionManager; + logger = loggerFactory.CreateLogger(); + JT808Serializer = jT808Config.GetSerializer(); + unknownLogger = loggerFactory.CreateLogger("udp_unknown_msgid"); + } + + protected override void ChannelRead0(IChannelHandlerContext ctx, JT808UdpPackage msg) + { + try + { + jT808SourcePackageDispatcher.SendAsync(msg.Buffer); + jT808UplinkPacket.ProcessorAsync(msg.Buffer, JT808TransportProtocolType.udp); + //解析到头部,然后根据具体的消息Id通过队列去进行消费 + //要是一定要解析到数据体可以在JT808MsgIdHandlerBase类中根据具体的消息, + //解析具体的消息体,具体调用JT808Serializer.Deserialize + JT808HeaderPackage jT808HeaderPackage = JT808Serializer.Deserialize(msg.Buffer); + jT808AtomicCounterService.MsgSuccessIncrement(); + jT808TrafficService.ReceiveSize(msg.Buffer.Length); + jT808UdpSessionManager.TryAdd(ctx.Channel, msg.Sender, jT808HeaderPackage.Header.TerminalPhoneNo); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("accept package success count<<<" + jT808AtomicCounterService.MsgSuccessCount.ToString()); + logger.LogTrace("accept msg <<< " + ByteBufferUtil.HexDump(msg.Buffer)); + } + if (handler.HandlerDict.TryGetValue(jT808HeaderPackage.Header.MsgId, out var handlerFunc)) + { + IJT808Reply jT808Response = handlerFunc(new JT808Request(jT808HeaderPackage, msg.Buffer)); + if (jT808Response != null) + { + var sendData = JT808Serializer.Serialize(jT808Response.Package, jT808Response.MinBufferSize); + ctx.WriteAndFlushAsync(jT808DatagramPacket.Create(sendData,msg.Sender)); + } + } + else + { + IJT808Reply jT808CustomMsgIdResponse = jT808UdpCustomMsgIdHandler.Proccesser(new JT808Request(jT808HeaderPackage, msg.Buffer)); + if (jT808CustomMsgIdResponse != null) + { + var sendData = JT808Serializer.Serialize(jT808CustomMsgIdResponse.Package, jT808CustomMsgIdResponse.MinBufferSize); + ctx.WriteAndFlushAsync(jT808DatagramPacket.Create(sendData, msg.Sender)); + } + else + { + //未知的消息类型已日志形式输出 + if (unknownLogger.IsEnabled(LogLevel.Debug)) + { + unknownLogger.LogDebug(ByteBufferUtil.HexDump(msg.Buffer)); + } + } + } + } + catch (JT808.Protocol.Exceptions.JT808Exception ex) + { + jT808AtomicCounterService.MsgFailIncrement(); + if (logger.IsEnabled(LogLevel.Error)) + { + logger.LogError("accept package fail count<<<" + jT808AtomicCounterService.MsgFailCount.ToString()); + logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg.Buffer)); + } + } + catch (Exception ex) + { + jT808AtomicCounterService.MsgFailIncrement(); + if (logger.IsEnabled(LogLevel.Error)) + { + logger.LogError("accept package fail count<<<" + jT808AtomicCounterService.MsgFailCount.ToString()); + logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg.Buffer)); + } + } + } + + public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); + + } +} diff --git a/src/JT808.DotNetty.Udp/JT1078UdpBuilderDefault.cs b/src/JT808.DotNetty.Udp/JT1078UdpBuilderDefault.cs new file mode 100644 index 0000000..1fcd18f --- /dev/null +++ b/src/JT808.DotNetty.Udp/JT1078UdpBuilderDefault.cs @@ -0,0 +1,49 @@ +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.Udp +{ + class JT1078UdpBuilderDefault : IJT808UdpNettyBuilder + { + public IJT808NettyBuilder Instance { get; } + + public JT1078UdpBuilderDefault(IJT808NettyBuilder builder) + { + Instance = builder; + } + + public IJT808NettyBuilder Builder() + { + return Instance; + } + + public IJT808UdpNettyBuilder ReplaceCustomMsgIdHandler() where T : IJT808UdpCustomMsgIdHandler + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808UdpCustomMsgIdHandler), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808UdpNettyBuilder ReplaceMsgIdHandler() where T : JT808MsgIdUdpHandlerBase + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(JT808MsgIdUdpHandlerBase), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808UdpNettyBuilder ReplaceSessionService() where T : IJT808UdpSessionService + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808UdpSessionService), typeof(T), ServiceLifetime.Singleton)); + return this; + } + + public IJT808UdpNettyBuilder ReplaceUnificationSendService() where T : IJT808UnificationUdpSendService + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(IJT808UnificationUdpSendService), typeof(T), ServiceLifetime.Singleton)); + return this; + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Udp/JT808.DotNetty.Udp.csproj b/src/JT808.DotNetty.Udp/JT808.DotNetty.Udp.csproj new file mode 100644 index 0000000..42db860 --- /dev/null +++ b/src/JT808.DotNetty.Udp/JT808.DotNetty.Udp.csproj @@ -0,0 +1,23 @@ + + + + JT808.DotNetty.Udp + JT808.DotNetty.Udp + 基于DotNetty实现的JT808DotNetty的Udp服务 + 基于DotNetty实现的JT808DotNetty的Udp服务 + + + + + + + + + + + + + + + + diff --git a/src/JT808.DotNetty.Udp/JT808UdpDotnettyExtensions.cs b/src/JT808.DotNetty.Udp/JT808UdpDotnettyExtensions.cs new file mode 100644 index 0000000..5f93ce9 --- /dev/null +++ b/src/JT808.DotNetty.Udp/JT808UdpDotnettyExtensions.cs @@ -0,0 +1,38 @@ +using JT808.DotNetty.Core; +using JT808.DotNetty.Core.Codecs; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Impls; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Jobs; +using JT808.DotNetty.Core.Services; +using JT808.DotNetty.Internal; +using JT808.DotNetty.Udp.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Internal; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("JT808.DotNetty.Udp.Test")] + +namespace JT808.DotNetty.Udp +{ + public static class JT808UdpDotnettyExtensions + { + public static IJT808UdpNettyBuilder AddJT808UdpNettyHost(this IJT808NettyBuilder jT808NettyBuilder) + { + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + return new JT1078UdpBuilderDefault(jT808NettyBuilder); + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.Udp/JT808UdpServerHost.cs b/src/JT808.DotNetty.Udp/JT808UdpServerHost.cs new file mode 100644 index 0000000..5c3a7eb --- /dev/null +++ b/src/JT808.DotNetty.Udp/JT808UdpServerHost.cs @@ -0,0 +1,76 @@ +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using JT808.DotNetty.Core.Codecs; +using JT808.DotNetty.Core.Configurations; +using JT808.DotNetty.Udp.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.Udp +{ + /// + /// JT808 Udp网关服务 + /// + internal class JT808UdpServerHost : IHostedService + { + private readonly IServiceProvider serviceProvider; + private readonly JT808Configuration configuration; + private readonly ILogger logger; + private MultithreadEventLoopGroup group; + private IChannel bootstrapChannel; + + public JT808UdpServerHost( + IServiceProvider provider, + ILoggerFactory loggerFactory, + IOptions jT808ConfigurationAccessor) + { + serviceProvider = provider; + configuration = jT808ConfigurationAccessor.Value; + logger=loggerFactory.CreateLogger(); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + group = new MultithreadEventLoopGroup(); + Bootstrap bootstrap = new Bootstrap(); + bootstrap.Group(group); + bootstrap.Channel(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + bootstrap + .Option(ChannelOption.SoReuseport, true); + } + bootstrap + .Option(ChannelOption.SoBroadcast, true) + .Handler(new ActionChannelInitializer(channel => + { + IChannelPipeline pipeline = channel.Pipeline; + using (var scope = serviceProvider.CreateScope()) + { + pipeline.AddLast("jt808UdpDecoder", scope.ServiceProvider.GetRequiredService()); + pipeline.AddLast("jt808UdpService", scope.ServiceProvider.GetRequiredService()); + } + })); + logger.LogInformation($"JT808 Udp Server start at {IPAddress.Any}:{configuration.UdpPort}."); + return bootstrap.BindAsync(configuration.UdpPort) + .ContinueWith(i => bootstrapChannel = i.Result); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + await bootstrapChannel.CloseAsync(); + var quietPeriod = configuration.QuietPeriodTimeSpan; + var shutdownTimeout = configuration.ShutdownTimeoutTimeSpan; + await group.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + } + } +} diff --git a/src/JT808.DotNetty.Udp/Properties/PublishProfiles/FolderProfile.pubxml b/src/JT808.DotNetty.Udp/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..e572661 --- /dev/null +++ b/src/JT808.DotNetty.Udp/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + FileSystem + Release + Any CPU + netstandard2.0 + ..\..\nupkgs + + \ No newline at end of file diff --git a/src/JT808.DotNetty.Udp/Properties/PublishProfiles/FolderProfile.pubxml.user b/src/JT808.DotNetty.Udp/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/src/JT808.DotNetty.Udp/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.WebApi/Handlers/JT808MsgIdDefaultWebApiHandler.cs b/src/JT808.DotNetty.WebApi/Handlers/JT808MsgIdDefaultWebApiHandler.cs new file mode 100644 index 0000000..0c7c775 --- /dev/null +++ b/src/JT808.DotNetty.WebApi/Handlers/JT808MsgIdDefaultWebApiHandler.cs @@ -0,0 +1,334 @@ +using JT808.DotNetty.Abstractions; +using JT808.DotNetty.Abstractions.Dtos; +using JT808.DotNetty.Abstractions.Enums; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.Core.Metadata; +using JT808.DotNetty.Core.Services; +using Newtonsoft.Json; + +namespace JT808.DotNetty.WebApi.Handlers +{ + /// + /// 默认消息处理业务实现 + /// + public class JT808MsgIdDefaultWebApiHandler : JT808MsgIdHttpHandlerBase + { + private readonly JT808AtomicCounterService jT808TcpAtomicCounterService; + + private readonly JT808AtomicCounterService jT808UdpAtomicCounterService; + + private readonly JT808TransmitAddressFilterService jT808TransmitAddressFilterService; + + private readonly IJT808TcpSessionService jT808TcpSessionService; + + private readonly IJT808UdpSessionService jT808UdpSessionService; + + private readonly IJT808UnificationTcpSendService jT808UnificationTcpSendService; + + private readonly IJT808UnificationUdpSendService jT808UnificationUdpSendService; + + private readonly JT808TrafficService jT808TcpTrafficService; + + private readonly JT808TrafficService jT808UdpTrafficService; + + private readonly JT808SimpleSystemCollectService jT808SimpleSystemCollectService; + + /// + /// TCP一套注入 + /// + /// + public JT808MsgIdDefaultWebApiHandler( + JT808SimpleSystemCollectService jT808SimpleSystemCollectService, + JT808TrafficServiceFactory jT808TrafficServiceFactory, + IJT808UnificationTcpSendService jT808UnificationTcpSendService, + IJT808TcpSessionService jT808TcpSessionService, + JT808TransmitAddressFilterService jT808TransmitAddressFilterService, + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory + ) + { + this.jT808SimpleSystemCollectService = jT808SimpleSystemCollectService; + this.jT808TcpTrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.tcp); + this.jT808UnificationTcpSendService = jT808UnificationTcpSendService; + this.jT808TcpSessionService = jT808TcpSessionService; + this.jT808TransmitAddressFilterService = jT808TransmitAddressFilterService; + this.jT808TcpAtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.tcp); + InitCommonRoute(); + InitTcpRoute(); + } + + /// + /// UDP一套注入 + /// + /// + public JT808MsgIdDefaultWebApiHandler( + JT808SimpleSystemCollectService jT808SimpleSystemCollectService, + JT808TrafficServiceFactory jT808TrafficServiceFactory, + IJT808UdpSessionService jT808UdpSessionService, + IJT808UnificationUdpSendService jT808UnificationUdpSendService, + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory + ) + { + this.jT808SimpleSystemCollectService = jT808SimpleSystemCollectService; + this.jT808UdpTrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.udp); + this.jT808UdpSessionService = jT808UdpSessionService; + this.jT808UnificationUdpSendService = jT808UnificationUdpSendService; + this.jT808UdpAtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.udp); + InitCommonRoute(); + InitUdpRoute(); + } + + /// + /// 统一的一套注入 + /// + /// + /// + public JT808MsgIdDefaultWebApiHandler( + JT808SimpleSystemCollectService jT808SimpleSystemCollectService, + JT808TrafficServiceFactory jT808TrafficServiceFactory, + IJT808UnificationTcpSendService jT808UnificationTcpSendService, + IJT808UnificationUdpSendService jT808UnificationUdpSendService, + IJT808TcpSessionService jT808TcpSessionService, + IJT808UdpSessionService jT808UdpSessionService, + JT808TransmitAddressFilterService jT808TransmitAddressFilterService, + JT808AtomicCounterServiceFactory jT808AtomicCounterServiceFactory + ) + { + this.jT808SimpleSystemCollectService = jT808SimpleSystemCollectService; + this.jT808TcpTrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.tcp); + this.jT808UdpTrafficService = jT808TrafficServiceFactory.Create(JT808TransportProtocolType.udp); + this.jT808UdpSessionService = jT808UdpSessionService; + this.jT808UnificationTcpSendService = jT808UnificationTcpSendService; + this.jT808UnificationUdpSendService = jT808UnificationUdpSendService; + this.jT808TcpSessionService = jT808TcpSessionService; + this.jT808TransmitAddressFilterService = jT808TransmitAddressFilterService; + this.jT808TcpAtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.tcp); + this.jT808UdpAtomicCounterService = jT808AtomicCounterServiceFactory.Create(JT808TransportProtocolType.udp); + InitCommonRoute(); + InitTcpRoute(); + InitUdpRoute(); + } + + /// + /// 会话服务集合 + /// + /// + /// + public JT808HttpResponse GetTcpSessionAll(JT808HttpRequest request) + { + var result = jT808TcpSessionService.GetAll(); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-通过设备终端号移除对应会话 + /// + /// + /// + public JT808HttpResponse RemoveTcpSessionByTerminalPhoneNo(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + var result = jT808TcpSessionService.RemoveByTerminalPhoneNo(request.Json); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务集合 + /// + /// + /// + public JT808HttpResponse GetUdpSessionAll(JT808HttpRequest request) + { + var result = jT808UdpSessionService.GetAll(); + return CreateJT808HttpResponse(result); + } + + /// + /// 会话服务-通过设备终端号移除对应会话 + /// + /// + /// + public JT808HttpResponse RemoveUdpSessionByTerminalPhoneNo(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + var result = jT808UdpSessionService.RemoveByTerminalPhoneNo(request.Json); + return CreateJT808HttpResponse(result); + } + + /// + /// 添加转发过滤地址 + /// + /// + /// + public JT808HttpResponse AddTransmitAddress(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + return CreateJT808HttpResponse(jT808TransmitAddressFilterService.Add(request.Json)); + } + + /// + /// 删除转发过滤地址(不能删除在网关服务器配置文件配的地址) + /// + /// + /// + public JT808HttpResponse RemoveTransmitAddress(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + return CreateJT808HttpResponse(jT808TransmitAddressFilterService.Remove(request.Json)); + } + + /// + /// 获取转发过滤地址信息集合 + /// + /// + /// + public JT808HttpResponse GetTransmitAll(JT808HttpRequest request) + { + return CreateJT808HttpResponse(jT808TransmitAddressFilterService.GetAll()); + } + + /// + /// 获取Tcp包计数器 + /// + /// + /// + public JT808HttpResponse GetTcpAtomicCounter(JT808HttpRequest request) + { + JT808AtomicCounterDto jT808AtomicCounterDto = new JT808AtomicCounterDto(); + jT808AtomicCounterDto.MsgFailCount = jT808TcpAtomicCounterService.MsgFailCount; + jT808AtomicCounterDto.MsgSuccessCount = jT808TcpAtomicCounterService.MsgSuccessCount; + return CreateJT808HttpResponse(new JT808ResultDto + { + Code = JT808ResultCode.Ok, + Data = jT808AtomicCounterDto + }); + } + + /// + /// 获取Udp包计数器 + /// + /// + /// + public JT808HttpResponse GetUdpAtomicCounter(JT808HttpRequest request) + { + JT808AtomicCounterDto jT808AtomicCounterDto = new JT808AtomicCounterDto(); + jT808AtomicCounterDto.MsgFailCount = jT808UdpAtomicCounterService.MsgFailCount; + jT808AtomicCounterDto.MsgSuccessCount = jT808UdpAtomicCounterService.MsgSuccessCount; + return CreateJT808HttpResponse(new JT808ResultDto + { + Code = JT808ResultCode.Ok, + Data = jT808AtomicCounterDto + }); + } + + /// + /// 基于Tcp的统一下发信息 + /// + /// + /// + public JT808HttpResponse UnificationTcpSend(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + JT808UnificationSendRequestDto jT808UnificationSendRequestDto = JsonConvert.DeserializeObject(request.Json); + var result = jT808UnificationTcpSendService.Send(jT808UnificationSendRequestDto.TerminalPhoneNo, jT808UnificationSendRequestDto.Data); + return CreateJT808HttpResponse(result); + } + + /// + /// 基于Udp的统一下发信息 + /// + /// + /// + public JT808HttpResponse UnificationUdpSend(JT808HttpRequest request) + { + if (string.IsNullOrEmpty(request.Json)) + { + return EmptyHttpResponse(); + } + JT808UnificationSendRequestDto jT808UnificationSendRequestDto = JsonConvert.DeserializeObject(request.Json); + var result = jT808UnificationUdpSendService.Send(jT808UnificationSendRequestDto.TerminalPhoneNo, jT808UnificationSendRequestDto.Data); + return CreateJT808HttpResponse(result); + } + + /// + /// 基于Tcp的流量获取 + /// + /// + /// + public JT808HttpResponse TrafficTcpGet(JT808HttpRequest request) + { + JT808ResultDto jT808ResultDto = new JT808ResultDto(); + jT808ResultDto.Data = new JT808TrafficInfoDto(); + jT808ResultDto.Data.TotalReceiveSize = (jT808TcpTrafficService.TotalReceiveSize * 1.0) / 1024; + jT808ResultDto.Data.TotalSendSize = (jT808TcpTrafficService.TotalSendSize * 1.0) / 1024; + return CreateJT808HttpResponse(jT808ResultDto); + } + + /// + /// 基于Udp的流量获取 + /// + /// + /// + public JT808HttpResponse TrafficUdpGet(JT808HttpRequest request) + { + JT808ResultDto jT808ResultDto = new JT808ResultDto(); + jT808ResultDto.Data = new JT808TrafficInfoDto(); + jT808ResultDto.Data.TotalReceiveSize = (jT808UdpTrafficService.TotalReceiveSize * 1.0) / 1024; + jT808ResultDto.Data.TotalSendSize = (jT808UdpTrafficService.TotalSendSize * 1.0) / 1024; + return CreateJT808HttpResponse(jT808ResultDto); + } + + /// + /// 获取当前系统进程使用率 + /// + /// + /// + public JT808HttpResponse SystemCollectGet(JT808HttpRequest request) + { + JT808ResultDto jT808ResultDto = new JT808ResultDto(); + jT808ResultDto.Data = jT808SimpleSystemCollectService.Get(); + return CreateJT808HttpResponse(jT808ResultDto); + } + + protected virtual void InitCommonRoute() + { + CreateRoute(JT808Constants.JT808WebApiRouteTable.SystemCollectGet, SystemCollectGet); + } + + protected virtual void InitTcpRoute() + { + CreateRoute(JT808Constants.JT808WebApiRouteTable.TransmitAdd, AddTransmitAddress); + CreateRoute(JT808Constants.JT808WebApiRouteTable.TransmitRemove, RemoveTransmitAddress); + CreateRoute(JT808Constants.JT808WebApiRouteTable.TransmitGetAll, GetTransmitAll); + CreateRoute(JT808Constants.JT808WebApiRouteTable.GetTcpAtomicCounter, GetTcpAtomicCounter); + CreateRoute(JT808Constants.JT808WebApiRouteTable.SessionTcpGetAll, GetTcpSessionAll); + CreateRoute(JT808Constants.JT808WebApiRouteTable.SessionTcpRemoveByTerminalPhoneNo, RemoveTcpSessionByTerminalPhoneNo); + CreateRoute(JT808Constants.JT808WebApiRouteTable.UnificationTcpSend, UnificationTcpSend); + CreateRoute(JT808Constants.JT808WebApiRouteTable.TrafficTcpGet, TrafficTcpGet); + } + + protected virtual void InitUdpRoute() + { + CreateRoute(JT808Constants.JT808WebApiRouteTable.GetUdpAtomicCounter, GetUdpAtomicCounter); + CreateRoute(JT808Constants.JT808WebApiRouteTable.UnificationUdpSend, UnificationUdpSend); + CreateRoute(JT808Constants.JT808WebApiRouteTable.SessionUdpGetAll, GetUdpSessionAll); + CreateRoute(JT808Constants.JT808WebApiRouteTable.SessionUdpRemoveByTerminalPhoneNo, RemoveUdpSessionByTerminalPhoneNo); + CreateRoute(JT808Constants.JT808WebApiRouteTable.TrafficUdpGet, TrafficUdpGet); + } + } +} diff --git a/src/JT808.DotNetty.WebApi/Handlers/JT808WebAPIServerHandler.cs b/src/JT808.DotNetty.WebApi/Handlers/JT808WebAPIServerHandler.cs new file mode 100644 index 0000000..159625d --- /dev/null +++ b/src/JT808.DotNetty.WebApi/Handlers/JT808WebAPIServerHandler.cs @@ -0,0 +1,82 @@ +using DotNetty.Buffers; +using DotNetty.Codecs.Http; +using DotNetty.Common.Utilities; +using DotNetty.Transport.Channels; +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Metadata; +using Microsoft.Extensions.Logging; +using System; +using System.Text; + +namespace JT808.DotNetty.WebApi.Handlers +{ + /// + /// jt808 webapi服务 + /// 请求量不大,只支持JSON格式并且只支持post发数据 + /// ref: dotnetty HttpServer + /// + internal class JT808WebAPIServerHandler : SimpleChannelInboundHandler + { + private static readonly AsciiString TypeJson = AsciiString.Cached("application/json"); + private static readonly AsciiString ServerName = AsciiString.Cached("JT808WebAPINetty"); + private static readonly AsciiString ContentTypeEntity = HttpHeaderNames.ContentType; + private static readonly AsciiString DateEntity = HttpHeaderNames.Date; + private static readonly AsciiString ContentLengthEntity = HttpHeaderNames.ContentLength; + private static readonly AsciiString ServerEntity = HttpHeaderNames.Server; + private readonly JT808MsgIdHttpHandlerBase jT808MsgIdHttpHandlerBase; + private readonly ILogger logger; + + public JT808WebAPIServerHandler( + JT808MsgIdHttpHandlerBase jT808MsgIdHttpHandlerBase, + ILoggerFactory loggerFactory) + { + this.jT808MsgIdHttpHandlerBase = jT808MsgIdHttpHandlerBase; + logger = loggerFactory.CreateLogger(); + } + + protected override void ChannelRead0(IChannelHandlerContext ctx, IFullHttpRequest msg) + { + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug($"Uri:{msg.Uri}"); + logger.LogDebug($"Content:{msg.Content.ToString(Encoding.UTF8)}"); + } + JT808HttpResponse jT808HttpResponse = null; + if (jT808MsgIdHttpHandlerBase.HandlerDict.TryGetValue(msg.Uri,out var funcHandler)) + { + jT808HttpResponse = funcHandler( new JT808HttpRequest() { Json = msg.Content.ToString(Encoding.UTF8)}); + } + else + { + jT808HttpResponse = jT808MsgIdHttpHandlerBase.NotFoundHttpResponse(); + } + if (jT808HttpResponse != null) + { + WriteResponse(ctx, Unpooled.WrappedBuffer(jT808HttpResponse.Data), TypeJson, jT808HttpResponse.Data.Length); + } + } + + private void WriteResponse(IChannelHandlerContext ctx, IByteBuffer buf, ICharSequence contentType, int contentLength) + { + // Build the response object. + var response = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK, buf, false); + HttpHeaders headers = response.Headers; + headers.Set(ContentTypeEntity, contentType); + headers.Set(ServerEntity, ServerName); + headers.Set(DateEntity, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); + headers.Set(ContentLengthEntity, contentLength); + // Close the non-keep-alive connection after the write operation is done. + ctx.WriteAndFlushAsync(response); + ctx.CloseAsync(); + } + + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) + { + WriteResponse(context, Unpooled.WrappedBuffer(jT808MsgIdHttpHandlerBase.ErrorHttpResponse(exception).Data), TypeJson, jT808MsgIdHttpHandlerBase.ErrorHttpResponse(exception).Data.Length); + logger.LogError(exception, exception.Message); + context.CloseAsync(); + } + + public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); + } +} diff --git a/src/JT808.DotNetty.WebApi/JT808.DotNetty.WebApi.csproj b/src/JT808.DotNetty.WebApi/JT808.DotNetty.WebApi.csproj new file mode 100644 index 0000000..b7ddcd7 --- /dev/null +++ b/src/JT808.DotNetty.WebApi/JT808.DotNetty.WebApi.csproj @@ -0,0 +1,21 @@ + + + + JT808.DotNetty.WebApi + JT808.DotNetty.WebApi + 基于DotNetty实现的JT808DotNetty的WebApi服务 + 基于DotNetty实现的JT808DotNetty的WebApi服务 + + + + + + + + + + + + + + diff --git a/src/JT808.DotNetty.WebApi/JT808WebAPIServerHost.cs b/src/JT808.DotNetty.WebApi/JT808WebAPIServerHost.cs new file mode 100644 index 0000000..66cd93e --- /dev/null +++ b/src/JT808.DotNetty.WebApi/JT808WebAPIServerHost.cs @@ -0,0 +1,82 @@ +using DotNetty.Codecs.Http; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Libuv; +using JT808.DotNetty.Core.Configurations; +using JT808.DotNetty.WebApi.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace JT808.DotNetty.WebApi +{ + /// + /// JT808 集成一个webapi服务 + /// + internal class JT808WebAPIServerHost : IHostedService + { + private readonly IServiceProvider serviceProvider; + private readonly JT808Configuration configuration; + private readonly ILogger logger; + private DispatcherEventLoopGroup bossGroup; + private WorkerEventLoopGroup workerGroup; + private IChannel bootstrapChannel; + + public JT808WebAPIServerHost( + IServiceProvider provider, + ILoggerFactory loggerFactory, + IOptions jT808ConfigurationAccessor) + { + serviceProvider = provider; + configuration = jT808ConfigurationAccessor.Value; + logger = loggerFactory.CreateLogger(); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + bossGroup = new DispatcherEventLoopGroup(); + workerGroup = new WorkerEventLoopGroup(bossGroup, 1); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.Group(bossGroup, workerGroup); + bootstrap.Channel(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + bootstrap + .Option(ChannelOption.SoReuseport, true) + .ChildOption(ChannelOption.SoReuseaddr, true); + } + bootstrap + .Option(ChannelOption.SoBacklog, 8192) + .ChildHandler(new ActionChannelInitializer(channel => + { + IChannelPipeline pipeline = channel.Pipeline; + using (var scope = serviceProvider.CreateScope()) + { + pipeline.AddLast("http_encoder", new HttpResponseEncoder()); + pipeline.AddLast("http_decoder", new HttpRequestDecoder(4096, 8192, 8192, false)); + //将多个消息转换为单一的request或者response对象 =>IFullHttpRequest + pipeline.AddLast("http_aggregator", new HttpObjectAggregator(int.MaxValue)); + pipeline.AddLast("http_jt808webapihandler", scope.ServiceProvider.GetRequiredService()); + } + })); + logger.LogInformation($"JT808 WebAPI Server start at {IPAddress.Any}:{configuration.WebApiPort}."); + return bootstrap.BindAsync(configuration.WebApiPort).ContinueWith(i => bootstrapChannel = i.Result); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + await bootstrapChannel.CloseAsync(); + var quietPeriod = configuration.QuietPeriodTimeSpan; + var shutdownTimeout = configuration.ShutdownTimeoutTimeSpan; + await workerGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + await bossGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); + } + } +} diff --git a/src/JT808.DotNetty.WebApi/JT808WebApiBuilderDefault.cs b/src/JT808.DotNetty.WebApi/JT808WebApiBuilderDefault.cs new file mode 100644 index 0000000..53cda12 --- /dev/null +++ b/src/JT808.DotNetty.WebApi/JT808WebApiBuilderDefault.cs @@ -0,0 +1,30 @@ +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT808.DotNetty.WebApi +{ + public class JT808WebApiBuilderDefault : IJT808WebApiNettyBuilder + { + public JT808WebApiBuilderDefault(IJT808NettyBuilder jT808NettyBuilder) { + Instance = jT808NettyBuilder; + } + + public IJT808NettyBuilder Instance { get; } + + public IJT808NettyBuilder Builder() + { + return Instance; + } + + public IJT808WebApiNettyBuilder ReplaceMsgIdHandler() where T : JT808MsgIdHttpHandlerBase + { + Instance.JT808Builder.Services.Replace(new ServiceDescriptor(typeof(JT808MsgIdHttpHandlerBase), typeof(T), ServiceLifetime.Singleton)); + return this; + } + } +} diff --git a/src/JT808.DotNetty.WebApi/JT808WebApiDotnettyExtensions.cs b/src/JT808.DotNetty.WebApi/JT808WebApiDotnettyExtensions.cs new file mode 100644 index 0000000..f3b30fe --- /dev/null +++ b/src/JT808.DotNetty.WebApi/JT808WebApiDotnettyExtensions.cs @@ -0,0 +1,22 @@ +using JT808.DotNetty.Core.Handlers; +using JT808.DotNetty.Core.Interfaces; +using JT808.DotNetty.WebApi.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("JT808.DotNetty.WebApi.Test")] + +namespace JT808.DotNetty.WebApi +{ + public static class JT808WebApiDotnettyExtensions + { + public static IJT808WebApiNettyBuilder AddJT808WebApiNettyHost(this IJT808NettyBuilder jT808NettyBuilder) + { + jT808NettyBuilder.JT808Builder.Services.TryAddSingleton(); + jT808NettyBuilder.JT808Builder.Services.TryAddScoped(); + jT808NettyBuilder.JT808Builder.Services.AddHostedService(); + return new JT808WebApiBuilderDefault(jT808NettyBuilder); + } + } +} \ No newline at end of file diff --git a/src/JT808.DotNetty.WebApi/Properties/PublishProfiles/FolderProfile.pubxml b/src/JT808.DotNetty.WebApi/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..e572661 --- /dev/null +++ b/src/JT808.DotNetty.WebApi/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + FileSystem + Release + Any CPU + netstandard2.0 + ..\..\nupkgs + + \ No newline at end of file diff --git a/src/JT808.DotNetty.WebApi/Properties/PublishProfiles/FolderProfile.pubxml.user b/src/JT808.DotNetty.WebApi/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/src/JT808.DotNetty.WebApi/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.WebApiClientTool/IJT808DotNettyWebApi.cs b/src/JT808.DotNetty.WebApiClientTool/IJT808DotNettyWebApi.cs new file mode 100644 index 0000000..3f6613c --- /dev/null +++ b/src/JT808.DotNetty.WebApiClientTool/IJT808DotNettyWebApi.cs @@ -0,0 +1,112 @@ +using JT808.DotNetty.Abstractions.Dtos; +using System.Collections.Generic; +using WebApiClient; +using WebApiClient.Attributes; + +namespace JT808.DotNetty.WebApiClientTool +{ + public interface IJT808DotNettyWebApi : IHttpApi + { + #region 基于Tcp WebApi + /// + /// 会话服务集合 + /// + /// + [HttpGet("Tcp/Session/GetAll")] + ITask>> GetTcpSessionAll(); + /// + /// 会话服务-通过设备终端号移除对应会话 + /// + /// + /// + [HttpPost("Tcp/Session/RemoveByTerminalPhoneNo")] + ITask> RemoveTcpSessionByTerminalPhoneNo([JsonContent] string terminalPhoneNo); + /// + /// 基于Tcp的统一下发信息 + /// + /// + /// + [HttpPost("Tcp/UnificationSend")] + ITask> UnificationTcpSend([JsonContent]JT808UnificationSendRequestDto jT808UnificationSendRequestDto); + /// + /// 添加转发过滤地址 + /// + /// + /// + [HttpPost("Tcp/Transmit/Add")] + ITask> AddTransmitAddress([JsonContent]JT808IPAddressDto jT808IPAddressDto); + /// + /// 删除转发过滤地址(不能删除在网关服务器配置文件配的地址) + /// + /// + /// + [HttpPost("Tcp/Transmit/Remove")] + ITask> RemoveTransmitAddress([JsonContent]JT808IPAddressDto jT808IPAddressDto); + /// + /// 获取转发过滤地址信息集合 + /// + /// + [HttpGet("Tcp/Transmit/GetAll")] + ITask>> GetTransmitAll(); + /// + /// 获取Tcp包计数器 + /// + /// + /// + [HttpGet("Tcp/GetAtomicCounter")] + ITask> GetTcpAtomicCounter(); + /// + /// 基于Tcp的流量获取 + /// + /// + [HttpGet("Tcp/Traffic/Get")] + ITask> GetTcpTraffic(); + + #endregion + + #region 基于Udp WebApi + /// + /// 会话服务集合 + /// + /// + /// + [HttpGet("Udp/Session/GetAll")] + ITask>> GetUdpSessionAll(); + /// + /// 会话服务-通过设备终端号移除对应会话 + /// + /// + /// + [HttpPost("Udp/Session/RemoveByTerminalPhoneNo")] + ITask> RemoveUdpSessionByTerminalPhoneNo([JsonContent] string terminalPhoneNo); + /// + /// /基于Udp的统一下发信息 + /// + /// + /// + [HttpPost("Udp/UnificationSend")] + ITask> UnificationUdpSend([JsonContent]JT808UnificationSendRequestDto jT808UnificationSendRequestDto); + /// + /// 基于Udp的流量获取 + /// + /// + [HttpGet("Udp/Traffic/Get")] + ITask> GetUdpTraffic(); + /// + /// 获取Udp包计数器 + /// + /// + [HttpGet("Udp/GetAtomicCounter")] + ITask> GetUdpAtomicCounter(); + #endregion + + #region 公共部分 + /// + /// 获取当前系统进程使用率 + /// + /// + [HttpGet("SystemCollect/Get")] + ITask> GetSystemCollect(); + #endregion + } +} diff --git a/src/JT808.DotNetty.WebApiClientTool/JT808.DotNetty.WebApiClientTool.csproj b/src/JT808.DotNetty.WebApiClientTool/JT808.DotNetty.WebApiClientTool.csproj new file mode 100644 index 0000000..a05ec5f --- /dev/null +++ b/src/JT808.DotNetty.WebApiClientTool/JT808.DotNetty.WebApiClientTool.csproj @@ -0,0 +1,18 @@ + + + + JT808.DotNetty.WebApiClientTool + JT808.DotNetty.WebApiClientTool + JT808DotNetty的WebApiClient客户端调用工具 + JT808DotNetty的WebApiClient客户端调用工具 + + + + + + + + + + + diff --git a/src/JT808.DotNetty.WebApiClientTool/JT808DotNettyWebApiOptions.cs b/src/JT808.DotNetty.WebApiClientTool/JT808DotNettyWebApiOptions.cs new file mode 100644 index 0000000..8286ba3 --- /dev/null +++ b/src/JT808.DotNetty.WebApiClientTool/JT808DotNettyWebApiOptions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace JT808.DotNetty.WebApiClientTool +{ + public class JT808DotNettyWebApiOptions + { + /// + /// 接口服务地址 + /// + public List WebApiHosts { get; set; } + } +} diff --git a/src/JT808.DotNetty.WebApiClientTool/Properties/PublishProfiles/FolderProfile.pubxml b/src/JT808.DotNetty.WebApiClientTool/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..e572661 --- /dev/null +++ b/src/JT808.DotNetty.WebApiClientTool/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + FileSystem + Release + Any CPU + netstandard2.0 + ..\..\nupkgs + + \ No newline at end of file diff --git a/src/JT808.DotNetty.WebApiClientTool/Properties/PublishProfiles/FolderProfile.pubxml.user b/src/JT808.DotNetty.WebApiClientTool/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/src/JT808.DotNetty.WebApiClientTool/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/JT808.DotNetty.sln b/src/JT808.DotNetty.sln new file mode 100644 index 0000000..c5f65be --- /dev/null +++ b/src/JT808.DotNetty.sln @@ -0,0 +1,122 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28922.388 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Udp", "JT808.DotNetty.Udp\JT808.DotNetty.Udp.csproj", "{C960084C-2CF4-4748-AD35-D2384285D6A3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Core", "JT808.DotNetty.Core\JT808.DotNetty.Core.csproj", "{67C5DC72-0004-48B3-BB5A-9CB7069B4F02}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Abstractions", "JT808.DotNetty.Abstractions\JT808.DotNetty.Abstractions.csproj", "{4DCF33C0-67C5-4179-AF1E-4E919F9F856D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3BD7FF02-8516-4A77-A385-9FDCDD792E22}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Tcp", "JT808.DotNetty.Tcp\JT808.DotNetty.Tcp.csproj", "{330CD783-5564-4083-ABFC-573CDC369F50}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApi", "JT808.DotNetty.WebApi\JT808.DotNetty.WebApi.csproj", "{B783DE53-CE2A-4225-921F-04E5E57B28F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Core.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Core.Test\JT808.DotNetty.Core.Test.csproj", "{1C4CCE9B-761B-4581-B5DA-5B6D83572D56}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Tcp.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Tcp.Test\JT808.DotNetty.Tcp.Test.csproj", "{AEF1E1E2-C861-4268-86F6-6F376FAF79A7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Udp.Test", "JT808.DotNetty.Tests\JT808.DotNetty.Udp.Test\JT808.DotNetty.Udp.Test.csproj", "{E503BFD8-D90A-4610-97C7-5B9A0497303B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApi.Test", "JT808.DotNetty.Tests\JT808.DotNetty.WebApi.Test\JT808.DotNetty.WebApi.Test.csproj", "{EDE77A29-0840-450C-8B08-2D3388845AE5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Hosting", "JT808.DotNetty.Tests\JT808.DotNetty.Hosting\JT808.DotNetty.Hosting.csproj", "{A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApiClientTool", "JT808.DotNetty.WebApiClientTool\JT808.DotNetty.WebApiClientTool.csproj", "{9D86C951-94F2-4CBD-B177-8AF31DDB05D8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Client", "JT808.DotNetty.Client\JT808.DotNetty.Client.csproj", "{87C08239-C57F-4FC5-9579-05D0723AA4A0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "simples", "simples", "{2459FB59-8A33-49A4-ADBC-A0B12C5886A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.SimpleClient", "..\simples\JT808.DotNetty.SimpleClient\JT808.DotNetty.SimpleClient.csproj", "{E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.SimpleServer", "..\simples\JT808.DotNetty.SimpleServer\JT808.DotNetty.SimpleServer.csproj", "{CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.CleintBenchmark", "JT808.DotNetty.CleintBenchmark\JT808.DotNetty.CleintBenchmark.csproj", "{C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C960084C-2CF4-4748-AD35-D2384285D6A3}.Release|Any CPU.Build.0 = Release|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67C5DC72-0004-48B3-BB5A-9CB7069B4F02}.Release|Any CPU.Build.0 = Release|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DCF33C0-67C5-4179-AF1E-4E919F9F856D}.Release|Any CPU.Build.0 = Release|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {330CD783-5564-4083-ABFC-573CDC369F50}.Release|Any CPU.Build.0 = Release|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B783DE53-CE2A-4225-921F-04E5E57B28F3}.Release|Any CPU.Build.0 = Release|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56}.Release|Any CPU.Build.0 = Release|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7}.Release|Any CPU.Build.0 = Release|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E503BFD8-D90A-4610-97C7-5B9A0497303B}.Release|Any CPU.Build.0 = Release|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDE77A29-0840-450C-8B08-2D3388845AE5}.Release|Any CPU.Build.0 = Release|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9}.Release|Any CPU.Build.0 = Release|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D86C951-94F2-4CBD-B177-8AF31DDB05D8}.Release|Any CPU.Build.0 = Release|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87C08239-C57F-4FC5-9579-05D0723AA4A0}.Release|Any CPU.Build.0 = Release|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5}.Release|Any CPU.Build.0 = Release|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88}.Release|Any CPU.Build.0 = Release|Any CPU + {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2B1A0F4-2C49-45DA-9F48-7A016FC6E9E1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1C4CCE9B-761B-4581-B5DA-5B6D83572D56} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {AEF1E1E2-C861-4268-86F6-6F376FAF79A7} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {E503BFD8-D90A-4610-97C7-5B9A0497303B} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {EDE77A29-0840-450C-8B08-2D3388845AE5} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {A0F2F006-5AEB-454E-83C5-ABFB58DE17A9} = {3BD7FF02-8516-4A77-A385-9FDCDD792E22} + {E6F61CE8-BFB4-4946-A0D3-AECCE77824E5} = {2459FB59-8A33-49A4-ADBC-A0B12C5886A6} + {CCE6AEFB-1AB0-4BD9-8EA2-8B4CDD097E88} = {2459FB59-8A33-49A4-ADBC-A0B12C5886A6} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FC0FFCEA-E1EF-4C97-A1C5-F89418B6834B} + EndGlobalSection +EndGlobal diff --git a/src/SharedProperties.props b/src/SharedProperties.props new file mode 100644 index 0000000..c5f522f --- /dev/null +++ b/src/SharedProperties.props @@ -0,0 +1,20 @@ + + + netstandard2.0 + 7.3 + Copyright 2018. + SmallChi(Koike) + https://github.com/SmallChi/JT808DotNetty + https://github.com/SmallChi/JT808DotNetty + https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE + https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE + false + 2.1.2 + false + LICENSE + true + + + + +