瀏覽代碼

添加客户端调用工具

tags/v1.2.0
SmallChi 6 年之前
父節點
當前提交
db2601217f
共有 14 個文件被更改,包括 559 次插入5 次删除
  1. +20
    -0
      src/JT808.DotNetty.Client/Codecs/JT808ClientTcpDecoder.cs
  2. +44
    -0
      src/JT808.DotNetty.Client/Codecs/JT808ClientTcpEncoder.cs
  3. +27
    -0
      src/JT808.DotNetty.Client/DeviceConfig.cs
  4. +95
    -0
      src/JT808.DotNetty.Client/Handlers/JT808TcpClientConnectionHandler.cs
  5. +27
    -0
      src/JT808.DotNetty.Client/Handlers/JT808TcpClientHandler.cs
  6. +25
    -2
      src/JT808.DotNetty.Client/JT808.DotNetty.Client.csproj
  7. +16
    -0
      src/JT808.DotNetty.Client/JT808ClientDotnettyExtensions.cs
  8. +15
    -0
      src/JT808.DotNetty.Client/JT808ClientMsgSNDistributedImpl.cs
  9. +97
    -2
      src/JT808.DotNetty.Client/JT808TcpClient.cs
  10. +103
    -0
      src/JT808.DotNetty.Client/JT808TcpClientExtensions.cs
  11. +53
    -0
      src/JT808.DotNetty.Client/JT808TcpClientFactory.cs
  12. +30
    -0
      src/JT808.DotNetty.Client/Metadata/JT808ClientRequest.cs
  13. +6
    -0
      src/JT808.DotNetty.Core/Metadata/JT808Response.cs
  14. +1
    -1
      src/JT808.DotNetty.sln

+ 20
- 0
src/JT808.DotNetty.Client/Codecs/JT808ClientTcpDecoder.cs 查看文件

@@ -0,0 +1,20 @@
using DotNetty.Buffers;
using DotNetty.Codecs;
using System.Collections.Generic;
using JT808.Protocol;
using DotNetty.Transport.Channels;

namespace JT808.DotNetty.Client.Codecs
{
public class JT808ClientTcpDecoder : ByteToMessageDecoder
{
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<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);
}
}
}

+ 44
- 0
src/JT808.DotNetty.Client/Codecs/JT808ClientTcpEncoder.cs 查看文件

@@ -0,0 +1,44 @@
using DotNetty.Buffers;
using DotNetty.Codecs;
using System.Collections.Generic;
using JT808.Protocol;
using DotNetty.Transport.Channels;
using Microsoft.Extensions.Logging;
using JT808.DotNetty.Client.Metadata;

namespace JT808.DotNetty.Client.Codecs
{
public class JT808ClientTcpEncoder : MessageToByteEncoder<JT808ClientRequest>
{
private readonly ILogger<JT808ClientTcpEncoder> logger;

public JT808ClientTcpEncoder(ILoggerFactory loggerFactory)
{
logger=loggerFactory.CreateLogger<JT808ClientTcpEncoder>();
}

protected override void Encode(IChannelHandlerContext context, JT808ClientRequest message, IByteBuffer output)
{
if (message.Package != null)
{
try
{
var sendData = JT808Serializer.Serialize(message.Package, message.MinBufferSize);
output.WriteBytes(sendData);
}
catch (JT808.Protocol.Exceptions.JT808Exception ex)
{
logger.LogError(ex, context.Channel.Id.AsShortText());
}
catch (System.Exception ex)
{
logger.LogError(ex, context.Channel.Id.AsShortText());
}
}
else if (message.HexData != null)
{
output.WriteBytes(message.HexData);
}
}
}
}

+ 27
- 0
src/JT808.DotNetty.Client/DeviceConfig.cs 查看文件

@@ -0,0 +1,27 @@
using JT808.Protocol;
using System;
using System.Collections.Generic;
using System.Text;

