@@ -1,90 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.DotNetty.Metadata; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Extensions; | |||||
using JT808.Protocol.MessageBody; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Net.Http; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
public class JT808AtomicCounterServiceTest: TestBase | |||||
{ | |||||
private IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); | |||||
public JT808SimpleTcpClient SimpleTcpClient; | |||||
public JT808AtomicCounterServiceTest() | |||||
{ | |||||
SimpleTcpClient = new JT808SimpleTcpClient(endPoint); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808AtomicCounter jT808AtomicCounter = new JT808AtomicCounter(); | |||||
JT808AtomicCounter jT808AtomicCounter1 = new JT808AtomicCounter(); | |||||
Parallel.For(0, 1000, (i) => | |||||
{ | |||||
jT808AtomicCounter.Increment(); | |||||
}); | |||||
Assert.Equal(1000, jT808AtomicCounter.Count); | |||||
Parallel.For(0, 1000, (i) => | |||||
{ | |||||
jT808AtomicCounter1.Increment(); | |||||
}); | |||||
Assert.Equal(1000, jT808AtomicCounter1.Count); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
// 心跳会话包 | |||||
JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789001"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package1)); | |||||
// 心跳会话包 | |||||
JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789002"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package2)); | |||||
// 心跳会话包 | |||||
JT808Package jT808Package3 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789003"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package3)); | |||||
// 心跳会话包 | |||||
JT808Package jT808Package4 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789004"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package4)); | |||||
// 心跳会话包 | |||||
JT808Package jT808Package5 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789005"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package5)); | |||||
// 异步的需要延时下 | |||||
Thread.Sleep(1000); | |||||
HttpClient httpClient = new HttpClient(); | |||||
// 调用内置的http服务接收文本信息下发 | |||||
var result = httpClient.GetAsync("http://127.0.0.1:828/jt808api/GetAtomicCounter").Result; | |||||
string content = result.Content.ReadAsStringAsync().Result; | |||||
JT808ResultDto<JT808AtomicCounterDto> jt808Result = JsonConvert.DeserializeObject<JT808ResultDto<JT808AtomicCounterDto>>(content); | |||||
Assert.Equal(200, jt808Result.Code); | |||||
Assert.Equal(5,jt808Result.Data.MsgSuccessCount); | |||||
Assert.Equal(0, jt808Result.Data.MsgFailCount); | |||||
SimpleTcpClient.Down(); | |||||
} | |||||
} | |||||
} |
@@ -1,78 +0,0 @@ | |||||
using JT808.DotNetty.Internal; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using JT808.DotNetty.Configurations; | |||||
using Microsoft.Extensions.Options; | |||||
using Xunit; | |||||
using System.Threading.Tasks; | |||||
using System.Threading; | |||||
using StackExchange.Redis; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
public class JT808SessionPublishingRedisImplTest: TestBase | |||||
{ | |||||
JT808SessionPublishingRedisImpl jT808SessionPublishingRedisImpl; | |||||
public JT808SessionPublishingRedisImplTest() | |||||
{ | |||||
jT808SessionPublishingRedisImpl = new JT808SessionPublishingRedisImpl( | |||||
ServiceProvider.GetRequiredService<IOptionsMonitor<JT808Configuration>>()); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
int i = 10000; | |||||
Task.Run(() => { | |||||
while (i > 0) | |||||
{ | |||||
jT808SessionPublishingRedisImpl.PublishAsync(JT808Constants.SessionOnline, null, Guid.NewGuid().ToString("N")); | |||||
jT808SessionPublishingRedisImpl.PublishAsync(JT808Constants.SessionOffline, null, Guid.NewGuid().ToString("N")); | |||||
i--; | |||||
Thread.Sleep(1000); | |||||
} | |||||
}); | |||||
Thread.Sleep(1000); | |||||
List<string> SessionOnlines = new List<string>(); | |||||
ChannelMessageQueue channelMessageQueue= jT808SessionPublishingRedisImpl.Subscriber.Subscribe(JT808Constants.SessionOnline); | |||||
channelMessageQueue.OnMessage((msg) => { | |||||
SessionOnlines.Add(msg.Message); | |||||
}); | |||||
List<string> SessionOfflines = new List<string>(); | |||||
ChannelMessageQueue channelMessageQueue1 = jT808SessionPublishingRedisImpl.Subscriber.Subscribe(JT808Constants.SessionOffline); | |||||
channelMessageQueue1.OnMessage((msg) => { | |||||
SessionOfflines.Add(msg.Message); | |||||
}); | |||||
Thread.Sleep(3000); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
int i = 100000; | |||||
Task.Run(() => { | |||||
while (i > 0) | |||||
{ | |||||
jT808SessionPublishingRedisImpl.PublishAsync(JT808Constants.SessionOnline, null, Guid.NewGuid().ToString("N")); | |||||
jT808SessionPublishingRedisImpl.PublishAsync(JT808Constants.SessionOffline, null, Guid.NewGuid().ToString("N")); | |||||
i--; | |||||
Thread.Sleep(1000); | |||||
} | |||||
}); | |||||
Thread.Sleep(1000); | |||||
List<string> SessionOnlines = new List<string>(); | |||||
List<string> SessionOfflines = new List<string>(); | |||||
ChannelMessageQueue channelMessageQueue = jT808SessionPublishingRedisImpl.Subscriber.Subscribe(JT808Constants.SessionOnline); | |||||
channelMessageQueue.OnMessage((msg) => { | |||||
SessionOnlines.Add(msg.Message); | |||||
}); | |||||
ChannelMessageQueue channelMessageQueue1 = jT808SessionPublishingRedisImpl.Subscriber.Subscribe(JT808Constants.SessionOffline); | |||||
channelMessageQueue1.OnMessage((msg) => { | |||||
SessionOfflines.Add(msg.Message); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,62 +0,0 @@ | |||||
using JT808.DotNetty.Internal; | |||||
using System; | |||||
using System.Linq; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System.Net; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Extensions; | |||||
using System.Threading; | |||||
using Xunit; | |||||
using JT808.DotNetty.Interfaces; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
public class JT808SessionServiceDefaultImplTest : TestBase | |||||
{ | |||||
static IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
IJT808SessionService jT808SessionServiceDefaultImpl = ServiceProvider.GetService<IJT808SessionService>(); | |||||
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(1000); | |||||
var result = jT808SessionServiceDefaultImpl.GetAll(); | |||||
var result1 = jT808SessionServiceDefaultImpl.GetAll(); | |||||
Thread.Sleep(10000); | |||||
SimpleTcpClient1.Down(); | |||||
SimpleTcpClient2.Down(); | |||||
SimpleTcpClient3.Down(); | |||||
SimpleTcpClient4.Down(); | |||||
SimpleTcpClient5.Down(); | |||||
} | |||||
} | |||||
} |
@@ -1,146 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Internal; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Extensions; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
public class JT808SourcePackageChannelServiceTest:TestBase | |||||
{ | |||||
private JT808SourcePackageChannelService jT808SourcePackageChannelService; | |||||
private IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); | |||||
public JT808SimpleTcpClient SimpleTcpClient; | |||||
/// <summary> | |||||
/// 需要使用 SocketTool 创建tcp服务器 | |||||
/// </summary> | |||||
public JT808SourcePackageChannelServiceTest() | |||||
{ | |||||
SimpleTcpClient = new JT808SimpleTcpClient(endPoint, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6555)); | |||||
//作为设备5秒上传 | |||||
Task.Run(() => | |||||
{ | |||||
Random random = new Random(); | |||||
while (true) | |||||
{ | |||||
JT808Package jT808Package = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("12345678900"+ random.Next(0,2).ToString()); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package)); | |||||
Thread.Sleep(1000); | |||||
} | |||||
}); | |||||
// 作为源包转发服务端 | |||||
DispatcherEventLoopGroup bossGroup = new DispatcherEventLoopGroup(); | |||||
WorkerEventLoopGroup workerGroup = new WorkerEventLoopGroup(bossGroup, 1); | |||||
ServerBootstrap bootstrap = new ServerBootstrap(); | |||||
bootstrap.Group(bossGroup, workerGroup); | |||||
bootstrap.Channel<TcpServerChannel>(); | |||||
bootstrap | |||||
.ChildHandler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", new JT808ClientDecoder()); | |||||
})); | |||||
bootstrap.BindAsync(6655); | |||||
DispatcherEventLoopGroup bossGroup1 = new DispatcherEventLoopGroup(); | |||||
WorkerEventLoopGroup workerGroup1 = new WorkerEventLoopGroup(bossGroup1, 1); | |||||
ServerBootstrap bootstrap1 = new ServerBootstrap(); | |||||
bootstrap1.Group(bossGroup1, workerGroup1); | |||||
bootstrap1.Channel<TcpServerChannel>(); | |||||
bootstrap1 | |||||
.ChildHandler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", new JT808ClientDecoder()); | |||||
})); | |||||
bootstrap1.BindAsync(6656); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
//预热 | |||||
Thread.Sleep(3000); | |||||
jT808SourcePackageChannelService = ServiceProvider.GetService<JT808SourcePackageChannelService>(); | |||||
var result = jT808SourcePackageChannelService.GetAll(); | |||||
//创建服务 | |||||
DispatcherEventLoopGroup bossGroup2 = new DispatcherEventLoopGroup(); | |||||
WorkerEventLoopGroup workerGroup2 = new WorkerEventLoopGroup(bossGroup2, 1); | |||||
ServerBootstrap bootstrap2 = new ServerBootstrap(); | |||||
bootstrap2.Group(bossGroup2, workerGroup2); | |||||
bootstrap2.Channel<TcpServerChannel>(); | |||||
bootstrap2 | |||||
.ChildHandler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", new JT808ClientDecoder()); | |||||
})); | |||||
bootstrap2.BindAsync(6522); | |||||
//添加服务 | |||||
var addResult = jT808SourcePackageChannelService.Add(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 6522 | |||||
}).Result; | |||||
Thread.Sleep(1000); | |||||
var result1 = jT808SourcePackageChannelService.GetAll(); | |||||
//删除 | |||||
var result2 = jT808SourcePackageChannelService.Remove(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 6522 | |||||
}).Result; | |||||
//[::ffff:127.0.0.1]:13196 | |||||
var result3 = jT808SourcePackageChannelService.GetAll(); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
//[::ffff:127.0.0.1]:13196 | |||||
var str = "[::ffff:127.0.0.1]:13196"; | |||||
IPAddress.TryParse(str, out IPAddress ip); | |||||
var a = ip.MapToIPv4().ToString(); | |||||
var a1 = str.Replace("[::ffff:", "").Replace("]", ""); | |||||
} | |||||
} | |||||
} |
@@ -1,72 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
using Xunit; | |||||
using JT808.Protocol.Extensions; | |||||
using System.Threading; | |||||
using DotNetty.Transport.Channels; | |||||
using System.Runtime.InteropServices; | |||||
using DotNetty.Transport.Libuv; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using JT808.DotNetty.Codecs; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
public class JT808SourcePackageDispatcherDefaultImplTest: TestBase | |||||
{ | |||||
private IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); | |||||
public JT808SimpleTcpClient SimpleTcpClient; | |||||
public JT808SourcePackageDispatcherDefaultImplTest() | |||||
{ | |||||
SimpleTcpClient = new JT808SimpleTcpClient(endPoint, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6561)); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
// 作为源包转发服务端 | |||||
DispatcherEventLoopGroup bossGroup = new DispatcherEventLoopGroup(); | |||||
WorkerEventLoopGroup workerGroup = new WorkerEventLoopGroup(bossGroup, 1); | |||||
ServerBootstrap bootstrap = new ServerBootstrap(); | |||||
bootstrap.Group(bossGroup, workerGroup); | |||||
bootstrap.Channel<TcpServerChannel>(); | |||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) | |||||
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | |||||
{ | |||||
bootstrap | |||||
.Option(ChannelOption.SoReuseport, true) | |||||
.ChildOption(ChannelOption.SoReuseaddr, true); | |||||
} | |||||
bootstrap | |||||
.ChildHandler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", new JT808ClientDecoder()); | |||||
})); | |||||
bootstrap.BindAsync(6655); | |||||
//作为设备上传 | |||||
byte[] bytes = "7E 02 00 00 26 12 34 56 78 90 12 00 7D 02 00 00 00 01 00 00 00 02 00 BA 7F 0E 07 E4 F1 1C 00 28 00 3C 00 00 18 10 15 10 10 10 01 04 00 00 00 64 02 02 00 7D 01 13 7E".ToHexBytes(); | |||||
SimpleTcpClient.WriteAsync(bytes); | |||||
Thread.Sleep(10000); | |||||
SimpleTcpClient.Down(); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
//原包转发 不下发 - 貔貅 | |||||
byte[] bytes = "7E 02 00 00 26 12 34 56 78 90 12 00 7D 02 00 00 00 01 00 00 00 02 00 BA 7F 0E 07 E4 F1 1C 00 28 00 3C 00 00 18 10 15 10 10 10 01 04 00 00 00 64 02 02 00 7D 01 13 7E".ToHexBytes(); | |||||
SimpleTcpClient.WriteAsync(bytes); | |||||
Thread.Sleep(3000); | |||||
SimpleTcpClient.Down(); | |||||
} | |||||
} | |||||
} |
@@ -1,104 +0,0 @@ | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.DotNetty.Internal; | |||||
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 Xunit; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
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<ILoggerFactory, LoggerFactory>(); | |||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); | |||||
services.Configure<JT808Configuration>(hostContext.Configuration.GetSection("JT808Configuration")); | |||||
services.AddSingleton<JT808TransmitAddressFilterService>(); | |||||
}); | |||||
var serviceProvider = serverHostBuilder.Build().Services; | |||||
jT808TransmitAddressFilterService = serviceProvider.GetService<JT808TransmitAddressFilterService>(); | |||||
jT808TransmitAddressFilterService.Add(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 12345 | |||||
}); | |||||
jT808TransmitAddressFilterService.Add(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 12346 | |||||
}); | |||||
jT808TransmitAddressFilterService.Add(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 12347 | |||||
}); | |||||
jT808TransmitAddressFilterService.Add(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 12348 | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
Assert.True(jT808TransmitAddressFilterService.ContainsKey(new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 12348 | |||||
}.EndPoint)); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var result = jT808TransmitAddressFilterService.GetAll(); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var ip1 = new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 12349 | |||||
}; | |||||
var result1= jT808TransmitAddressFilterService.Add(ip1); | |||||
Assert.Equal(JT808ResultCode.Ok, result1.Code); | |||||
Assert.True(result1.Data); | |||||
var result2 = jT808TransmitAddressFilterService.Remove(ip1); | |||||
Assert.Equal(JT808ResultCode.Ok, result2.Code); | |||||
Assert.True(result2.Data); | |||||
} | |||||
[Fact] | |||||
public void Test4() | |||||
{ | |||||
var configIp = new Dtos.JT808IPAddressDto | |||||
{ | |||||
Host = "127.0.0.1", | |||||
Port = 6561 | |||||
}; | |||||
var result2 = jT808TransmitAddressFilterService.Remove(configIp); | |||||
Assert.Equal(JT808ResultCode.Ok, result2.Code); | |||||
Assert.False(result2.Data); | |||||
Assert.Equal("不能删除服务器配置的地址", result2.Message); | |||||
} | |||||
} | |||||
} |
@@ -1,87 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Extensions; | |||||
using JT808.Protocol.MessageBody; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Net.Http; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test.Internal | |||||
{ | |||||
public class JT808UnificationSendServiceDefaultImplTest : TestBase | |||||
{ | |||||
private IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); | |||||
public JT808SimpleTcpClient SimpleTcpClient; | |||||
public JT808UnificationSendServiceDefaultImplTest() | |||||
{ | |||||
SimpleTcpClient = new JT808SimpleTcpClient(endPoint); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
// 文本信息包 | |||||
JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.文本信息下发.Create("123456789012", new JT808_0x8300 | |||||
{ | |||||
TextFlag = 5, | |||||
TextInfo = "smallchi 518" | |||||
}); | |||||
var data = JT808Serializer.Serialize(jT808Package2); | |||||
JT808UnificationSendRequestDto jT808UnificationSendRequestDto = new JT808UnificationSendRequestDto(); | |||||
jT808UnificationSendRequestDto.TerminalPhoneNo = "123456789012"; | |||||
jT808UnificationSendRequestDto.Data = data; | |||||
HttpClient httpClient = new HttpClient(); | |||||
// 调用内置的http服务接收文本信息下发 | |||||
var result = httpClient.PostAsync("http://127.0.0.1:828/jt808api/UnificationSend", new StringContent(JsonConvert.SerializeObject(jT808UnificationSendRequestDto))).Result; | |||||
string content = result.Content.ReadAsStringAsync().Result; | |||||
JT808ResultDto<bool> jt808Result = JsonConvert.DeserializeObject<JT808ResultDto<bool>>(content); | |||||
Assert.Equal(200, jt808Result.Code); | |||||
Assert.False(jt808Result.Data); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
// 心跳会话包 | |||||
JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("123456789012"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package1)); | |||||
// 文本信息包 | |||||
JT808Package jT808Package2 = JT808.Protocol.Enums.JT808MsgId.文本信息下发.Create("123456789012", new JT808_0x8300 | |||||
{ | |||||
TextFlag=5, | |||||
TextInfo= "smallchi 518" | |||||
}); | |||||
var data = JT808Serializer.Serialize(jT808Package2); | |||||
JT808UnificationSendRequestDto jT808UnificationSendRequestDto = new JT808UnificationSendRequestDto(); | |||||
jT808UnificationSendRequestDto.TerminalPhoneNo = "123456789012"; | |||||
jT808UnificationSendRequestDto.Data = data; | |||||
HttpClient httpClient = new HttpClient(); | |||||
// 调用内置的http服务接收文本信息下发 | |||||
var result =httpClient.PostAsync("http://127.0.0.1:828/jt808api/UnificationSend", new StringContent(JsonConvert.SerializeObject(jT808UnificationSendRequestDto))).Result; | |||||
string content = result.Content.ReadAsStringAsync().Result; | |||||
JT808ResultDto<bool> jt808Result = JsonConvert.DeserializeObject<JT808ResultDto<bool>>(content); | |||||
Assert.Equal(200, jt808Result.Code); | |||||
Assert.True(jt808Result.Data); | |||||
SimpleTcpClient.Down(); | |||||
} | |||||
} | |||||
} |
@@ -1,32 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.2</TargetFramework> | |||||
<IsPackable>false</IsPackable> | |||||
<LangVersion>7.1</LangVersion> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> | |||||
<PrivateAssets>all</PrivateAssets> | |||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | |||||
</PackageReference> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.DotNetty\JT808.DotNetty.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Update="appsettings.json"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,39 +0,0 @@ | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.DotNetty.Metadata; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Test | |||||
{ | |||||
public class JT808DefaultChannelId : IChannelId | |||||
{ | |||||
private static readonly JT808AtomicCounter jT808AtomicCounter = new JT808AtomicCounter(); | |||||
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; | |||||
} | |||||
} | |||||
} |
@@ -1,85 +0,0 @@ | |||||
using DotNetty.Transport.Channels.Embedded; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test | |||||
{ | |||||
public class JT808SessionManagerTest: SeedSession | |||||
{ | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
var no = "test150"; | |||||
var channel = new EmbeddedChannel(new JT808DefaultChannelId()); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no)); | |||||
Thread.Sleep(1000); | |||||
jT808SessionManager.Heartbeat(no); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var no = "test151"; | |||||
var channel = new EmbeddedChannel(new JT808DefaultChannelId()); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no)); | |||||
var sessionInfo = jT808SessionManager.RemoveSession(no); | |||||
Assert.Equal(no, sessionInfo.TerminalPhoneNo); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var realSessionInfos = jT808SessionManager.GetAll(); | |||||
} | |||||
[Fact] | |||||
public void Test4() | |||||
{ | |||||
var realSessionCount = jT808SessionManager.SessionCount; | |||||
} | |||||
[Fact] | |||||
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()); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no1)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no2)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no3)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, no4)); | |||||
var removeSession = jT808SessionManager.RemoveSession(no); | |||||
Assert.Equal(no, removeSession.TerminalPhoneNo); | |||||
Assert.Equal(channel, removeSession.Channel); | |||||
Assert.Equal(channel.Id, removeSession.Channel.Id); | |||||
} | |||||
[Fact] | |||||
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()); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel1, no)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel1, no1)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel1, no2)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel2, no3)); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel2, no4)); | |||||
jT808SessionManager.RemoveSessionByChannel(channel1); | |||||
} | |||||
} | |||||
} |
@@ -1,90 +0,0 @@ | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.Protocol; | |||||
using JT808.Protocol.Extensions; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Net.Http; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test | |||||
{ | |||||
public class JT808WebAPIServiceTest: TestBase | |||||
{ | |||||
private IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6565); | |||||
public JT808SimpleTcpClient SimpleTcpClient; | |||||
private HttpClient httpClient; | |||||
private const string Url = "http://127.0.0.1:828/jt808api"; | |||||
private const string sessionRoutePrefix = "Session"; | |||||
private const int length = 10; | |||||
public JT808WebAPIServiceTest() | |||||
{ | |||||
SimpleTcpClient = new JT808SimpleTcpClient(endPoint); | |||||
httpClient = new HttpClient(); | |||||
for (var i = 1; i <= length; i++) | |||||
{ | |||||
// 心跳会话包 | |||||
JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create($"{i.ToString()}"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package1)); | |||||
} | |||||
Thread.Sleep(300); | |||||
} | |||||
[Fact] | |||||
public void GetAllTest() | |||||
{ | |||||
var result = httpClient.GetAsync($"{Url}/{sessionRoutePrefix}/GetAll").Result; | |||||
string content = result.Content.ReadAsStringAsync().Result; | |||||
JT808ResultDto<IEnumerable<JT808SessionInfoDto>> jt808Result = JsonConvert.DeserializeObject<JT808ResultDto<IEnumerable<JT808SessionInfoDto>>>(content); | |||||
Assert.Equal(200, jt808Result.Code); | |||||
Assert.Equal(10,jt808Result.Data.Count()); | |||||
} | |||||
[Fact] | |||||
public void RemoveByChannelIdTest() | |||||
{ | |||||
// 心跳会话包 | |||||
JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("99"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package1)); | |||||
var result4 = httpClient.PostAsync($"{Url}/{sessionRoutePrefix}/RemoveByChannelId", new StringContent("99")).Result; | |||||
string content4 = result4.Content.ReadAsStringAsync().Result; | |||||
JT808ResultDto<bool> jt808Result4= JsonConvert.DeserializeObject<JT808ResultDto<bool>>(content4); | |||||
Assert.Equal(200, jt808Result4.Code); | |||||
Assert.True(jt808Result4.Data); | |||||
} | |||||
[Fact] | |||||
public void RemoveByTerminalPhoneNoTest() | |||||
{ | |||||
// 心跳会话包 | |||||
JT808Package jT808Package1 = JT808.Protocol.Enums.JT808MsgId.终端心跳.Create("999"); | |||||
SimpleTcpClient.WriteAsync(JT808Serializer.Serialize(jT808Package1)); | |||||
var result4 = httpClient.PostAsync($"{Url}/{sessionRoutePrefix}/RemoveByTerminalPhoneNo", new StringContent("999")).Result; | |||||
string content4 = result4.Content.ReadAsStringAsync().Result; | |||||
JT808ResultDto<bool> jt808Result4 = JsonConvert.DeserializeObject<JT808ResultDto<bool>>(content4); | |||||
Assert.Equal(200, jt808Result4.Code); | |||||
Assert.True(jt808Result4.Data); | |||||
} | |||||
public override void Dispose() | |||||
{ | |||||
base.Dispose(); | |||||
SimpleTcpClient.Down(); | |||||
} | |||||
} | |||||
} |
@@ -1,36 +0,0 @@ | |||||
using DotNetty.Transport.Channels.Embedded; | |||||
using JT808.DotNetty.Internal; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test | |||||
{ | |||||
public class SeedSession | |||||
{ | |||||
public JT808SessionManager jT808SessionManager = new JT808SessionManager( | |||||
new JT808SessionPublishingEmptyImpl(), | |||||
new LoggerFactory()); | |||||
public SeedSession() | |||||
{ | |||||
for (var i = 0; i < 10; i++) | |||||
{ | |||||
var channel = new EmbeddedChannel(new JT808DefaultChannelId()); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, i.ToString())); | |||||
} | |||||
} | |||||
[Fact] | |||||
public void Init() | |||||
{ | |||||
for (var i = 0; i < 10; i++) | |||||
{ | |||||
var channel = new EmbeddedChannel(new JT808DefaultChannelId()); | |||||
jT808SessionManager.TryAdd(new Metadata.JT808Session(channel, i.ToString())); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,46 +0,0 @@ | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using JT808.DotNetty.Interfaces; | |||||
using JT808.DotNetty.Internal; | |||||
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.Net; | |||||
using Xunit; | |||||
namespace JT808.DotNetty.Test | |||||
{ | |||||
public class TestBase:IDisposable | |||||
{ | |||||
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<ILoggerFactory, LoggerFactory>(); | |||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); | |||||
}) | |||||
.UseJT808Host(); | |||||
var build = serverHostBuilder.Build(); | |||||
build.Start(); | |||||
ServiceProvider = build.Services; | |||||
} | |||||
public virtual void Dispose() | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -1,39 +0,0 @@ | |||||
{ | |||||
"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" | |||||
} | |||||
} |
@@ -1,59 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using JT808.Protocol; | |||||
using JT808.DotNetty.Metadata; | |||||
namespace JT808.DotNetty.Codecs | |||||
{ | |||||
/// <summary> | |||||
/// JT808客户端解码(测试客户端) | |||||
/// </summary> | |||||
internal class JT808ClientDecoder : ByteToMessageDecoder | |||||
{ | |||||
private static readonly ILogger<JT808ClientDecoder> logger=new LoggerFactory().CreateLogger<JT808ClientDecoder>(); | |||||
private static readonly JT808AtomicCounter MsgSuccessCounter = new JT808AtomicCounter(); | |||||
private static readonly JT808AtomicCounter MsgFailCounter = new JT808AtomicCounter(); | |||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) | |||||
{ | |||||
byte[] buffer = new byte[input.Capacity + 2]; | |||||
try | |||||
{ | |||||
input.ReadBytes(buffer, 1, input.Capacity); | |||||
buffer[0] = JT808Package.BeginFlag; | |||||
buffer[input.Capacity + 1] = JT808Package.EndFlag; | |||||
JT808Package jT808Package = JT808Serializer.Deserialize<JT808Package>(buffer); | |||||
MsgSuccessCounter.Increment(); | |||||
output.Add(jT808Package); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug("accept package success count<<<" + MsgSuccessCounter.Count.ToString()); | |||||
} | |||||
} | |||||
catch (JT808.Protocol.Exceptions.JT808Exception ex) | |||||
{ | |||||
MsgFailCounter.Increment(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError("accept package fail count<<<" + MsgFailCounter.Count.ToString()); | |||||
logger.LogError(ex, "accept msg<<<" + buffer); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
MsgFailCounter.Increment(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError("accept package fail count<<<" + MsgFailCounter.Count.ToString()); | |||||
logger.LogError(ex, "accept msg<<<" + buffer); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,28 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.Protocol; | |||||
using JT808.DotNetty.Internal; | |||||
using JT808.DotNetty.Interfaces; | |||||
namespace JT808.DotNetty.Codecs | |||||
{ | |||||
/// <summary> | |||||
/// JT808解码 | |||||
/// </summary> | |||||
internal class JT808Decoder : ByteToMessageDecoder | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) | |||||
{ | |||||
byte[] buffer = new byte[input.Capacity + 2]; | |||||
input.ReadBytes(buffer, 1, input.Capacity); | |||||
buffer[0] = JT808Package.BeginFlag; | |||||
buffer[input.Capacity + 1] = JT808Package.EndFlag; | |||||
output.Add(buffer); | |||||
} | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.Protocol; | |||||
using JT808.DotNetty.Internal; | |||||
using JT808.DotNetty.Interfaces; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT808.DotNetty.Metadata; | |||||
namespace JT808.DotNetty.Codecs | |||||
{ | |||||
/// <summary> | |||||
/// JT808 UDP解码 | |||||
/// </summary> | |||||
internal class JT808UDPDecoder : MessageToMessageDecoder<DatagramPacket> | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List<object> output) | |||||
{ | |||||
IByteBuffer byteBuffer = message.Content; | |||||
byte[] buffer = new byte[byteBuffer.ReadableBytes]; | |||||
byteBuffer.ReadBytes(buffer); | |||||
output.Add(new JT808UDPPackage(buffer, message.Sender)); | |||||
} | |||||
} | |||||
} |
@@ -1,35 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.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); | |||||
} | |||||
else | |||||
{ | |||||
endPoint = new DnsEndPoint(Host, Port); | |||||
} | |||||
} | |||||
return endPoint; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,54 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Configurations | |||||
{ | |||||
public class JT808Configuration | |||||
{ | |||||
public int Port { get; set; } = 808; | |||||
public int UDPPort { get; set; } = 809; | |||||
public int QuietPeriodSeconds { get; set; } = 1; | |||||
public TimeSpan QuietPeriodTimeSpan => TimeSpan.FromSeconds(QuietPeriodSeconds); | |||||
public int ShutdownTimeoutSeconds { get; set; } = 3; | |||||
public TimeSpan ShutdownTimeoutTimeSpan => TimeSpan.FromSeconds(ShutdownTimeoutSeconds); | |||||
public int SoBacklog { get; set; } = 8192; | |||||
public int EventLoopCount { get; set; } = Environment.ProcessorCount; | |||||
public int ReaderIdleTimeSeconds { get; set; } = 3600; | |||||
public int WriterIdleTimeSeconds { get; set; } = 3600; | |||||
public int AllIdleTimeSeconds { get; set; } = 3600; | |||||
/// <summary> | |||||
/// WebApi服务 | |||||
/// 默认828端口 | |||||
/// </summary> | |||||
public int WebApiPort { get; set; } = 828; | |||||
/// <summary> | |||||
/// 源包分发器配置 | |||||
/// </summary> | |||||
public List<JT808ClientConfiguration> SourcePackageDispatcherClientConfigurations { get; set; } | |||||
/// <summary> | |||||
/// 转发远程地址 (可选项)知道转发的地址有利于提升性能 | |||||
/// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: | |||||
// 1.消息的序列化 | |||||
// 2.消息的下发 | |||||
// 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle | |||||
// 就跟神兽貔貅一样。。。 | |||||
/// </summary> | |||||
public List<JT808ClientConfiguration> ForwardingRemoteAddress { get; set; } | |||||
public string RedisHost { get; set; } | |||||
} | |||||
} |
@@ -1,16 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
/// <summary> | |||||
/// 包计数器服务 | |||||
/// </summary> | |||||
public class JT808AtomicCounterDto | |||||
{ | |||||
public long MsgSuccessCount { get; set; } | |||||
public long MsgFailCount { get; set; } | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
public class JT808DefaultResultDto: JT808ResultDto<string> | |||||
{ | |||||
public JT808DefaultResultDto() | |||||
{ | |||||
Data = "Hello,JT808 WebAPI"; | |||||
Code = JT808ResultCode.Ok; | |||||
} | |||||
} | |||||
} |
@@ -1,35 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
public class JT808IPAddressDto | |||||
{ | |||||
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); | |||||
} | |||||
else | |||||
{ | |||||
endPoint = new DnsEndPoint(Host, Port); | |||||
} | |||||
} | |||||
return endPoint; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
public class JT808ResultDto<T> | |||||
{ | |||||
public string Message { get; set; } | |||||
public int Code { get; set; } | |||||
public T Data { get; set; } | |||||
} | |||||
internal class JT808ResultCode | |||||
{ | |||||
public const int Ok = 200; | |||||
public const int Empty = 201; | |||||
public const int NotFound = 404; | |||||
public const int Fail = 400; | |||||
public const int Error = 500; | |||||
} | |||||
} |
@@ -1,30 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
public class JT808SessionInfoDto | |||||
{ | |||||
/// <summary> | |||||
/// 通道Id | |||||
/// </summary> | |||||
public string ChannelId { get; set; } | |||||
/// <summary> | |||||
/// 最后上线时间 | |||||
/// </summary> | |||||
public DateTime LastActiveTime { get; set; } | |||||
/// <summary> | |||||
/// 上线时间 | |||||
/// </summary> | |||||
public DateTime StartTime { get; set; } | |||||
/// <summary> | |||||
/// 终端手机号 | |||||
/// </summary> | |||||
public string TerminalPhoneNo { get; set; } | |||||
/// <summary> | |||||
/// 远程ip地址 | |||||
/// </summary> | |||||
public string RemoteAddressIP { get; set; } | |||||
} | |||||
} |
@@ -1,33 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
/// <summary> | |||||
/// 原包通道信息 | |||||
/// </summary> | |||||
public class JT808SourcePackageChannelInfoDto | |||||
{ | |||||
/// <summary> | |||||
/// 远程地址 | |||||
/// </summary> | |||||
public string RemoteAddress { get; set; } | |||||
/// <summary> | |||||
/// 本地地址 | |||||
/// </summary> | |||||
public string LocalAddress { get; set; } | |||||
/// <summary> | |||||
/// 是否注册 | |||||
/// </summary> | |||||
public bool Registered { get; set; } | |||||
/// <summary> | |||||
/// 是否活动 | |||||
/// </summary> | |||||
public bool Active { get; set; } | |||||
/// <summary> | |||||
/// 是否打开 | |||||
/// </summary> | |||||
public bool Open { get; set; } | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Dtos | |||||
{ | |||||
/// <summary> | |||||
/// 统一下发请求参数 | |||||
/// </summary> | |||||
public class JT808UnificationSendRequestDto | |||||
{ | |||||
public string TerminalPhoneNo { get; set; } | |||||
public byte[] Data { get; set; } | |||||
} | |||||
} |
@@ -1,107 +0,0 @@ | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.DotNetty.Metadata; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808服务通道处理程序 | |||||
/// </summary> | |||||
internal class JT808ConnectionHandler : ChannelHandlerAdapter | |||||
{ | |||||
private readonly ILogger<JT808ConnectionHandler> logger; | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
public JT808ConnectionHandler( | |||||
JT808SessionManager jT808SessionManager, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
logger = loggerFactory.CreateLogger<JT808ConnectionHandler>(); | |||||
} | |||||
/// <summary> | |||||
/// 通道激活 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
public override void ChannelActive(IChannelHandlerContext context) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
logger.LogDebug($"<<<{ channelId } Successful client connection to server."); | |||||
base.ChannelActive(context); | |||||
} | |||||
/// <summary> | |||||
/// 设备主动断开 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
public override void ChannelInactive(IChannelHandlerContext context) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
logger.LogDebug($">>>{ channelId } The client disconnects from the server."); | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
base.ChannelInactive(context); | |||||
} | |||||
/// <summary> | |||||
/// 服务器主动断开 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
/// <returns></returns> | |||||
public override Task CloseAsync(IChannelHandlerContext context) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
logger.LogDebug($"<<<{ channelId } The server disconnects from the client."); | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
return base.CloseAsync(context); | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush(); | |||||
/// <summary> | |||||
/// 超时策略 | |||||
/// </summary> | |||||
/// <param name="context"></param> | |||||
/// <param name="evt"></param> | |||||
public override void UserEventTriggered(IChannelHandlerContext context, object evt) | |||||
{ | |||||
IdleStateEvent idleStateEvent = evt as IdleStateEvent; | |||||
if (idleStateEvent != null) | |||||
{ | |||||
if(idleStateEvent.State== IdleState.ReaderIdle) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
logger.LogInformation($"{idleStateEvent.State.ToString()}>>>{channelId}"); | |||||
// 由于808是设备发心跳,如果很久没有上报数据,那么就由服务器主动关闭连接。 | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | |||||
} | |||||
// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: | |||||
// 1.消息的序列化 | |||||
// 2.消息的下发 | |||||
// 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle | |||||
// 就跟神兽貔貅一样。。。 | |||||
} | |||||
base.UserEventTriggered(context, evt); | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
logger.LogError(exception,$"{channelId} {exception.Message}" ); | |||||
jT808SessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,96 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.DotNetty.Metadata; | |||||
using JT808.DotNetty.Internal; | |||||
using JT808.DotNetty.Interfaces; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace JT808.DotNetty.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808服务端处理程序 | |||||
/// </summary> | |||||
internal class JT808ServerHandler : SimpleChannelInboundHandler<byte[]> | |||||
{ | |||||
private readonly JT808MsgIdHandlerBase handler; | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
private readonly JT808TransmitAddressFilterService jT808TransmitAddressFilterService; | |||||
private readonly IJT808SourcePackageDispatcher jT808SourcePackageDispatcher; | |||||
private readonly JT808AtomicCounterService jT808AtomicCounterService; | |||||
private readonly ILogger<JT808ServerHandler> logger; | |||||
public JT808ServerHandler( | |||||
ILoggerFactory loggerFactory, | |||||
JT808TransmitAddressFilterService jT808TransmitAddressFilterService, | |||||
IJT808SourcePackageDispatcher jT808SourcePackageDispatcher, | |||||
JT808MsgIdHandlerBase handler, | |||||
JT808AtomicCounterService jT808AtomicCounterService, | |||||
JT808SessionManager jT808SessionManager) | |||||
{ | |||||
this.jT808TransmitAddressFilterService = jT808TransmitAddressFilterService; | |||||
this.handler = handler; | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
this.jT808SourcePackageDispatcher = jT808SourcePackageDispatcher; | |||||
this.jT808AtomicCounterService = jT808AtomicCounterService; | |||||
logger = loggerFactory.CreateLogger<JT808ServerHandler>(); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg) | |||||
{ | |||||
try | |||||
{ | |||||
jT808SourcePackageDispatcher?.SendAsync(msg); | |||||
//解析到头部,然后根据具体的消息Id通过队列去进行消费 | |||||
//要是一定要解析到数据体可以在JT808MsgIdHandlerBase类中根据具体的消息, | |||||
//解析具体的消息体,具体调用JT808Serializer.Deserialize<T> | |||||
JT808HeaderPackage jT808HeaderPackage = JT808Serializer.Deserialize<JT808HeaderPackage>(msg); | |||||
jT808AtomicCounterService.MsgSuccessIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug("accept package success count<<<" + jT808AtomicCounterService.MsgSuccessCount.ToString()); | |||||
} | |||||
jT808SessionManager.TryAdd(new JT808Session(ctx.Channel, jT808HeaderPackage.Header.TerminalPhoneNo)); | |||||
Func<JT808Request, JT808Response> handlerFunc; | |||||
if (handler.HandlerDict.TryGetValue(jT808HeaderPackage.Header.MsgId, out handlerFunc)) | |||||
{ | |||||
JT808Response jT808Response = handlerFunc(new JT808Request(jT808HeaderPackage, msg)); | |||||
if (jT808Response != null) | |||||
{ | |||||
if (!jT808TransmitAddressFilterService.ContainsKey(ctx.Channel.RemoteAddress)) | |||||
{ | |||||
ctx.WriteAndFlushAsync(Unpooled.WrappedBuffer(JT808Serializer.Serialize(jT808Response.Package, jT808Response.MinBufferSize))); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
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)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,70 +0,0 @@ | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.DotNetty.Internal; | |||||
using Microsoft.Extensions.Logging; | |||||
using Polly; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Handlers | |||||
{ | |||||
internal class JT808SourcePackageDispatcherHandler: ChannelHandlerAdapter | |||||
{ | |||||
private readonly ILogger<JT808SourcePackageDispatcherHandler> logger; | |||||
private readonly JT808SourcePackageChannelService jT808SourcePackageChannelService; | |||||
public JT808SourcePackageDispatcherHandler(JT808SourcePackageChannelService jT808SourcePackageChannelService) | |||||
{ | |||||
logger= jT808SourcePackageChannelService.LoggerFactory.CreateLogger<JT808SourcePackageDispatcherHandler>(); | |||||
this.jT808SourcePackageChannelService = jT808SourcePackageChannelService; | |||||
} | |||||
public override void ChannelInactive(IChannelHandlerContext context) | |||||
{ | |||||
Policy.HandleResult<bool>(context.Channel.Open) | |||||
.WaitAndRetryForeverAsync(retryAttempt => | |||||
{ | |||||
return retryAttempt > 20 ? TimeSpan.FromSeconds(Math.Pow(2, 50)) : TimeSpan.FromSeconds(Math.Pow(2, retryAttempt));//超过重试20次,接近12个小时链接一次 | |||||
},(exception, timespan, ctx) => | |||||
{ | |||||
logger.LogError($"Server Disconnection {context.Channel.RemoteAddress.ToString()},Retry Results{exception.Result},Retry Number{timespan},Next Retry Interval(s){ctx.TotalSeconds}"); | |||||
}) | |||||
.ExecuteAsync(async () => | |||||
{ | |||||
try | |||||
{ | |||||
var newChannel = jT808SourcePackageChannelService.channels.FirstOrDefault(m => m.Value == context.Channel); | |||||
if (default(KeyValuePair<EndPoint, IChannel>).Equals(newChannel)) | |||||
{ | |||||
if(logger.IsEnabled(LogLevel.Debug)) | |||||
logger.LogDebug($"Server already deleted {context.Channel.RemoteAddress.ToString()} remote server configuration"); | |||||
return true; | |||||
} | |||||
var channel = await jT808SourcePackageChannelService.bootstrap.ConnectAsync(context.Channel.RemoteAddress); | |||||
jT808SourcePackageChannelService.channels.AddOrUpdate(newChannel.Key, channel, (x, y) => channel); | |||||
return channel.Open; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex,$"Reconnection abnormal:After the server is disconnected {context.Channel.RemoteAddress.ToString()}"); | |||||
return false; | |||||
} | |||||
}); | |||||
} | |||||
public override void ChannelRead(IChannelHandlerContext context, object message) | |||||
{ | |||||
if(logger.IsEnabled(LogLevel.Debug)) | |||||
logger.LogDebug($"The server returns a message {message.ToString()}"); | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
logger.LogError(exception, context.Channel.RemoteAddress.ToString()); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
} |
@@ -1,88 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT808.DotNetty.Metadata; | |||||
using JT808.DotNetty.Internal; | |||||
using JT808.DotNetty.Interfaces; | |||||
using Microsoft.Extensions.Logging; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
namespace JT808.DotNetty.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT808 UDP服务端处理程序 | |||||
/// </summary> | |||||
internal class JT808UDPServerHandler : SimpleChannelInboundHandler<JT808UDPPackage> | |||||
{ | |||||
private readonly JT808MsgIdHandlerBase handler; | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
private readonly IJT808SourcePackageDispatcher jT808SourcePackageDispatcher; | |||||
private readonly JT808AtomicCounterService jT808AtomicCounterService; | |||||
private readonly ILogger<JT808UDPServerHandler> logger; | |||||
public JT808UDPServerHandler( | |||||
ILoggerFactory loggerFactory, | |||||
IJT808SourcePackageDispatcher jT808SourcePackageDispatcher, | |||||
JT808MsgIdHandlerBase handler, | |||||
JT808AtomicCounterService jT808AtomicCounterService, | |||||
JT808SessionManager jT808SessionManager) | |||||
{ | |||||
this.handler = handler; | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
this.jT808SourcePackageDispatcher = jT808SourcePackageDispatcher; | |||||
this.jT808AtomicCounterService = jT808AtomicCounterService; | |||||
logger = loggerFactory.CreateLogger<JT808UDPServerHandler>(); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, JT808UDPPackage msg) | |||||
{ | |||||
try | |||||
{ | |||||
jT808SourcePackageDispatcher?.SendAsync(msg.Buffer); | |||||
//解析到头部,然后根据具体的消息Id通过队列去进行消费 | |||||
//要是一定要解析到数据体可以在JT808MsgIdHandlerBase类中根据具体的消息, | |||||
//解析具体的消息体,具体调用JT808Serializer.Deserialize<T> | |||||
JT808HeaderPackage jT808HeaderPackage = JT808Serializer.Deserialize<JT808HeaderPackage>(msg.Buffer); | |||||
jT808AtomicCounterService.MsgSuccessIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug("accept package success count<<<" + jT808AtomicCounterService.MsgSuccessCount.ToString()); | |||||
} | |||||
Func<JT808Request, JT808Response> handlerFunc; | |||||
if (handler.HandlerDict.TryGetValue(jT808HeaderPackage.Header.MsgId, out handlerFunc)) | |||||
{ | |||||
JT808Response jT808Response = handlerFunc(new JT808Request(jT808HeaderPackage, msg.Buffer)); | |||||
if (jT808Response != null) | |||||
{ | |||||
ctx.WriteAndFlushAsync(new DatagramPacket(Unpooled.WrappedBuffer(JT808Serializer.Serialize(jT808Response.Package, jT808Response.MinBufferSize)), msg.Sender)); | |||||
} | |||||
} | |||||
} | |||||
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)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Common.Utilities; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.DotNetty.Internal; | |||||
using JT808.DotNetty.Metadata; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// jt808 webapi服务 | |||||
/// 请求量不大,只支持JSON格式并且只支持post发数据 | |||||
/// ref: dotnetty HttpServer | |||||
/// </summary> | |||||
internal class JT808WebAPIServerHandler : SimpleChannelInboundHandler<IFullHttpRequest> | |||||
{ | |||||
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 JT808WebAPIService jT808WebAPIService; | |||||
private readonly ILogger<JT808WebAPIServerHandler> logger; | |||||
public JT808WebAPIServerHandler( | |||||
JT808WebAPIService jT808WebAPIService, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.jT808WebAPIService = jT808WebAPIService; | |||||
logger = loggerFactory.CreateLogger<JT808WebAPIServerHandler>(); | |||||
} | |||||
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 (jT808WebAPIService.HandlerDict.TryGetValue(msg.Uri,out var funcHandler)) | |||||
{ | |||||
jT808HttpResponse = funcHandler( new JT808HttpRequest() { Json = msg.Content.ToString(Encoding.UTF8)}); | |||||
} | |||||
else | |||||
{ | |||||
jT808HttpResponse = jT808WebAPIService.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(jT808WebAPIService.ErrorHttpResponse(exception).Data), TypeJson, jT808WebAPIService.ErrorHttpResponse(exception).Data.Length); | |||||
logger.LogError(exception, exception.Message); | |||||
context.CloseAsync(); | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty.Interfaces | |||||
{ | |||||
public interface IJT808SessionPublishing | |||||
{ | |||||
Task PublishAsync(string topicName, string key, string value); | |||||
} | |||||
} |
@@ -1,25 +0,0 @@ | |||||
using JT808.DotNetty.Dtos; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Interfaces | |||||
{ | |||||
/// <summary> | |||||
/// JT808会话服务 | |||||
/// </summary> | |||||
internal interface IJT808SessionService | |||||
{ | |||||
/// <summary> | |||||
/// 获取会话集合 | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
JT808ResultDto<List<JT808SessionInfoDto>> GetAll(); | |||||
/// <summary> | |||||
/// 通过设备终端号移除对应会话 | |||||
/// </summary> | |||||
/// <param name="terminalPhoneNo"></param> | |||||
/// <returns></returns> | |||||
JT808ResultDto<bool> RemoveByTerminalPhoneNo(string terminalPhoneNo); | |||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty.Interfaces | |||||
{ | |||||
/// <summary> | |||||
/// 源包分发器 | |||||
/// 自定义源包分发器业务 | |||||
/// ConfigureServices: | |||||
/// services.Replace(new ServiceDescriptor(typeof(IJT808SourcePackageDispatcher),typeof(JT808SourcePackageDispatcherDefaultImpl),ServiceLifetime.Singleton)); | |||||
/// </summary> | |||||
public interface IJT808SourcePackageDispatcher | |||||
{ | |||||
Task SendAsync(byte[] data); | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using JT808.DotNetty.Dtos; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Interfaces | |||||
{ | |||||
/// <summary> | |||||
/// JT808统一下发命令 | |||||
/// </summary> | |||||
internal interface IJT808UnificationSendService | |||||
{ | |||||
JT808ResultDto<bool> Send(string terminalPhoneNo, byte[] data); | |||||
} | |||||
} |
@@ -1,48 +0,0 @@ | |||||
using JT808.DotNetty.Metadata; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
/// <summary> | |||||
/// 计数包服务 | |||||
/// </summary> | |||||
internal class JT808AtomicCounterService | |||||
{ | |||||
private static readonly JT808AtomicCounter MsgSuccessCounter = new JT808AtomicCounter(); | |||||
private static readonly JT808AtomicCounter MsgFailCounter = new JT808AtomicCounter(); | |||||
public JT808AtomicCounterService() | |||||
{ | |||||
} | |||||
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; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,16 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
/// <summary> | |||||
/// 默认消息处理业务实现 | |||||
/// </summary> | |||||
internal class JT808MsgIdDefaultHandler : JT808MsgIdHandlerBase | |||||
{ | |||||
public JT808MsgIdDefaultHandler(JT808SessionManager sessionManager) : base(sessionManager) | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -1,16 +0,0 @@ | |||||
using JT808.DotNetty.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
internal class JT808SessionPublishingEmptyImpl : IJT808SessionPublishing | |||||
{ | |||||
public Task PublishAsync(string topicName, string key, string value) | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,75 +0,0 @@ | |||||
using JT808.DotNetty.Interfaces; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using StackExchange.Redis; | |||||
using Microsoft.Extensions.Options; | |||||
using JT808.DotNetty.Configurations; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
internal class JT808SessionPublishingRedisImpl : IJT808SessionPublishing,IDisposable | |||||
{ | |||||
private IConnectionMultiplexer connectionMultiplexer; | |||||
private IOptionsMonitor<JT808Configuration> optionsMonitor; | |||||
private string redisHost; | |||||
private IDisposable optionsMonitorDisposable; | |||||
public JT808SessionPublishingRedisImpl( | |||||
IOptionsMonitor<JT808Configuration> optionsMonitor | |||||
) | |||||
{ | |||||
this.optionsMonitor = optionsMonitor; | |||||
redisHost = optionsMonitor.CurrentValue.RedisHost; | |||||
try | |||||
{ | |||||
connectionMultiplexer = ConnectionMultiplexer.Connect(redisHost); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
optionsMonitorDisposable= this.optionsMonitor.OnChange((config,str) => | |||||
{ | |||||
if(config.RedisHost!= redisHost) | |||||
{ | |||||
redisHost = config.RedisHost; | |||||
connectionMultiplexer.Close(); | |||||
try | |||||
{ | |||||
connectionMultiplexer = ConnectionMultiplexer.Connect(redisHost); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
public Task PublishAsync(string topicName, string key, string value) | |||||
{ | |||||
if (connectionMultiplexer.IsConnected) | |||||
{ | |||||
Subscriber?.PublishAsync(topicName, value); | |||||
} | |||||
return Task.CompletedTask; | |||||
} | |||||
internal ISubscriber Subscriber | |||||
{ | |||||
get | |||||
{ | |||||
return connectionMultiplexer.GetSubscriber(); | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
connectionMultiplexer.Close(); | |||||
optionsMonitorDisposable.Dispose(); | |||||
} | |||||
} | |||||
} |
@@ -1,77 +0,0 @@ | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.DotNetty.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Runtime.CompilerServices; | |||||
using Microsoft.Extensions.Options; | |||||
using JT808.DotNetty.Configurations; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
internal class JT808SessionServiceDefaultImpl : IJT808SessionService | |||||
{ | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
public JT808SessionServiceDefaultImpl( | |||||
JT808SessionManager jT808SessionManager) | |||||
{ | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
} | |||||
public JT808ResultDto<List<JT808SessionInfoDto>> GetAll() | |||||
{ | |||||
JT808ResultDto<List<JT808SessionInfoDto>> resultDto = new JT808ResultDto<List<JT808SessionInfoDto>>(); | |||||
try | |||||
{ | |||||
resultDto.Data = jT808SessionManager.GetAll().Select(s => new JT808SessionInfoDto | |||||
{ | |||||
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<bool> RemoveByTerminalPhoneNo(string terminalPhoneNo) | |||||
{ | |||||
JT808ResultDto<bool> resultDto = new JT808ResultDto<bool>(); | |||||
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; | |||||
} | |||||
} | |||||
} |
@@ -1,290 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.DotNetty.Handlers; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
/// <summary> | |||||
/// 原包分发器通道服务 | |||||
/// </summary> | |||||
internal class JT808SourcePackageChannelService:IDisposable | |||||
{ | |||||
private readonly IOptionsMonitor<JT808Configuration> jT808ConfigurationOptionsMonitor; | |||||
internal readonly ConcurrentDictionary<EndPoint, IChannel> channels; | |||||
private readonly ILogger<JT808SourcePackageChannelService> logger; | |||||
private readonly MultithreadEventLoopGroup group; | |||||
internal readonly ILoggerFactory LoggerFactory; | |||||
internal readonly Bootstrap bootstrap; | |||||
private IDisposable jT808ConfigurationOptionsMonitorDisposable; | |||||
public JT808SourcePackageChannelService( | |||||
ILoggerFactory loggerFactory, | |||||
IOptionsMonitor<JT808Configuration> jT808ConfigurationOptionsMonitor) | |||||
{ | |||||
this.LoggerFactory = loggerFactory; | |||||
this.logger = loggerFactory.CreateLogger<JT808SourcePackageChannelService>(); | |||||
this.channels = new ConcurrentDictionary<EndPoint, IChannel>(); | |||||
this.jT808ConfigurationOptionsMonitor = jT808ConfigurationOptionsMonitor; | |||||
this.group = new MultithreadEventLoopGroup(); | |||||
this.bootstrap = new Bootstrap(); | |||||
jT808ConfigurationOptionsMonitorDisposable = jT808ConfigurationOptionsMonitor.OnChange(options => | |||||
{ | |||||
List<JT808ClientConfiguration> chgRemoteServers = new List<JT808ClientConfiguration>(); | |||||
if (jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations != null && jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations.Count > 0) | |||||
{ | |||||
chgRemoteServers = options.SourcePackageDispatcherClientConfigurations; | |||||
} | |||||
DelRemoteServsers(chgRemoteServers); | |||||
AddRemoteServsers(chgRemoteServers); | |||||
}); | |||||
StartAsync(); | |||||
} | |||||
/// <summary> | |||||
/// 下发数据 | |||||
/// </summary> | |||||
/// <param name="data"></param> | |||||
/// <returns></returns> | |||||
public Task SendAsync(byte[] data) | |||||
{ | |||||
foreach (var item in channels) | |||||
{ | |||||
try | |||||
{ | |||||
if (item.Value.Open && item.Value.Active) | |||||
{ | |||||
item.Value.WriteAndFlushAsync(Unpooled.WrappedBuffer(data)); | |||||
} | |||||
else | |||||
{ | |||||
logger.LogInformation($"{item} link closed."); | |||||
} | |||||
} | |||||
catch (AggregateException ex) | |||||
{ | |||||
logger.LogError(ex, $"{item} Send Data Error."); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex, $"{item} Send Data Error."); | |||||
} | |||||
} | |||||
return Task.CompletedTask; | |||||
} | |||||
/// <summary> | |||||
/// 获取通道信息集合 | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
public JT808ResultDto<List<JT808SourcePackageChannelInfoDto>> GetAll() | |||||
{ | |||||
JT808ResultDto<List<JT808SourcePackageChannelInfoDto>> jT808ResultDto = new JT808ResultDto<List<JT808SourcePackageChannelInfoDto>>(); | |||||
jT808ResultDto.Data = new List<JT808SourcePackageChannelInfoDto>(); | |||||
jT808ResultDto.Code = JT808ResultCode.Ok; | |||||
foreach (var item in channels) | |||||
{ | |||||
JT808SourcePackageChannelInfoDto jT808SourcePackageChannelInfoDto = new JT808SourcePackageChannelInfoDto(); | |||||
jT808SourcePackageChannelInfoDto.Active = item.Value.Active; | |||||
jT808SourcePackageChannelInfoDto.Open = item.Value.Open; | |||||
jT808SourcePackageChannelInfoDto.Registered = item.Value.Registered; | |||||
jT808SourcePackageChannelInfoDto.LocalAddress = item.Value.LocalAddress.ToString().Replace("[::ffff:", "").Replace("]", ""); | |||||
jT808SourcePackageChannelInfoDto.RemoteAddress = item.Value.RemoteAddress.ToString().Replace("[::ffff:", "").Replace("]", ""); | |||||
jT808ResultDto.Data.Add(jT808SourcePackageChannelInfoDto); | |||||
} | |||||
return jT808ResultDto; | |||||
} | |||||
/// <summary> | |||||
/// 添加地址 | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
public async Task<JT808ResultDto<bool>> Add(JT808IPAddressDto jT808IPAddressDto) | |||||
{ | |||||
JT808ResultDto<bool> jT808ResultDto = new JT808ResultDto<bool>(); | |||||
jT808ResultDto.Code= JT808ResultCode.Ok; | |||||
jT808ResultDto.Data = true; | |||||
if (!channels.ContainsKey(jT808IPAddressDto.EndPoint)) | |||||
{ | |||||
try | |||||
{ | |||||
IChannel clientChannel = await bootstrap.ConnectAsync(jT808IPAddressDto.EndPoint); | |||||
channels.TryAdd(jT808IPAddressDto.EndPoint, clientChannel); | |||||
} | |||||
catch (AggregateException ex) | |||||
{ | |||||
jT808ResultDto.Data = false; | |||||
jT808ResultDto.Code = JT808ResultCode.Error; | |||||
jT808ResultDto.Message = JsonConvert.SerializeObject(ex); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
jT808ResultDto.Data = false; | |||||
jT808ResultDto.Code= JT808ResultCode.Error; | |||||
jT808ResultDto.Message = JsonConvert.SerializeObject(ex); | |||||
} | |||||
} | |||||
return jT808ResultDto; | |||||
} | |||||
/// <summary> | |||||
/// 删除地址 | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
public async Task<JT808ResultDto<bool>> Remove(JT808IPAddressDto jT808IPAddressDto) | |||||
{ | |||||
JT808ResultDto<bool> jT808ResultDto = new JT808ResultDto<bool>(); | |||||
jT808ResultDto.Code = JT808ResultCode.Ok; | |||||
jT808ResultDto.Data = true; | |||||
if(jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations!=null && | |||||
jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations.Any(a=>a.EndPoint.ToString()== jT808IPAddressDto.EndPoint.ToString()) | |||||
) | |||||
{ | |||||
jT808ResultDto.Data = false; | |||||
jT808ResultDto.Message = "不能删除服务器配置的地址"; | |||||
} | |||||
else | |||||
{ | |||||
if (channels.TryRemove(jT808IPAddressDto.EndPoint, out var channel)) | |||||
{ | |||||
try | |||||
{ | |||||
await channel.CloseAsync(); | |||||
} | |||||
catch (AggregateException ex) | |||||
{ | |||||
jT808ResultDto.Data = false; | |||||
jT808ResultDto.Code = JT808ResultCode.Error; | |||||
jT808ResultDto.Message = JsonConvert.SerializeObject(ex); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
jT808ResultDto.Data = false; | |||||
jT808ResultDto.Code = JT808ResultCode.Error; | |||||
jT808ResultDto.Message = JsonConvert.SerializeObject(ex); | |||||
} | |||||
} | |||||
} | |||||
return jT808ResultDto; | |||||
} | |||||
private void StartAsync() | |||||
{ | |||||
bootstrap | |||||
.Group(group) | |||||
.Channel<TcpSocketChannel>() | |||||
.Option(ChannelOption.TcpNodelay, true) | |||||
.Handler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
channel.Pipeline.AddLast(new JT808SourcePackageDispatcherHandler(this)); | |||||
})); | |||||
if (jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations != null && | |||||
jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations.Count > 0) | |||||
{ | |||||
foreach (var item in jT808ConfigurationOptionsMonitor.CurrentValue.SourcePackageDispatcherClientConfigurations) | |||||
{ | |||||
try | |||||
{ | |||||
Task.Run(async () => | |||||
{ | |||||
IChannel clientChannel = await bootstrap.ConnectAsync(item.EndPoint); | |||||
channels.TryAdd(item.EndPoint, clientChannel); | |||||
logger.LogInformation($"init remote link {item.EndPoint.ToString()}."); | |||||
}); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex, $"there is an exception in sending data {item}."); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
private Task StopAsync() | |||||
{ | |||||
foreach (var channel in channels) | |||||
{ | |||||
try | |||||
{ | |||||
channel.Value.CloseAsync(); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
} | |||||
group.ShutdownGracefullyAsync(jT808ConfigurationOptionsMonitor.CurrentValue.QuietPeriodTimeSpan, jT808ConfigurationOptionsMonitor.CurrentValue.ShutdownTimeoutTimeSpan); | |||||
return Task.CompletedTask; | |||||
} | |||||
/// <summary> | |||||
/// 动态删除远程服务器 | |||||
/// </summary> | |||||
/// <param name="chgRemoteServers"></param> | |||||
private void DelRemoteServsers(List<JT808ClientConfiguration> chgRemoteServers) | |||||
{ | |||||
var delChannels = channels.Keys.Except(chgRemoteServers.Select(s => s.EndPoint)).ToList(); | |||||
foreach (var item in delChannels) | |||||
{ | |||||
try | |||||
{ | |||||
channels.TryRemove(item, out var channel); | |||||
channel.CloseAsync(); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// 动态添加远程服务器 | |||||
/// </summary> | |||||
/// <param name="bootstrap"></param> | |||||
/// <param name="chgRemoteServers"></param> | |||||
private async void AddRemoteServsers(List<JT808ClientConfiguration> chgRemoteServers) | |||||
{ | |||||
var addChannels = chgRemoteServers.Select(s => s.EndPoint).Except(channels.Keys).ToList(); | |||||
foreach (var item in addChannels) | |||||
{ | |||||
try | |||||
{ | |||||
IChannel clientChannel = await bootstrap.ConnectAsync(item); | |||||
channels.TryAdd(item, clientChannel); | |||||
logger.LogInformation($"link to the remote server after the change {item}."); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex, $"reconnect the remote server after the exception changes {item}."); | |||||
} | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
jT808ConfigurationOptionsMonitorDisposable.Dispose(); | |||||
StopAsync(); | |||||
} | |||||
} | |||||
} |
@@ -1,39 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using JT808.DotNetty.Interfaces; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
/// <summary> | |||||
/// 原包分发器默认实现 | |||||
/// </summary> | |||||
internal class JT808SourcePackageDispatcherDefaultImpl : IJT808SourcePackageDispatcher | |||||
{ | |||||
private readonly JT808SourcePackageChannelService jT808SourcePackageChannelService; | |||||
public JT808SourcePackageDispatcherDefaultImpl(JT808SourcePackageChannelService jT808SourcePackageChannelService) | |||||
{ | |||||
this.jT808SourcePackageChannelService = jT808SourcePackageChannelService; | |||||
} | |||||
public async Task SendAsync(byte[] data) | |||||
{ | |||||
await jT808SourcePackageChannelService.SendAsync(data); | |||||
} | |||||
} | |||||
} |
@@ -1,88 +0,0 @@ | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Dtos; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
/// <summary> | |||||
/// JT808转发地址过滤服务 | |||||
/// 按照808的消息,有些请求必须要应答,但是转发可以不需要有应答可以节省部分资源包括: | |||||
// 1.消息的序列化 | |||||
// 2.消息的下发 | |||||
// 都有一定的性能损耗,那么不需要判断写超时 IdleState.WriterIdle | |||||
// 就跟神兽貔貅一样。。。 | |||||
/// </summary> | |||||
public class JT808TransmitAddressFilterService : IDisposable | |||||
{ | |||||
private readonly IOptionsMonitor<JT808Configuration> jT808ConfigurationOptionsMonitor; | |||||
private ConcurrentDictionary<string,int> ForwardingRemoteAddresssDict; | |||||
private IDisposable jT808ConfigurationOptionsMonitorDisposable; | |||||
public JT808TransmitAddressFilterService( | |||||
IOptionsMonitor<JT808Configuration> jT808ConfigurationOptionsMonitor) | |||||
{ | |||||
this.jT808ConfigurationOptionsMonitor = jT808ConfigurationOptionsMonitor; | |||||
ForwardingRemoteAddresssDict = new ConcurrentDictionary<string, int>(); | |||||
InitForwardingRemoteAddress(jT808ConfigurationOptionsMonitor.CurrentValue.ForwardingRemoteAddress); | |||||
//OnChange 源码多播委托 | |||||
jT808ConfigurationOptionsMonitorDisposable = this.jT808ConfigurationOptionsMonitor.OnChange(options => | |||||
{ | |||||
InitForwardingRemoteAddress(options.ForwardingRemoteAddress); | |||||
}); | |||||
} | |||||
private void InitForwardingRemoteAddress(List<JT808ClientConfiguration> jT808ClientConfigurations) | |||||
{ | |||||
if (jT808ClientConfigurations != null && jT808ClientConfigurations.Count > 0) | |||||
{ | |||||
foreach (var item in jT808ClientConfigurations) | |||||
{ | |||||
string host = item.EndPoint.ToString(); | |||||
ForwardingRemoteAddresssDict.TryAdd(host, 0); | |||||
} | |||||
} | |||||
} | |||||
public bool ContainsKey(EndPoint endPoint) | |||||
{ | |||||
return ForwardingRemoteAddresssDict.ContainsKey(endPoint.ToString()); | |||||
} | |||||
public JT808ResultDto<bool> Add(JT808IPAddressDto jT808IPAddressDto) | |||||
{ | |||||
string host = jT808IPAddressDto.EndPoint.ToString(); | |||||
return new JT808ResultDto<bool>() { Code = JT808ResultCode.Ok, Data = ForwardingRemoteAddresssDict.TryAdd(host,0) }; | |||||
} | |||||
public JT808ResultDto<bool> Remove(JT808IPAddressDto jT808IPAddressDto) | |||||
{ | |||||
string host = jT808IPAddressDto.EndPoint.ToString(); | |||||
if(jT808ConfigurationOptionsMonitor.CurrentValue.ForwardingRemoteAddress!=null && | |||||
jT808ConfigurationOptionsMonitor.CurrentValue.ForwardingRemoteAddress.Any(w=>w.EndPoint.ToString()== host)) | |||||
{ | |||||
return new JT808ResultDto<bool>() { Code = JT808ResultCode.Ok, Data = false,Message="不能删除服务器配置的地址" }; | |||||
} | |||||
else | |||||
{ | |||||
return new JT808ResultDto<bool>() { Code = JT808ResultCode.Ok, Data = ForwardingRemoteAddresssDict.TryRemove(host,out var temp) }; | |||||
} | |||||
} | |||||
public JT808ResultDto<List<string>> GetAll() | |||||
{ | |||||
return new JT808ResultDto<List<string>>(){ Code = JT808ResultCode.Ok, Data = ForwardingRemoteAddresssDict.Select(s=>s.Key).ToList() }; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
jT808ConfigurationOptionsMonitorDisposable.Dispose(); | |||||
} | |||||
} | |||||
} |
@@ -1,56 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.DotNetty.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
internal class JT808UnificationSendServiceDefaultImpl : IJT808UnificationSendService | |||||
{ | |||||
private readonly JT808SessionManager jT808SessionManager; | |||||
public JT808UnificationSendServiceDefaultImpl(JT808SessionManager jT808SessionManager) | |||||
{ | |||||
this.jT808SessionManager = jT808SessionManager; | |||||
} | |||||
public JT808ResultDto<bool> Send(string terminalPhoneNo, byte[] data) | |||||
{ | |||||
JT808ResultDto<bool> resultDto = new JT808ResultDto<bool>(); | |||||
try | |||||
{ | |||||
var session = jT808SessionManager.GetSession(terminalPhoneNo); | |||||
if (session != null) | |||||
{ | |||||
if (session.Channel.Open) | |||||
{ | |||||
session.Channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(data)); | |||||
resultDto.Code = JT808ResultCode.Ok; | |||||
resultDto.Data = true; | |||||
} | |||||
else | |||||
{ | |||||
resultDto.Code = JT808ResultCode.Ok; | |||||
resultDto.Data = false; | |||||
resultDto.Message = "offline"; | |||||
} | |||||
} | |||||
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; | |||||
} | |||||
} | |||||
} |
@@ -1,253 +0,0 @@ | |||||
using JT808.DotNetty.Dtos; | |||||
using JT808.DotNetty.Interfaces; | |||||
using JT808.DotNetty.Metadata; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Internal | |||||
{ | |||||
/// <summary> | |||||
/// JT808 WebApi 业务服务 | |||||
/// </summary> | |||||
internal class JT808WebAPIService | |||||
{ | |||||
public Dictionary<string, Func<JT808HttpRequest, JT808HttpResponse>> HandlerDict { get; protected set; } | |||||
private const string RouteTablePrefix = "/jt808api"; | |||||
private const string sessionRoutePrefix = "Session"; | |||||
private const string sourcePackagePrefix = "SourcePackage"; | |||||
private const string transmitPrefix = "Transmit"; | |||||
private readonly IJT808SessionService jT808SessionService; | |||||
private readonly IJT808UnificationSendService jT808UnificationSendService; | |||||
private readonly JT808AtomicCounterService jT808AtomicCounterService; | |||||
private readonly JT808SourcePackageChannelService jT808SourcePackageChannelService; | |||||
private readonly JT808TransmitAddressFilterService jT808TransmitAddressFilterService; | |||||
/// <summary> | |||||
/// 初始化消息处理业务 | |||||
/// </summary> | |||||
public JT808WebAPIService( | |||||
JT808AtomicCounterService jT808AtomicCounterService, | |||||
JT808SourcePackageChannelService jT808SourcePackageChannelService, | |||||
JT808TransmitAddressFilterService jT808TransmitAddressFilterService, | |||||
IJT808SessionService jT808SessionService, | |||||
IJT808UnificationSendService jT808UnificationSendService) | |||||
{ | |||||
this.jT808AtomicCounterService = jT808AtomicCounterService; | |||||
this.jT808SourcePackageChannelService = jT808SourcePackageChannelService; | |||||
this.jT808TransmitAddressFilterService = jT808TransmitAddressFilterService; | |||||
this.jT808SessionService = jT808SessionService; | |||||
this.jT808UnificationSendService = jT808UnificationSendService; | |||||
HandlerDict = new Dictionary<string, Func<JT808HttpRequest, JT808HttpResponse>> | |||||
{ | |||||
{$"{RouteTablePrefix}/UnificationSend", UnificationSend}, | |||||
{$"{RouteTablePrefix}/{sessionRoutePrefix}/GetAll", GetSessionAll}, | |||||
{$"{RouteTablePrefix}/{sessionRoutePrefix}/RemoveByTerminalPhoneNo", RemoveByTerminalPhoneNo}, | |||||
{$"{RouteTablePrefix}/GetAtomicCounter", GetAtomicCounter}, | |||||
{$"{RouteTablePrefix}/{sourcePackagePrefix}/Add", AddSourcePackageAddress}, | |||||
{$"{RouteTablePrefix}/{sourcePackagePrefix}/Remove", RemoveSourcePackageAddress}, | |||||
{$"{RouteTablePrefix}/{sourcePackagePrefix}/GetAll", GetSourcePackageAll}, | |||||
{$"{RouteTablePrefix}/{transmitPrefix}/Add", AddTransmitAddress}, | |||||
{$"{RouteTablePrefix}/{transmitPrefix}/Remove", RemoveTransmitAddress}, | |||||
{$"{RouteTablePrefix}/{transmitPrefix}/GetAll", GetTransmitAll}, | |||||
}; | |||||
} | |||||
/// <summary> | |||||
/// 统一下发信息 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse UnificationSend(JT808HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
JT808UnificationSendRequestDto jT808UnificationSendRequestDto = JsonConvert.DeserializeObject<JT808UnificationSendRequestDto>(request.Json); | |||||
var result = jT808UnificationSendService.Send(jT808UnificationSendRequestDto.TerminalPhoneNo, jT808UnificationSendRequestDto.Data); | |||||
return CreateJT808HttpResponse(result); | |||||
} | |||||
/// <summary> | |||||
/// 会话服务集合 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse GetSessionAll(JT808HttpRequest request) | |||||
{ | |||||
var result = jT808SessionService.GetAll(); | |||||
return CreateJT808HttpResponse(result); | |||||
} | |||||
/// <summary> | |||||
/// 会话服务-通过设备终端号移除对应会话 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse RemoveByTerminalPhoneNo(JT808HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
var result = jT808SessionService.RemoveByTerminalPhoneNo(request.Json); | |||||
return CreateJT808HttpResponse(result); | |||||
} | |||||
/// <summary> | |||||
/// 获取包计数器 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse GetAtomicCounter(JT808HttpRequest request) | |||||
{ | |||||
JT808AtomicCounterDto jT808AtomicCounterDto = new JT808AtomicCounterDto(); | |||||
jT808AtomicCounterDto.MsgFailCount = jT808AtomicCounterService.MsgFailCount; | |||||
jT808AtomicCounterDto.MsgSuccessCount = jT808AtomicCounterService.MsgSuccessCount; | |||||
return CreateJT808HttpResponse(new JT808ResultDto<JT808AtomicCounterDto> | |||||
{ | |||||
Code=JT808ResultCode.Ok, | |||||
Data= jT808AtomicCounterDto | |||||
}); | |||||
} | |||||
/// <summary> | |||||
/// 添加原包转发地址 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse AddSourcePackageAddress(JT808HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
JT808IPAddressDto jT808IPAddressDto = JsonConvert.DeserializeObject<JT808IPAddressDto>(request.Json); | |||||
return CreateJT808HttpResponse(jT808SourcePackageChannelService.Add(jT808IPAddressDto).Result); | |||||
} | |||||
/// <summary> | |||||
/// 删除原包转发地址(不能删除在网关服务器配置文件配的地址) | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse RemoveSourcePackageAddress(JT808HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
JT808IPAddressDto jT808IPAddressDto = JsonConvert.DeserializeObject<JT808IPAddressDto>(request.Json); | |||||
return CreateJT808HttpResponse(jT808SourcePackageChannelService.Remove(jT808IPAddressDto).Result); | |||||
} | |||||
/// <summary> | |||||
/// 获取原包信息集合 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse GetSourcePackageAll(JT808HttpRequest request) | |||||
{ | |||||
return CreateJT808HttpResponse(jT808SourcePackageChannelService.GetAll()); | |||||
} | |||||
/// <summary> | |||||
/// 添加转发过滤地址 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse AddTransmitAddress(JT808HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
JT808IPAddressDto jT808IPAddressDto = JsonConvert.DeserializeObject<JT808IPAddressDto>(request.Json); | |||||
return CreateJT808HttpResponse(jT808TransmitAddressFilterService.Add(jT808IPAddressDto)); | |||||
} | |||||
/// <summary> | |||||
/// 删除转发过滤地址(不能删除在网关服务器配置文件配的地址) | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse RemoveTransmitAddress(JT808HttpRequest request) | |||||
{ | |||||
if (string.IsNullOrEmpty(request.Json)) | |||||
{ | |||||
return EmptyHttpResponse(); | |||||
} | |||||
JT808IPAddressDto jT808IPAddressDto = JsonConvert.DeserializeObject<JT808IPAddressDto>(request.Json); | |||||
return CreateJT808HttpResponse(jT808TransmitAddressFilterService.Remove(jT808IPAddressDto)); | |||||
} | |||||
/// <summary> | |||||
/// 获取转发过滤地址信息集合 | |||||
/// </summary> | |||||
/// <param name="request"></param> | |||||
/// <returns></returns> | |||||
public JT808HttpResponse GetTransmitAll(JT808HttpRequest request) | |||||
{ | |||||
return CreateJT808HttpResponse(jT808TransmitAddressFilterService.GetAll()); | |||||
} | |||||
private JT808HttpResponse CreateJT808HttpResponse(dynamic dynamicObject) | |||||
{ | |||||
byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dynamicObject)); | |||||
return new JT808HttpResponse() | |||||
{ | |||||
Data = data | |||||
}; | |||||
} | |||||
public JT808HttpResponse DefaultHttpResponse() | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808DefaultResultDto())); | |||||
return new JT808HttpResponse(json); | |||||
} | |||||
public JT808HttpResponse EmptyHttpResponse() | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto<string>() | |||||
{ | |||||
Code = JT808ResultCode.Empty, | |||||
Message ="内容为空", | |||||
Data="Content Empty" | |||||
})); | |||||
return new JT808HttpResponse(json); | |||||
} | |||||
public JT808HttpResponse NotFoundHttpResponse() | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto<string>() | |||||
{ | |||||
Code= JT808ResultCode.NotFound, | |||||
Message="没有该服务", | |||||
Data= "没有该服务" | |||||
})); | |||||
return new JT808HttpResponse(json); | |||||
} | |||||
public JT808HttpResponse ErrorHttpResponse(Exception ex) | |||||
{ | |||||
byte[] json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new JT808ResultDto<string>() | |||||
{ | |||||
Code = JT808ResultCode.Error, | |||||
Message = JsonConvert.SerializeObject(ex), | |||||
Data= ex.Message | |||||
})); | |||||
return new JT808HttpResponse(json); | |||||
} | |||||
} | |||||
} |
@@ -1,46 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>latest</LangVersion> | |||||
<Copyright>Copyright 2018.</Copyright> | |||||
<Authors>SmallChi</Authors> | |||||
<PackageId>JT808DotNetty</PackageId> | |||||
<Product>JT808DotNetty</Product> | |||||
<Description>基于DotNetty封装的JT808DotNetty专注消息业务处理</Description> | |||||
<PackageReleaseNotes>基于DotNetty封装的JT808DotNetty专注消息业务处理</PackageReleaseNotes> | |||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT808DotNetty</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT808DotNetty</PackageProjectUrl> | |||||
<PackageLicenseUrl>https://github.com/SmallChi/JT808DotNetty/blob/master/LICENSE</PackageLicenseUrl> | |||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | |||||
<Version>1.0.0</Version> | |||||
</PropertyGroup> | |||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |||||
<DocumentationFile></DocumentationFile> | |||||
</PropertyGroup> | |||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |||||
<DocumentationFile>E:\koike\My Project\JT808DotNetty\src\JT808.DotNetty\JT808.DotNetty.xml</DocumentationFile> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="DotNetty.Codecs" Version="0.6.0" /> | |||||
<PackageReference Include="DotNetty.Codecs.Http" Version="0.6.0" /> | |||||
<PackageReference Include="DotNetty.Handlers" Version="0.6.0" /> | |||||
<PackageReference Include="DotNetty.Transport.Libuv" Version="0.6.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" /> | |||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> | |||||
<PackageReference Include="Polly" Version="6.1.2" /> | |||||
<PackageReference Include="StackExchange.Redis" Version="2.0.519" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.Protocol\src\JT808.Protocol\JT808.Protocol.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,418 +0,0 @@ | |||||
<?xml version="1.0"?> | |||||
<doc> | |||||
<assembly> | |||||
<name>JT808.DotNetty</name> | |||||
</assembly> | |||||
<members> | |||||
<member name="T:JT808.DotNetty.Codecs.JT808ClientDecoder"> | |||||
<summary> | |||||
JT808客户端解码(测试客户端) | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Codecs.JT808Decoder"> | |||||
<summary> | |||||
JT808解码 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Configurations.JT808Configuration.WebApiPort"> | |||||
<summary> | |||||
WebApi服务 | |||||
默认828端口 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Configurations.JT808Configuration.SourcePackageDispatcherClientConfigurations"> | |||||
<summary> | |||||
源包分发器配置 | |||||
</summary> | |||||
</member> | |||||
<!-- 对于成员“P:JT808.DotNetty.Configurations.JT808Configuration.ForwardingRemoteAddress”忽略有格式错误的 XML 注释 --> | |||||
<member name="T:JT808.DotNetty.Dtos.JT808AtomicCounterDto"> | |||||
<summary> | |||||
包计数器服务 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.ChannelId"> | |||||
<summary> | |||||
通道Id | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.LastActiveTime"> | |||||
<summary> | |||||
最后上线时间 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.StartTime"> | |||||
<summary> | |||||
上线时间 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.TerminalPhoneNo"> | |||||
<summary> | |||||
终端手机号 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.LoaclAddressIP"> | |||||
<summary> | |||||
本地ip地址 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.WebApiPort"> | |||||
<summary> | |||||
WebApi端口号 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SessionInfoDto.RemoteAddressIP"> | |||||
<summary> | |||||
远程ip地址 | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Dtos.JT808SourcePackageChannelInfoDto"> | |||||
<summary> | |||||
原包通道信息 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SourcePackageChannelInfoDto.RemoteAddress"> | |||||
<summary> | |||||
远程地址 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SourcePackageChannelInfoDto.LocalAddress"> | |||||
<summary> | |||||
本地地址 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SourcePackageChannelInfoDto.Registered"> | |||||
<summary> | |||||
是否注册 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SourcePackageChannelInfoDto.Active"> | |||||
<summary> | |||||
是否活动 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Dtos.JT808SourcePackageChannelInfoDto.Open"> | |||||
<summary> | |||||
是否打开 | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Dtos.JT808UnificationSendRequestDto"> | |||||
<summary> | |||||
统一下发请求参数 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Handlers.JT808ConnectionHandler.ChannelActive(DotNetty.Transport.Channels.IChannelHandlerContext)"> | |||||
<summary> | |||||
通道激活 | |||||
</summary> | |||||
<param name="context"></param> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Handlers.JT808ConnectionHandler.ChannelInactive(DotNetty.Transport.Channels.IChannelHandlerContext)"> | |||||
<summary> | |||||
设备主动断开 | |||||
</summary> | |||||
<param name="context"></param> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Handlers.JT808ConnectionHandler.CloseAsync(DotNetty.Transport.Channels.IChannelHandlerContext)"> | |||||
<summary> | |||||
服务器主动断开 | |||||
</summary> | |||||
<param name="context"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Handlers.JT808ConnectionHandler.UserEventTriggered(DotNetty.Transport.Channels.IChannelHandlerContext,System.Object)"> | |||||
<summary> | |||||
超时策略 | |||||
</summary> | |||||
<param name="context"></param> | |||||
<param name="evt"></param> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Handlers.JT808WebAPIServerHandler"> | |||||
<summary> | |||||
jt808 webapi服务 | |||||
请求量不大,只支持JSON格式并且只支持post发数据 | |||||
ref: dotnetty HttpServer | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Interfaces.IJT808SessionService"> | |||||
<summary> | |||||
JT808会话服务 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Interfaces.IJT808SessionService.GetAll"> | |||||
<summary> | |||||
获取会话集合 | |||||
</summary> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Interfaces.IJT808SessionService.RemoveByChannelId(System.String)"> | |||||
<summary> | |||||
通过通道Id移除对应会话 | |||||
</summary> | |||||
<param name="channelId"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Interfaces.IJT808SessionService.RemoveByTerminalPhoneNo(System.String)"> | |||||
<summary> | |||||
通过设备终端号移除对应会话 | |||||
</summary> | |||||
<param name="terminalPhoneNo"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Interfaces.IJT808SourcePackageDispatcher"> | |||||
<summary> | |||||
源包分发器 | |||||
自定义源包分发器业务 | |||||
ConfigureServices: | |||||
services.Replace(new ServiceDescriptor(typeof(IJT808SourcePackageDispatcher),typeof(JT808SourcePackageDispatcherDefaultImpl),ServiceLifetime.Singleton)); | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Interfaces.IJT808UnificationSendService"> | |||||
<summary> | |||||
JT808统一下发命令 | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Internal.JT808AtomicCounterService"> | |||||
<summary> | |||||
计数包服务 | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Internal.JT808MsgIdDefaultHandler"> | |||||
<summary> | |||||
默认消息处理业务实现 | |||||
</summary> | |||||
</member> | |||||
<!-- 对于成员“T:JT808.DotNetty.Internal.JT808RemoteAddressTransmitConfigurationService”忽略有格式错误的 XML 注释 --> | |||||
<member name="T:JT808.DotNetty.Internal.JT808SourcePackageChannelService"> | |||||
<summary> | |||||
原包分发器通道服务 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808SourcePackageChannelService.SendAsync(System.Byte[])"> | |||||
<summary> | |||||
下发数据 | |||||
</summary> | |||||
<param name="data"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808SourcePackageChannelService.GetAll"> | |||||
<summary> | |||||
获取通道信息集合 | |||||
</summary> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808SourcePackageChannelService.Add(JT808.DotNetty.Dtos.JT808IPAddressDto)"> | |||||
<summary> | |||||
添加地址 | |||||
</summary> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808SourcePackageChannelService.Remove(JT808.DotNetty.Dtos.JT808IPAddressDto)"> | |||||
<summary> | |||||
删除地址 | |||||
</summary> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808SourcePackageChannelService.DelRemoteServsers(System.Collections.Generic.List{JT808.DotNetty.Configurations.JT808ClientConfiguration})"> | |||||
<summary> | |||||
动态删除远程服务器 | |||||
</summary> | |||||
<param name="chgRemoteServers"></param> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808SourcePackageChannelService.AddRemoteServsers(System.Collections.Generic.List{JT808.DotNetty.Configurations.JT808ClientConfiguration})"> | |||||
<summary> | |||||
动态添加远程服务器 | |||||
</summary> | |||||
<param name="bootstrap"></param> | |||||
<param name="chgRemoteServers"></param> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Internal.JT808SourcePackageDispatcherDefaultImpl"> | |||||
<summary> | |||||
原包分发器默认实现 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.#ctor(JT808.DotNetty.Internal.JT808AtomicCounterService,JT808.DotNetty.Internal.JT808SourcePackageChannelService,JT808.DotNetty.Interfaces.IJT808SessionService,JT808.DotNetty.Interfaces.IJT808UnificationSendService)"> | |||||
<summary> | |||||
初始化消息处理业务 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.UnificationSend(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
统一下发信息 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.GetSessionAll(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
会话服务集合 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.RemoveByChannelId(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
会话服务-通过通道Id移除对应会话 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.RemoveByTerminalPhoneNo(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
会话服务-通过设备终端号移除对应会话 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.GetAtomicCounter(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
获取包计数器 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.AddSourcePackageAddress(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
添加原包转发地址 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.RemoveSourcePackageAddress(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
删除原包转发地址(不能删除在网关服务器配置文件配的地址) | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.Internal.JT808WebAPIService.GetSourcePackageAll(JT808.DotNetty.Metadata.JT808HttpRequest)"> | |||||
<summary> | |||||
获取原包信息集合 | |||||
</summary> | |||||
<param name="request"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.JT808MsgIdHandlerBase"> | |||||
<summary> | |||||
抽象消息处理业务 | |||||
自定义消息处理业务 | |||||
ConfigureServices: | |||||
services.Replace(new ServiceDescriptor(typeof(JT808MsgIdHandlerBase),typeof(JT808MsgIdCustomHandlerImpl),ServiceLifetime.Singleton)); | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.#ctor(JT808.DotNetty.JT808SessionManager)"> | |||||
<summary> | |||||
初始化消息处理业务 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0001(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
终端通用应答 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0002(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
终端心跳 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0003(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
终端注销 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0100(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
终端注册 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0102(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
终端鉴权 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0200(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
位置信息汇报 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0704(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
定位数据批量上传 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT808.DotNetty.JT808MsgIdHandlerBase.Msg0x0900(JT808.DotNetty.Metadata.JT808Request)"> | |||||
<summary> | |||||
数据上行透传 | |||||
</summary> | |||||
<param name="reqJT808Package"></param> | |||||
<param name="ctx"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="F:JT808.DotNetty.JT808SessionManager.SessionIdDict"> | |||||
<summary> | |||||
Netty生成的sessionID和Session的对应关系 | |||||
key = seession id | |||||
value = Session | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT808.DotNetty.JT808SessionManager.TerminalPhoneNo_SessionId_Dict"> | |||||
<summary> | |||||
终端手机号和netty生成的sessionID的对应关系 | |||||
key = 终端手机号 | |||||
value = seession id | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.JT808SessionManager.RealSessionCount"> | |||||
<summary> | |||||
获取实际连接数 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.JT808SessionManager.RelevanceSessionCount"> | |||||
<summary> | |||||
获取设备相关连的连接数 | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.JT808WebAPIServerHost"> | |||||
<summary> | |||||
集成一个webapi服务 | |||||
</summary> | |||||
</member> | |||||
<member name="T:JT808.DotNetty.Metadata.JT808AtomicCounter"> | |||||
<summary> | |||||
<see cref="!:Grpc.Core.Internal"/> | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Metadata.JT808Response.MinBufferSize"> | |||||
<summary> | |||||
根据实际情况适当调整包的大小 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT808.DotNetty.Metadata.JT808Session.TerminalPhoneNo"> | |||||
<summary> | |||||
终端手机号 | |||||
</summary> | |||||
</member> | |||||
</members> | |||||
</doc> |
@@ -1,13 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
public static class JT808Constants | |||||
{ | |||||
public const string SessionOnline= "JT808SessionOnline"; | |||||
public const string SessionOffline = "JT808SessionOffline"; | |||||
} | |||||
} |
@@ -1,58 +0,0 @@ | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using JT808.DotNetty.Interfaces; | |||||
using JT808.DotNetty.Internal; | |||||
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; | |||||
[assembly: InternalsVisibleTo("JT808.DotNetty.Test")] | |||||
namespace JT808.DotNetty | |||||
{ | |||||
public static class JT808DotnettyExtensions | |||||
{ | |||||
static JT808DotnettyExtensions() | |||||
{ | |||||
JsonSerializerSettings setting = new JsonSerializerSettings(); | |||||
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; | |||||
setting.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat; | |||||
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss"; | |||||
setting.NullValueHandling = NullValueHandling.Include; | |||||
return setting; | |||||
}); | |||||
} | |||||
public static IHostBuilder UseJT808Host(this IHostBuilder builder) | |||||
{ | |||||
return builder.ConfigureServices((hostContext, services) => | |||||
{ | |||||
services.Configure<JT808Configuration>(hostContext.Configuration.GetSection("JT808Configuration")); | |||||
services.TryAddSingleton<IJT808SessionPublishing, JT808SessionPublishingEmptyImpl>(); | |||||
services.TryAddSingleton<JT808SessionManager>(); | |||||
services.TryAddSingleton<JT808AtomicCounterService>(); | |||||
services.TryAddSingleton<JT808TransmitAddressFilterService>(); | |||||
services.TryAddSingleton<JT808MsgIdHandlerBase,JT808MsgIdDefaultHandler>(); | |||||
services.TryAddSingleton<JT808SourcePackageChannelService>(); | |||||
services.TryAddSingleton<IJT808SourcePackageDispatcher, JT808SourcePackageDispatcherDefaultImpl>(); | |||||
services.TryAddScoped<JT808ConnectionHandler>(); | |||||
services.TryAddScoped<JT808Decoder>(); | |||||
services.TryAddScoped<JT808ServerHandler>(); | |||||
services.TryAddScoped<JT808UDPServerHandler>(); | |||||
services.TryAddSingleton<IJT808SessionService, JT808SessionServiceDefaultImpl>(); | |||||
services.TryAddSingleton<IJT808UnificationSendService, JT808UnificationSendServiceDefaultImpl>(); | |||||
services.TryAddSingleton<JT808WebAPIService>(); | |||||
services.TryAddScoped<JT808WebAPIServerHandler>(); | |||||
services.AddHostedService<JT808ServerHost>(); | |||||
services.AddHostedService<JT808WebAPIServerHost>(); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,160 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using JT808.DotNetty.Metadata; | |||||
using JT808.Protocol.Enums; | |||||
using JT808.Protocol.Extensions; | |||||
using JT808.Protocol.MessageBody; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
/// <summary> | |||||
/// 抽象消息处理业务 | |||||
/// 自定义消息处理业务 | |||||
/// 注意: | |||||
/// 1.ConfigureServices: | |||||
/// services.Replace(new ServiceDescriptor(typeof(JT808MsgIdHandlerBase),typeof(JT808MsgIdCustomHandlerImpl),ServiceLifetime.Singleton)); | |||||
/// 2.解析具体的消息体,具体消息调用具体的JT808Serializer.Deserialize<T> | |||||
/// </summary> | |||||
public abstract class JT808MsgIdHandlerBase | |||||
{ | |||||
protected JT808SessionManager sessionManager { get; } | |||||
/// <summary> | |||||
/// 初始化消息处理业务 | |||||
/// </summary> | |||||
protected JT808MsgIdHandlerBase(JT808SessionManager sessionManager) | |||||
{ | |||||
this.sessionManager = sessionManager; | |||||
HandlerDict = new Dictionary<ushort, Func<JT808Request, JT808Response>> | |||||
{ | |||||
{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<ushort, Func<JT808Request, JT808Response>> HandlerDict { get; protected set; } | |||||
/// <summary> | |||||
/// 终端通用应答 | |||||
/// 平台无需回复 | |||||
/// 实现自己的业务 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response Msg0x0001(JT808Request request) | |||||
{ | |||||
return null; | |||||
} | |||||
/// <summary> | |||||
/// 终端心跳 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 终端注销 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 终端注册 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 终端鉴权 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 位置信息汇报 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 定位数据批量上传 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
/// <summary> | |||||
/// 数据上行透传 | |||||
/// </summary> | |||||
/// <param name="reqJT808Package"></param> | |||||
/// <param name="ctx"></param> | |||||
/// <returns></returns> | |||||
public virtual JT808Response 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 | |||||
})); | |||||
} | |||||
} | |||||
} |
@@ -1,96 +0,0 @@ | |||||
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.Codecs; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
/// <summary> | |||||
/// JT808 网关服务 | |||||
/// </summary> | |||||
internal class JT808ServerHost : IHostedService | |||||
{ | |||||
private readonly IServiceProvider serviceProvider; | |||||
private readonly JT808Configuration configuration; | |||||
private readonly ILogger<JT808ServerHost> logger; | |||||
private DispatcherEventLoopGroup bossGroup; | |||||
private WorkerEventLoopGroup workerGroup; | |||||
private IChannel bootstrapChannel; | |||||
private IByteBufferAllocator serverBufferAllocator; | |||||
public JT808ServerHost( | |||||
IServiceProvider provider, | |||||
ILoggerFactory loggerFactory, | |||||
IOptions<JT808Configuration> jT808ConfigurationAccessor) | |||||
{ | |||||
serviceProvider = provider; | |||||
configuration = jT808ConfigurationAccessor.Value; | |||||
logger=loggerFactory.CreateLogger<JT808ServerHost>(); | |||||
} | |||||
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<TcpServerChannel>(); | |||||
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<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
using (var scope = serviceProvider.CreateScope()) | |||||
{ | |||||
channel.Pipeline.AddLast("systemIdleState", new IdleStateHandler( | |||||
configuration.ReaderIdleTimeSeconds, | |||||
configuration.WriterIdleTimeSeconds, | |||||
configuration.AllIdleTimeSeconds)); | |||||
channel.Pipeline.AddLast("jt808Connection", scope.ServiceProvider.GetRequiredService<JT808ConnectionHandler>()); | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", scope.ServiceProvider.GetRequiredService<JT808Decoder>()); | |||||
channel.Pipeline.AddLast("jt808Service", scope.ServiceProvider.GetRequiredService<JT808ServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"Server start at {IPAddress.Any}:{configuration.Port}."); | |||||
return bootstrap.BindAsync(configuration.Port) | |||||
.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); | |||||
} | |||||
} | |||||
} |
@@ -1,141 +0,0 @@ | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using JT808.DotNetty.Metadata; | |||||
using DotNetty.Transport.Channels; | |||||
using JT808.DotNetty.Interfaces; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
/// <summary> | |||||
/// JT808会话管理 | |||||
/// </summary> | |||||
public class JT808SessionManager | |||||
{ | |||||
private readonly ILogger<JT808SessionManager> logger; | |||||
private readonly IJT808SessionPublishing jT808SessionPublishing; | |||||
public JT808SessionManager( | |||||
IJT808SessionPublishing jT808SessionPublishing, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.jT808SessionPublishing = jT808SessionPublishing; | |||||
logger = loggerFactory.CreateLogger<JT808SessionManager>(); | |||||
} | |||||
private ConcurrentDictionary<string, JT808Session> SessionIdDict = new ConcurrentDictionary<string, JT808Session>(StringComparer.OrdinalIgnoreCase); | |||||
public int SessionCount | |||||
{ | |||||
get | |||||
{ | |||||
return SessionIdDict.Count; | |||||
} | |||||
} | |||||
public JT808Session GetSession(string terminalPhoneNo) | |||||
{ | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) | |||||
return default; | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808Session targetSession)) | |||||
{ | |||||
return targetSession; | |||||
} | |||||
else | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
public void Heartbeat(string terminalPhoneNo) | |||||
{ | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) return; | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808Session oldjT808Session)) | |||||
{ | |||||
oldjT808Session.LastActiveTime = DateTime.Now; | |||||
SessionIdDict.TryUpdate(terminalPhoneNo, oldjT808Session, oldjT808Session); | |||||
} | |||||
} | |||||
public void TryAdd(JT808Session appSession) | |||||
{ | |||||
// 解决了设备号跟通道绑定到一起,不需要用到通道本身的SessionId | |||||
// 不管设备下发更改了设备终端号,只要是没有在内存中就当是新的 | |||||
// 存在的问题: | |||||
// 1.原先老的如何销毁 | |||||
// 2.这时候用的通道是相同的,设备终端是不同的 | |||||
// 当设备主动或者服务器断开以后,可以释放,这点内存忽略不计,况且更改设备号不是很频繁。 | |||||
if (SessionIdDict.TryAdd(appSession.TerminalPhoneNo, appSession)) | |||||
{ | |||||
//使用场景: | |||||
//部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接, | |||||
//这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。 | |||||
//todo: 有设备关联上来可以进行通知 例如:使用Redis发布订阅 | |||||
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline,null, appSession.TerminalPhoneNo); | |||||
} | |||||
} | |||||
public JT808Session RemoveSession(string terminalPhoneNo) | |||||
{ | |||||
//todo: 设备离线可以进行通知 | |||||
//todo: 使用Redis 发布订阅 | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) return default; | |||||
if (!SessionIdDict.TryGetValue(terminalPhoneNo, out JT808Session 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 JT808Session jT808SessionRemove); | |||||
} | |||||
string nos = string.Join(",", terminalPhoneNos); | |||||
logger.LogInformation($">>>{terminalPhoneNo}-{nos} 1-n Session Remove."); | |||||
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, null, nos); | |||||
return jT808Session; | |||||
} | |||||
else | |||||
{ | |||||
if (SessionIdDict.TryRemove(terminalPhoneNo, out JT808Session jT808SessionRemove)) | |||||
{ | |||||
logger.LogInformation($">>>{terminalPhoneNo} Session Remove."); | |||||
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, null, terminalPhoneNo); | |||||
return jT808SessionRemove; | |||||
} | |||||
else | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
} | |||||
internal void RemoveSessionByChannel(IChannel channel) | |||||
{ | |||||
//todo: 设备离线可以进行通知 | |||||
//todo: 使用Redis 发布订阅 | |||||
var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList(); | |||||
foreach (var key in terminalPhoneNos) | |||||
{ | |||||
SessionIdDict.TryRemove(key, out JT808Session jT808SessionRemove); | |||||
} | |||||
string nos = string.Join(",", terminalPhoneNos); | |||||
logger.LogInformation($">>>{nos} Channel Remove."); | |||||
jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, null, nos); | |||||
} | |||||
public IEnumerable<JT808Session> GetAll() | |||||
{ | |||||
return SessionIdDict.Select(s => s.Value).ToList(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,77 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
public class JT808SimpleTcpClient | |||||
{ | |||||
private Bootstrap cb; | |||||
private MultithreadEventLoopGroup clientGroup; | |||||
private IChannel clientChannel; | |||||
private IByteBufferAllocator clientBufferAllocator; | |||||
public JT808SimpleTcpClient(EndPoint remoteAddress) | |||||
{ | |||||
clientBufferAllocator = new PooledByteBufferAllocator(); | |||||
clientGroup = new MultithreadEventLoopGroup(1); | |||||
cb = new Bootstrap() | |||||
.Group(clientGroup) | |||||
.Channel<TcpSocketChannel>() | |||||
.Option(ChannelOption.TcpNodelay, true) | |||||
.Option(ChannelOption.Allocator, clientBufferAllocator) | |||||
.Handler(new ActionChannelInitializer<TcpSocketChannel>(channel => | |||||
{ | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", new JT808ClientDecoder()); | |||||
})); | |||||
clientChannel = cb.ConnectAsync(remoteAddress).Result; | |||||
} | |||||
public JT808SimpleTcpClient(EndPoint remoteAddress, EndPoint localAddress) | |||||
{ | |||||
clientBufferAllocator = new PooledByteBufferAllocator(); | |||||
clientGroup = new MultithreadEventLoopGroup(1); | |||||
cb = new Bootstrap() | |||||
.Group(clientGroup) | |||||
.Channel<TcpSocketChannel>() | |||||
.Option(ChannelOption.TcpNodelay, true) | |||||
.Option(ChannelOption.Allocator, clientBufferAllocator) | |||||
.Handler(new ActionChannelInitializer<TcpSocketChannel>(channel => | |||||
{ | |||||
channel.Pipeline.AddLast("jt808Buffer", new DelimiterBasedFrameDecoder(int.MaxValue, | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }), | |||||
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag }))); | |||||
channel.Pipeline.AddLast("jt808Decode", new JT808ClientDecoder()); | |||||
})); | |||||
clientChannel = cb.ConnectAsync(remoteAddress, localAddress).Result; | |||||
} | |||||
public void WriteAsync(byte[] data) | |||||
{ | |||||
clientChannel.WriteAndFlushAsync(Unpooled.WrappedBuffer(data)); | |||||
} | |||||
public void Down() | |||||
{ | |||||
this.clientChannel?.CloseAsync().Wait(); | |||||
Task.WaitAll(this.clientGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1))); | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT808.DotNetty.Codecs; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
/// <summary> | |||||
/// JT808 Udp网关服务 | |||||
/// </summary> | |||||
internal class JT808UdpServerHost : IHostedService | |||||
{ | |||||
private readonly IServiceProvider serviceProvider; | |||||
private readonly JT808Configuration configuration; | |||||
private readonly ILogger<JT808UdpServerHost> logger; | |||||
private MultithreadEventLoopGroup group; | |||||
private IChannel bootstrapChannel; | |||||
public JT808UdpServerHost( | |||||
IServiceProvider provider, | |||||
ILoggerFactory loggerFactory, | |||||
IOptions<JT808Configuration> jT808ConfigurationAccessor) | |||||
{ | |||||
serviceProvider = provider; | |||||
configuration = jT808ConfigurationAccessor.Value; | |||||
logger=loggerFactory.CreateLogger<JT808UdpServerHost>(); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
group = new MultithreadEventLoopGroup(); | |||||
Bootstrap bootstrap = new Bootstrap(); | |||||
bootstrap.Group(group); | |||||
bootstrap.Channel<SocketDatagramChannel>(); | |||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) | |||||
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | |||||
{ | |||||
bootstrap | |||||
.Option(ChannelOption.SoReuseport, true); | |||||
} | |||||
bootstrap | |||||
.Option(ChannelOption.SoBacklog, configuration.SoBacklog) | |||||
.Handler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
using (var scope = serviceProvider.CreateScope()) | |||||
{ | |||||
pipeline.AddLast(new JT808UDPDecoder()); | |||||
pipeline.AddLast("jt808UDPService", scope.ServiceProvider.GetRequiredService<JT808UDPServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"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); | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT808.DotNetty.Configurations; | |||||
using JT808.DotNetty.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Net; | |||||
using System.Runtime.InteropServices; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT808.DotNetty | |||||
{ | |||||
/// <summary> | |||||
/// 集成一个webapi服务 | |||||
/// </summary> | |||||
internal class JT808WebAPIServerHost : IHostedService | |||||
{ | |||||
private readonly IServiceProvider serviceProvider; | |||||
private readonly JT808Configuration configuration; | |||||
private readonly ILogger<JT808WebAPIServerHost> logger; | |||||
private DispatcherEventLoopGroup bossGroup; | |||||
private WorkerEventLoopGroup workerGroup; | |||||
private IChannel bootstrapChannel; | |||||
public JT808WebAPIServerHost( | |||||
IServiceProvider provider, | |||||
ILoggerFactory loggerFactory, | |||||
IOptions<JT808Configuration> jT808ConfigurationAccessor) | |||||
{ | |||||
serviceProvider = provider; | |||||
configuration = jT808ConfigurationAccessor.Value; | |||||
logger = loggerFactory.CreateLogger<JT808WebAPIServerHost>(); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
bossGroup = new DispatcherEventLoopGroup(); | |||||
workerGroup = new WorkerEventLoopGroup(bossGroup, 1); | |||||
ServerBootstrap bootstrap = new ServerBootstrap(); | |||||
bootstrap.Group(bossGroup, workerGroup); | |||||
bootstrap.Channel<TcpServerChannel>(); | |||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) | |||||
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | |||||
{ | |||||
bootstrap | |||||
.Option(ChannelOption.SoReuseport, true) | |||||
.ChildOption(ChannelOption.SoReuseaddr, true); | |||||
} | |||||
bootstrap | |||||
.Option(ChannelOption.SoBacklog, 8192) | |||||
.ChildHandler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
using (var scope = serviceProvider.CreateScope()) | |||||
{ | |||||
pipeline.AddLast("http_encoder", new HttpResponseEncoder()); | |||||
pipeline.AddLast("http_decoder", new HttpRequestDecoder(4096, 8192, 8192, false)); | |||||
//将多个消息转换为单一的request或者response对象 =>IFullHttpRequest | |||||
pipeline.AddLast("http_aggregator", new HttpObjectAggregator(65536)); | |||||
pipeline.AddLast("http_jt808webapihandler", scope.ServiceProvider.GetRequiredService<JT808WebAPIServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"WebAPI Server start at {IPAddress.Any}:{configuration.WebApiPort}."); | |||||
return bootstrap.BindAsync(configuration.WebApiPort).ContinueWith(i => bootstrapChannel = i.Result); | |||||
} | |||||
public async Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
await bootstrapChannel.CloseAsync(); | |||||
var quietPeriod = configuration.QuietPeriodTimeSpan; | |||||
var shutdownTimeout = configuration.ShutdownTimeoutTimeSpan; | |||||
await workerGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); | |||||
await bossGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); | |||||
} | |||||
} | |||||
} |
@@ -1,44 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// <see cref="Grpc.Core.Internal"/> | |||||
/// </summary> | |||||
internal class JT808AtomicCounter | |||||
{ | |||||
long counter = 0; | |||||
public JT808AtomicCounter(long initialCount = 0) | |||||
{ | |||||
this.counter = initialCount; | |||||
} | |||||
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); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,22 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
public class JT808HttpRequest | |||||
{ | |||||
public string Json { get; set; } | |||||
public JT808HttpRequest() | |||||
{ | |||||
} | |||||
public JT808HttpRequest(string json) | |||||
{ | |||||
Json = json; | |||||
} | |||||
} | |||||
} |
@@ -1,22 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
public class JT808HttpResponse | |||||
{ | |||||
public byte[] Data { get; set; } | |||||
public JT808HttpResponse() | |||||
{ | |||||
} | |||||
public JT808HttpResponse(byte[] data) | |||||
{ | |||||
this.Data = data; | |||||
} | |||||
} | |||||
} |
@@ -1,23 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
public class JT808Request | |||||
{ | |||||
public JT808HeaderPackage Package { get; } | |||||
/// <summary> | |||||
/// 用于消息发送 | |||||
/// </summary> | |||||
public byte[] OriginalPackage { get;} | |||||
public JT808Request(JT808HeaderPackage package, byte[] originalPackage) | |||||
{ | |||||
Package = package; | |||||
OriginalPackage = originalPackage; | |||||
} | |||||
} | |||||
} |
@@ -1,27 +0,0 @@ | |||||
using JT808.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
public class JT808Response | |||||
{ | |||||
public JT808Package Package { get; set; } | |||||
/// <summary> | |||||
/// 根据实际情况适当调整包的大小 | |||||
/// </summary> | |||||
public int MinBufferSize { get; set; } | |||||
public JT808Response() | |||||
{ | |||||
} | |||||
public JT808Response(JT808Package package, int minBufferSize = 1024) | |||||
{ | |||||
Package = package; | |||||
MinBufferSize = minBufferSize; | |||||
} | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
public class JT808Session | |||||
{ | |||||
public JT808Session(IChannel channel, string terminalPhoneNo) | |||||
{ | |||||
Channel = channel; | |||||
TerminalPhoneNo = terminalPhoneNo; | |||||
StartTime = DateTime.Now; | |||||
LastActiveTime = DateTime.Now; | |||||
} | |||||
public JT808Session() { } | |||||
/// <summary> | |||||
/// 终端手机号 | |||||
/// </summary> | |||||
public string TerminalPhoneNo { get; set; } | |||||
public IChannel Channel { get; set; } | |||||
public DateTime LastActiveTime { get; set; } | |||||
public DateTime StartTime { get; set; } | |||||
} | |||||
} |
@@ -1,20 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT808.DotNetty.Metadata | |||||
{ | |||||
internal class JT808UDPPackage | |||||
{ | |||||
public JT808UDPPackage(byte[] buffer, EndPoint sender) | |||||
{ | |||||
Buffer = buffer; | |||||
Sender = sender; | |||||
} | |||||
public byte[] Buffer { get; } | |||||
public EndPoint Sender { get; } | |||||
} | |||||
} |