namespace JT808.DotNetty.Client
{
public class DeviceConfig
{
public DeviceConfig(string terminalPhoneNo, string tcpHost,int tcpPort)
{
TerminalPhoneNo = terminalPhoneNo;
TcpHost = tcpHost;
TcpPort = tcpPort;
MsgSNDistributed = new JT808ClientMsgSNDistributedImpl();
}
public string TerminalPhoneNo { get; private set; }
public string TcpHost { get; private set; }
public int TcpPort { get; private set; }
/// <summary>
/// 心跳时间(秒)
/// </summary>
public int Heartbeat { get; set; } = 30;

public IMsgSNDistributed MsgSNDistributed { get; }
}
}

+ 95
- 0
src/JT808.DotNetty.Client/Handlers/JT808TcpClientConnectionHandler.cs 查看文件

@@ -0,0 +1,95 @@
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Channels;
using JT808.Protocol.MessageBody;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace JT808.DotNetty.Client.Handlers
{
/// <summary>
/// JT808客户端连接通道处理程序
/// </summary>
public class JT808TcpClientConnectionHandler : ChannelHandlerAdapter
{
private readonly ILogger<JT808TcpClientConnectionHandler> logger;
private readonly JT808TcpClient jT808TcpClient;

public JT808TcpClientConnectionHandler(
JT808TcpClient jT808TcpClient)
{
logger = jT808TcpClient.LoggerFactory.CreateLogger<JT808TcpClientConnectionHandler>();
this.jT808TcpClient = jT808TcpClient;
}

/// <summary>
/// 通道激活
/// </summary>
/// <param name="context"></param>
public override void ChannelActive(IChannelHandlerContext context)
{
string channelId = context.Channel.Id.AsShortText();
if (logger.IsEnabled(LogLevel.Debug))
logger.LogDebug($"<<<{ channelId } Successful client connection to server.");
base.ChannelActive(context);
}

/// <summary>
/// 设备主动断开
/// </summary>
/// <param name="context"></param>
public override void ChannelInactive(IChannelHandlerContext context)
{
string channelId = context.Channel.Id.AsShortText();
if (logger.IsEnabled(LogLevel.Debug))
logger.LogDebug($">>>{ channelId } The client disconnects from the server.");
base.ChannelInactive(context);
}

/// <summary>
/// 服务器主动断开
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task CloseAsync(IChannelHandlerContext context)
{
string channelId = context.Channel.Id.AsShortText();
if (logger.IsEnabled(LogLevel.Debug))
logger.LogDebug($"<<<{ channelId } The server disconnects from the client.");

return base.CloseAsync(context);
}

public override void ChannelReadComplete(IChannelHandlerContext context)=> context.Flush();

/// <summary>
/// 超时策略
/// </summary>
/// <param name="context"></param>
/// <param name="evt"></param>
public override void UserEventTriggered(IChannelHandlerContext context, object evt)
{
IdleStateEvent idleStateEvent = evt as IdleStateEvent;
if (idleStateEvent != null)
{
if(idleStateEvent.State== IdleState.WriterIdle)
{
string channelId = context.Channel.Id.AsShortText();
logger.LogInformation($"{idleStateEvent.State.ToString()}>>>{channelId}");
jT808TcpClient.Send(new JT808_0x0002());
}
}
base.UserEventTriggered(context, evt);
}

public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
string channelId = context.Channel.Id.AsShortText();
logger.LogError(exception,$"{channelId} {exception.Message}" );

context.CloseAsync();
}
}
}


+ 27
- 0
src/JT808.DotNetty.Client/Handlers/JT808TcpClientHandler.cs 查看文件

@@ -0,0 +1,27 @@
using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;

namespace JT808.DotNetty.Client.Handlers
{
/// <summary>
/// JT808客户端处理程序
/// </summary>
internal class JT808TcpClientHandler : SimpleChannelInboundHandler<byte[]>
{
private readonly ILogger<JT808TcpClientHandler> logger;

public JT808TcpClientHandler(JT808TcpClient jT808TcpClient)
{
logger = jT808TcpClient.LoggerFactory.CreateLogger<JT808TcpClientHandler>();
}

protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg)
{
if(logger.IsEnabled(LogLevel.Debug))
logger.LogDebug("accept msg<<<" + ByteBufferUtil.HexDump(msg));
}
}
}

+ 25
- 2
src/JT808.DotNetty.Client/JT808.DotNetty.Client.csproj 查看文件

@@ -1,11 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.1</LangVersion>
<Copyright>Copyright 2018.</Copyright>
<Authors>SmallChi</Authors>
<PackageId>JT808.DotNetty.Client</PackageId>
<Product>JT808.DotNetty.Client</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.1.1</Version>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\JT808.DotNetty.Core\JT808.DotNetty.Core.csproj" />
<PackageReference Include="DotNetty.Handlers" Version="0.6.0" />
<PackageReference Include="DotNetty.Transport.Libuv" Version="0.6.0" />
<PackageReference Include="DotNetty.Codecs" Version="0.6.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JT808.Protocol\src\JT808.Protocol\JT808.Protocol.csproj" />
</ItemGroup>

</Project>

+ 16
- 0
src/JT808.DotNetty.Client/JT808ClientDotnettyExtensions.cs 查看文件

@@ -0,0 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace JT808.DotNetty.Client
{
public static class JT808ClientDotnettyExtensions
{
public static IServiceCollection AddJT808Client(this IServiceCollection serviceDescriptors)
{
serviceDescriptors.AddSingleton<IJT808TcpClientFactory, JT808TcpClientFactory>();
return serviceDescriptors;
}
}
}

+ 15
- 0
src/JT808.DotNetty.Client/JT808ClientMsgSNDistributedImpl.cs 查看文件

@@ -0,0 +1,15 @@
using JT808.Protocol;
using System.Threading;

namespace JT808.DotNetty.Client
{
internal class JT808ClientMsgSNDistributedImpl : IMsgSNDistributed
{
int _counter = 0;

public ushort Increment()
{
return (ushort)Interlocked.Increment(ref _counter);
}
}
}

+ 97
- 2
src/JT808.DotNetty.Client/JT808TcpClient.cs 查看文件

@@ -1,11 +1,106 @@
using System;
using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using DotNetty.Transport.Libuv;
using JT808.DotNetty.Client.Handlers;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using System.Net;
using System.Threading.Tasks;
using JT808.DotNetty.Client.Metadata;
using JT808.DotNetty.Client.Codecs;

namespace JT808.DotNetty.Client
{
public class JT808TcpClient
public sealed class JT808TcpClient : IDisposable
{
private MultithreadEventLoopGroup group;

private IChannel clientChannel;

private bool disposed = false;

public DeviceConfig DeviceConfig { get; private set; }

public ILoggerFactory LoggerFactory { get; private set; }

public JT808TcpClient(DeviceConfig deviceConfig, IServiceProvider serviceProvider)
{
DeviceConfig = deviceConfig;
LoggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
group = new MultithreadEventLoopGroup(1);
Bootstrap bootstrap = new Bootstrap();
bootstrap.Group(group);
bootstrap.Channel<TcpSocketChannel>();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
bootstrap.Option(ChannelOption.SoReuseport, true);
}
bootstrap
.Option(ChannelOption.SoBacklog, 8192)
.Handler(new ActionChannelInitializer<IChannel>(channel =>
{
channel.Pipeline.AddLast("jt808TcpBuffer", new DelimiterBasedFrameDecoder(int.MaxValue,
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.BeginFlag }),
Unpooled.CopiedBuffer(new byte[] { JT808.Protocol.JT808Package.EndFlag })));
channel.Pipeline.AddLast("systemIdleState", new IdleStateHandler(60, deviceConfig.Heartbeat, 3600));
channel.Pipeline.AddLast("jt808TcpDecode", new JT808ClientTcpDecoder());
channel.Pipeline.AddLast("jt808TcpEncode", new JT808ClientTcpEncoder(LoggerFactory));
channel.Pipeline.AddLast("jt808TcpClientConnection", new JT808TcpClientConnectionHandler(this));
channel.Pipeline.AddLast("jt808TcpService", new JT808TcpClientHandler(this));
}));
Task.Run(async () =>
{
clientChannel = await bootstrap.ConnectAsync(deviceConfig.TcpHost, deviceConfig.TcpPort);
});
}

public async void Send(JT808ClientRequest request)
{
if (disposed) return;
if (clientChannel == null) throw new NullReferenceException("Channel is empty.");
if (request == null) throw new ArgumentNullException("JT808ClientRequest Parameter is empty.");
if (clientChannel.Active && clientChannel.Open)
{
await clientChannel.WriteAndFlushAsync(request);
}
}

private void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{

// 清理托管资源
group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
}
disposed = true;
}

~JT808TcpClient()
{
//必须为false
//这表明,隐式清理时,只要处理非托管资源就可以了。
Dispose(false);
}

public void Dispose()
{
//必须为true
Dispose(true);
//通知垃圾回收机制不再调用终结器(析构器)
GC.SuppressFinalize(this);
}
}
}

+ 103
- 0
src/JT808.DotNetty.Client/JT808TcpClientExtensions.cs 查看文件

@@ -0,0 +1,103 @@
using JT808.DotNetty.Client.Metadata;
using JT808.Protocol;
using JT808.Protocol.MessageBody;
using System;
using System.Collections.Generic;
using System.Text;
using JT808.Protocol.Enums;
using JT808.Protocol.Extensions;

namespace JT808.DotNetty.Client
{
public static class JT808TcpClientExtensions
{
public static void Send(this JT808TcpClient client, JT808Header header, JT808Bodies bodies, int minBufferSize = 1024)
{
JT808Package package = new JT808Package();
package.Header = header;
package.Bodies = bodies;
package.Header.TerminalPhoneNo = client.DeviceConfig.TerminalPhoneNo;
package.Header.MsgNum = client.DeviceConfig.MsgSNDistributed.Increment();
JT808ClientRequest request = new JT808ClientRequest(package, minBufferSize);
client.Send(request);
}

/// <summary>
/// 终端通用应答
/// </summary>
/// <param name="client"></param>
/// <param name="bodies"></param>
/// <param name="minBufferSize"></param>
public static void Send(this JT808TcpClient client, JT808_0x0001 bodies, int minBufferSize = 100)
{
JT808Header header = new JT808Header();
header.MsgId = JT808MsgId.终端通用应答.ToUInt16Value();
client.Send(header, bodies, minBufferSize);
}

/// <summary>
/// 终端心跳
/// </summary>
/// <param name="client"></param>
/// <param name="bodies"></param>
/// <param name="minBufferSize"></param>
public static void Send(this JT808TcpClient client, JT808_0x0002 bodies, int minBufferSize = 100)
{
JT808Header header = new JT808Header();
header.MsgId = JT808MsgId.终端心跳.ToUInt16Value();
client.Send(header, bodies, minBufferSize);
}

/// <summary>
/// 终端注销
/// </summary>
/// <param name="client"></param>
/// <param name="bodies"></param>
/// <param name="minBufferSize"></param>
public static void Send(this JT808TcpClient client, JT808_0x0003 bodies, int minBufferSize = 100)
{
JT808Header header = new JT808Header();
header.MsgId = JT808MsgId.终端注销.ToUInt16Value();
client.Send(header, bodies, minBufferSize);
}

/// <summary>
/// 终端鉴权
/// </summary>
/// <param name="client"></param>
/// <param name="bodies"></param>
/// <param name="minBufferSize"></param>
public static void Send(this JT808TcpClient client, JT808_0x0102 bodies, int minBufferSize = 100)
{
JT808Header header = new JT808Header();
header.MsgId = JT808MsgId.终端鉴权.ToUInt16Value();
client.Send(header, bodies, minBufferSize);
}

/// <summary>
/// 终端注册
/// </summary>
/// <param name="client"></param>
/// <param name="bodies"></param>
/// <param name="minBufferSize"></param>
public static void Send(this JT808TcpClient client, JT808_0x0100 bodies, int minBufferSize = 100)
{
JT808Header header = new JT808Header();
header.MsgId = JT808MsgId.终端注册.ToUInt16Value();
client.Send(header, bodies, minBufferSize);
}

/// <summary>
/// 位置信息汇报
/// </summary>
/// <param name="client"></param>
/// <param name="bodies"></param>
/// <param name="minBufferSize"></param>
public static void Send(this JT808TcpClient client, JT808_0x0200 bodies, int minBufferSize = 200)
{
JT808Header header = new JT808Header();
header.MsgId = JT808MsgId.位置信息汇报.ToUInt16Value();
client.Send(header, bodies, minBufferSize);
}
}
}

+ 53
- 0
src/JT808.DotNetty.Client/JT808TcpClientFactory.cs 查看文件

@@ -0,0 +1,53 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;

namespace JT808.DotNetty.Client
{
public interface IJT808TcpClientFactory : IDisposable
{
JT808TcpClient Create(DeviceConfig deviceConfig);
}

public class JT808TcpClientFactory: IJT808TcpClientFactory
{
private readonly ConcurrentDictionary<string, JT808TcpClient> dict;

private readonly IServiceProvider serviceProvider;

public JT808TcpClientFactory(IServiceProvider serviceProvider)
{
dict = new ConcurrentDictionary<string, JT808TcpClient>(StringComparer.OrdinalIgnoreCase);
this.serviceProvider = serviceProvider;
}

public JT808TcpClient Create(DeviceConfig deviceConfig)
{
if(dict.TryGetValue(deviceConfig.TerminalPhoneNo,out var client))
{
return client;
}
else
{
JT808TcpClient jT808TcpClient = new JT808TcpClient(deviceConfig, serviceProvider);
dict.TryAdd(deviceConfig.TerminalPhoneNo, jT808TcpClient);
return jT808TcpClient;
}
}

public void Dispose()
{
foreach(var client in dict)
{
try
{
client.Value.Dispose();
}
catch
{
}
}
}
}
}

+ 30
- 0
src/JT808.DotNetty.Client/Metadata/JT808ClientRequest.cs 查看文件

@@ -0,0 +1,30 @@
using JT808.Protocol;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace JT808.DotNetty.Client.Metadata
{
public class JT808ClientRequest
{
public JT808Package Package { get; }

public byte[] HexData { get; }

/// <summary>
/// 根据实际情况适当调整包的大小
/// </summary>
public int MinBufferSize { get;}

public JT808ClientRequest(JT808Package package,int minBufferSize=1024)
{
Package = package;
MinBufferSize = minBufferSize;
}

public JT808ClientRequest(byte[] hexData)
{
HexData = hexData;
}
}
}

+ 6
- 0
src/JT808.DotNetty.Core/Metadata/JT808Response.cs 查看文件

@@ -8,6 +8,7 @@ namespace JT808.DotNetty.Core.Metadata
public class JT808Response
{
public JT808Package Package { get; set; }
public byte[] HexData { get; set; }
/// <summary>
/// 根据实际情况适当调整包的大小
/// </summary>
@@ -23,5 +24,10 @@ namespace JT808.DotNetty.Core.Metadata
Package = package;
MinBufferSize = minBufferSize;
}

public JT808Response(byte[] hexData)
{
HexData = hexData;
}
}
}

+ 1
- 1
src/JT808.DotNetty.sln 查看文件

@@ -31,7 +31,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Hosting", "J
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.WebApiClientTool", "JT808.DotNetty.WebApiClientTool\JT808.DotNetty.WebApiClientTool.csproj", "{9D86C951-94F2-4CBD-B177-8AF31DDB05D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT808.DotNetty.Client", "JT808.DotNetty.Client\JT808.DotNetty.Client.csproj", "{87C08239-C57F-4FC5-9579-05D0723AA4A0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.DotNetty.Client", "JT808.DotNetty.Client\JT808.DotNetty.Client.csproj", "{87C08239-C57F-4FC5-9579-05D0723AA4A0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution


Loading…
取消
儲存