From c486772191e4124e1c1d80c47e158c35ea2ac0c2 Mon Sep 17 00:00:00 2001 From: H Vs Date: Tue, 27 Feb 2024 17:41:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 30 ++++ .gitignore | 9 ++ NearCardAttendance.Common/Class1.cs | 7 + NearCardAttendance.Common/Consts.cs | 26 ++++ .../NearCardAttendance.Common.csproj | 14 ++ .../helper/HttpHelper.cs | 128 ++++++++++++++++ NearCardAttendance.Model/Class1.cs | 7 + .../NearCardAttendance.Model.csproj | 9 ++ NearCardAttendance.Service/Class1.cs | 7 + .../NearCardAttendance.Service.csproj | 19 +++ .../TcpServer/Handler/ProtocolHandler.cs | 91 +++++++++++ .../TcpServer/Handler/RegisterHandler.cs | 142 ++++++++++++++++++ .../TcpServer/Mapper/ScheduleResendManager.cs | 21 +++ .../TcpServer/Mapper/TcpClientsManager.cs | 23 +++ .../TcpServer/Protocol/ProtocolParser.cs | 47 ++++++ .../TcpServer/Protocol/ProtocolWrapper.cs | 39 +++++ .../Config/ThreadInfoEnricher.cs | 31 ++++ NearCardAttendance.TcpServer/Dockerfile | 33 ++++ .../NearCardAttendance.TcpServer.csproj | 44 ++++++ NearCardAttendance.TcpServer/Program.cs | 112 ++++++++++++++ .../Properties/launchSettings.json | 10 ++ NearCardAttendance.TcpServer/Server.cs | 77 ++++++++++ .../appsettings.debug.json | 4 + NearCardAttendance.TcpServer/appsettings.json | 121 +++++++++++++++ .../appsettings.test.json | 3 + NearCardAttendance.sln | 43 ++++++ near_card_attendance_run.sh | 29 ++++ setup_test.sh | 17 +++ 28 files changed, 1143 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 NearCardAttendance.Common/Class1.cs create mode 100644 NearCardAttendance.Common/Consts.cs create mode 100644 NearCardAttendance.Common/NearCardAttendance.Common.csproj create mode 100644 NearCardAttendance.Common/helper/HttpHelper.cs create mode 100644 NearCardAttendance.Model/Class1.cs create mode 100644 NearCardAttendance.Model/NearCardAttendance.Model.csproj create mode 100644 NearCardAttendance.Service/Class1.cs create mode 100644 NearCardAttendance.Service/NearCardAttendance.Service.csproj create mode 100644 NearCardAttendance.Service/TcpServer/Handler/ProtocolHandler.cs create mode 100644 NearCardAttendance.Service/TcpServer/Handler/RegisterHandler.cs create mode 100644 NearCardAttendance.Service/TcpServer/Mapper/ScheduleResendManager.cs create mode 100644 NearCardAttendance.Service/TcpServer/Mapper/TcpClientsManager.cs create mode 100644 NearCardAttendance.Service/TcpServer/Protocol/ProtocolParser.cs create mode 100644 NearCardAttendance.Service/TcpServer/Protocol/ProtocolWrapper.cs create mode 100644 NearCardAttendance.TcpServer/Config/ThreadInfoEnricher.cs create mode 100644 NearCardAttendance.TcpServer/Dockerfile create mode 100644 NearCardAttendance.TcpServer/NearCardAttendance.TcpServer.csproj create mode 100644 NearCardAttendance.TcpServer/Program.cs create mode 100644 NearCardAttendance.TcpServer/Properties/launchSettings.json create mode 100644 NearCardAttendance.TcpServer/Server.cs create mode 100644 NearCardAttendance.TcpServer/appsettings.debug.json create mode 100644 NearCardAttendance.TcpServer/appsettings.json create mode 100644 NearCardAttendance.TcpServer/appsettings.test.json create mode 100644 NearCardAttendance.sln create mode 100644 near_card_attendance_run.sh create mode 100644 setup_test.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32092f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.user +*.suo +.vs/ +*.log +obj/ +bin/ +.idea/ +.vscode/ +publish/ \ No newline at end of file diff --git a/NearCardAttendance.Common/Class1.cs b/NearCardAttendance.Common/Class1.cs new file mode 100644 index 0000000..ad9b880 --- /dev/null +++ b/NearCardAttendance.Common/Class1.cs @@ -0,0 +1,7 @@ +namespace NearCardAttendance.Common +{ + public class Class1 + { + + } +} diff --git a/NearCardAttendance.Common/Consts.cs b/NearCardAttendance.Common/Consts.cs new file mode 100644 index 0000000..704c98a --- /dev/null +++ b/NearCardAttendance.Common/Consts.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.Common +{ + public class Consts + { + /// + /// HttpClient常用配置分组名称 + /// + public const string DEFAULT_HTTPCLIENT_NAME = "DEFAULT_HTTP"; + + /// + /// 纪元时间(UTC时间戳起始计算时间) + /// + public static DateTime EraUtcTime = DateTime.Parse("1970/01/01"); + + /// + /// 有效定位的半径阈值(大于该值则为无效定位) + /// + public static int RadiusThreshold = 150; + } +} diff --git a/NearCardAttendance.Common/NearCardAttendance.Common.csproj b/NearCardAttendance.Common/NearCardAttendance.Common.csproj new file mode 100644 index 0000000..1663be9 --- /dev/null +++ b/NearCardAttendance.Common/NearCardAttendance.Common.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/NearCardAttendance.Common/helper/HttpHelper.cs b/NearCardAttendance.Common/helper/HttpHelper.cs new file mode 100644 index 0000000..71ecc03 --- /dev/null +++ b/NearCardAttendance.Common/helper/HttpHelper.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Headers; +using System.Net.Security; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Microsoft.Extensions.Logging; + +namespace NearCardAttendance.Common.helper +{ + + public class HttpHelper + { + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + + public HttpHelper(IHttpClientFactory httpClientFactory, ILogger logger) + { + _httpClientFactory = httpClientFactory; + _logger = logger; + + ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult!); + } + + + public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) + { + return true; + } + + public async Task HttpToGetAsync(string url) + { + return await HttpToGetAsync(url, new List>()); + } + + public async Task HttpToGetAsync(string url, List> headers) + { + var client = _httpClientFactory.CreateClient(Consts.DEFAULT_HTTPCLIENT_NAME); + if (headers != null && headers.Count > 0) //指定请求头 + { + headers.ForEach(e => + { + client.DefaultRequestHeaders.Add(e.Key, e.Value); + }); + } + + try + { + using (var response = await client.GetAsync(url).ConfigureAwait(false)) + { + if (!response.IsSuccessStatusCode) return null; + else + { + var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return result; + } + } + } + catch (Exception ex) + { + _logger.LogError($"HttpToGetAsync发生异常, Msg:{ex.Message}\n{ex.StackTrace}"); + return null; + } + } + + public async Task HttpToPostAsync(string url, object data) + { + return await HttpToPostAsync(url, data, new List>()); + } + + public async Task HttpToPostAsync(string url, object data, List> headers) + { + if (data == null) return null; + + var client = _httpClientFactory.CreateClient(Consts.DEFAULT_HTTPCLIENT_NAME); + if (headers != null && headers.Count > 0) //指定请求头 + { + headers.ForEach(e => + { + client.DefaultRequestHeaders.Add(e.Key, e.Value); + }); + + } + + string? body; + if (data is string) body = data as string; + else body = JsonConvert.SerializeObject(data); + + try + { + using (var content = new StringContent(body!)) + { + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + using (var response = await client.PostAsync(url, content).ConfigureAwait(false)) + { + if (!response.IsSuccessStatusCode) return null; + else + { + var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return result; + } + } + } + } + catch (Exception ex) + { + _logger.LogError($"HttpToPostAsync发生异常, Msg:{ex.Message}\n{ex.StackTrace}"); + return null; + } + } + /// + /// Authorization 的 Base64 加密 + /// + /// + /// + /// + public string GetEncodedCredentials(string username, string password) + { + string mergedCredentials = string.Format("{0}:{1}", username, password); + byte[] byteCredentials = UTF8Encoding.UTF8.GetBytes(mergedCredentials); + return Convert.ToBase64String(byteCredentials); + } + } +} diff --git a/NearCardAttendance.Model/Class1.cs b/NearCardAttendance.Model/Class1.cs new file mode 100644 index 0000000..6511458 --- /dev/null +++ b/NearCardAttendance.Model/Class1.cs @@ -0,0 +1,7 @@ +namespace NearCardAttendance.Model +{ + public class Class1 + { + + } +} diff --git a/NearCardAttendance.Model/NearCardAttendance.Model.csproj b/NearCardAttendance.Model/NearCardAttendance.Model.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/NearCardAttendance.Model/NearCardAttendance.Model.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/NearCardAttendance.Service/Class1.cs b/NearCardAttendance.Service/Class1.cs new file mode 100644 index 0000000..82228fe --- /dev/null +++ b/NearCardAttendance.Service/Class1.cs @@ -0,0 +1,7 @@ +namespace NearCardAttendance.Service +{ + public class Class1 + { + + } +} diff --git a/NearCardAttendance.Service/NearCardAttendance.Service.csproj b/NearCardAttendance.Service/NearCardAttendance.Service.csproj new file mode 100644 index 0000000..e419948 --- /dev/null +++ b/NearCardAttendance.Service/NearCardAttendance.Service.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/NearCardAttendance.Service/TcpServer/Handler/ProtocolHandler.cs b/NearCardAttendance.Service/TcpServer/Handler/ProtocolHandler.cs new file mode 100644 index 0000000..cd11f03 --- /dev/null +++ b/NearCardAttendance.Service/TcpServer/Handler/ProtocolHandler.cs @@ -0,0 +1,91 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using Microsoft.Extensions.Logging; +using NearCardAttendance.Service.TcpServer.Protocol; +using System; +using System.Text; + +namespace NearCardAttendance.Service.TcpServer.Handler +{ + public class ProtocolHandler : SimpleChannelInboundHandler + { + private IByteBuffer? buffer; + private readonly ILogger _logger; + + public ProtocolHandler(ILogger logger) + { + _logger = logger; + } + + public override async void ChannelActive(IChannelHandlerContext context) + { + // Handle channel active event + } + + public override void ChannelInactive(IChannelHandlerContext context) + { + ReleaseBuffer(); + base.ChannelInactive(context); + } + + protected override void ChannelRead0(IChannelHandlerContext context, IByteBuffer message) + { + try + { + string content = message.ToString(Encoding.ASCII); + _logger.LogInformation($"{nameof(ProtocolHandler)} -- {nameof(ChannelRead0)} -- 最开始接受内容:{content}"); + + ProcessMessage(context, content); + } + catch (Exception ex) + { + ReleaseBuffer(); + _logger.LogInformation($"{nameof(ProtocolHandler)} --- {nameof(ChannelRead0)} 处理出错\n{ex.Message}\n{ex.StackTrace}"); + } + } + + private void ProcessMessage(IChannelHandlerContext context, string content) + { + buffer ??= Unpooled.Buffer(); + + byte[] bytes = Encoding.ASCII.GetBytes(content); + buffer.WriteBytes(Unpooled.WrappedBuffer(bytes)); + + if (int.TryParse(buffer.ToString(Encoding.ASCII).Substring(0, 4), out int messageLength)) + { + Console.WriteLine(buffer.ToString(Encoding.ASCII)); + if (buffer.ToString(Encoding.ASCII).Length == messageLength) + { + //var parser = buffer.ToString(Encoding.ASCII); + ProtocolParser parser = new(buffer.ToString(Encoding.ASCII)); + context.FireChannelRead(parser); + Console.WriteLine($"发送正常:{parser}"); + //ProtocolWrapper wrapper = new ProtocolWrapper("82", parser.SeqNo, DateTime.Now.ToString("yyyyMMddHHmmss")); + //Console.WriteLine(wrapper.GenerateProtocolString()); + Console.WriteLine($"length:{parser.MessageLength},func_no:{parser.FuncNo},seq_no:{parser.SeqNo},data:{ parser.Data}"); + ReleaseBuffer(); + } + else if (buffer.ToString(Encoding.ASCII).Length > messageLength) + { + // var parser = buffer.ToString(Encoding.ASCII).Substring(0, messageLength); + ProtocolParser parser = new(buffer.ToString(Encoding.ASCII).Substring(0, messageLength)); + context.FireChannelRead(parser); + //ReleaseBuffer(); + + var overLongbuffer = Unpooled.Buffer(); + Console.WriteLine($"过长消息:{buffer.ToString(Encoding.ASCII).Substring(messageLength)}"); + overLongbuffer.WriteBytes(Unpooled.WrappedBuffer(Encoding.ASCII.GetBytes(buffer.ToString(Encoding.ASCII).Substring(messageLength)))); + ReleaseBuffer(); + buffer = overLongbuffer; + Console.WriteLine($"剩余消息{buffer.ToString(Encoding.ASCII)}"); + } + } + } + + private void ReleaseBuffer() + { + buffer?.Release(); + buffer = null; + } + } +} diff --git a/NearCardAttendance.Service/TcpServer/Handler/RegisterHandler.cs b/NearCardAttendance.Service/TcpServer/Handler/RegisterHandler.cs new file mode 100644 index 0000000..690a0ea --- /dev/null +++ b/NearCardAttendance.Service/TcpServer/Handler/RegisterHandler.cs @@ -0,0 +1,142 @@ +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using Microsoft.Extensions.Logging; +using NearCardAttendance.Common.helper; +using NearCardAttendance.Service.TcpServer.Mapper; +using NearCardAttendance.Service.TcpServer.Protocol; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.Service.TcpServer.Handler +{ + public class RegisterHandler : ChannelHandlerAdapter + { + private readonly ILogger _logger; + private readonly HttpHelper _httpHelper = default!; + private readonly IDisposable _loggerScope = default!; + + + private readonly TcpClientsManager _managerTcpClients; + private readonly ScheduleResendManager _managerScheduleResend; + + public RegisterHandler(ILogger logger,HttpHelper httpHelper) + { + _logger = logger; + _httpHelper = httpHelper; + } + + public override void ChannelActive(IChannelHandlerContext context) + { + base.ChannelActive(context); // Pass the event to the next handler + } + + public override async void ChannelInactive(IChannelHandlerContext context) + { + try + { + + } + catch (Exception ex) + { + _logger.LogInformation($"{nameof(RegisterHandler)} --- {nameof(ChannelInactive)} 出错\n{ex.Message}\n{ex.StackTrace}"); + } + } + + public override void HandlerRemoved(IChannelHandlerContext context) + { + base.HandlerRemoved(context); + } + + public override async void ChannelRead(IChannelHandlerContext context, object message) + { + if (message is ProtocolParser parser) + { + try + { + using (_logger.BeginScope(new Dictionary { ["RequestId"] = parser.SeqNo })) + { + //认证 + if (parser.FuncNo.Equals("10")) + { + #region 认证业务 + // PHONE_AUTHEN + ProtocolWrapper phoneAuthWrapper = new(parser.FuncNo, parser.SeqNo, "1"); + await SendToTcpClientAsync(phoneAuthWrapper, context.Channel); + + // ABT_STATUS 延时3秒 给终端同步时间 + await context.Channel.EventLoop.Schedule(async () => + { + ProtocolWrapper abtStatusWrapper = new("82", parser.SeqNo, DateTime.Now.ToString("yyyyMMddHHmmss")); + await SendToTcpClientAsync(abtStatusWrapper, context.Channel); + },TimeSpan.FromSeconds(3)); + + #endregion + } + else if (parser.FuncNo.Equals("82")) + { + // 认证成功 + _logger.LogInformation($"认证成功{parser.Data}."); + } + // 心跳 + else if (parser.FuncNo.Equals("05")) + { + ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo,""); + await SendToTcpClientAsync(stdtSignRecsWrapper, context.Channel); + } // STDT_SIGN_RECS + else if (parser.FuncNo.Equals("04")) + { + // 回应设备 + ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo,"1"); + await SendToTcpClientAsync(stdtSignRecsWrapper, context.Channel); + // 刷卡考勤信息,需要推送给第三方平台 + + //var url = ""; + + //await _httpHelper.HttpToPostAsync(url, new object()); + } + + + //switch (parser.FuncNo) + //{ + // case "10": + // break; + + // case "82": + // break; + // default: + // break; + //} + } + } + catch (Exception ex) + { + _logger.LogInformation($"{nameof(RegisterHandler)} --- {nameof(ChannelRead)} 处理消息 {parser.SeqNo} 出错\n{ex.Message}\n{ex.StackTrace}"); + + } + + } + } + + /// + /// 发送到TCP客户端 + /// + /// + /// + private async Task SendToTcpClientAsync(ProtocolWrapper wrapper, IChannel channel) + { + + string protocolMsg = wrapper.GenerateProtocolString(); + + // 发送protocolMsg到tcp客户端 + await channel + .WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(protocolMsg))) + .ContinueWith(Action => { + _logger.LogInformation($"{nameof(RegisterHandler)} -- {nameof(SendToTcpClientAsync)} -- 下发设备内容:\n{protocolMsg}"); + }); + } + + } +} diff --git a/NearCardAttendance.Service/TcpServer/Mapper/ScheduleResendManager.cs b/NearCardAttendance.Service/TcpServer/Mapper/ScheduleResendManager.cs new file mode 100644 index 0000000..3d198ae --- /dev/null +++ b/NearCardAttendance.Service/TcpServer/Mapper/ScheduleResendManager.cs @@ -0,0 +1,21 @@ +using DotNetty.Common.Concurrency; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.Service.TcpServer.Mapper +{ + public class ScheduleResendManager : ConcurrentDictionary + { + private readonly ILogger _logger; + + public ScheduleResendManager(ILogger logger) + { + _logger = logger; + } + } +} diff --git a/NearCardAttendance.Service/TcpServer/Mapper/TcpClientsManager.cs b/NearCardAttendance.Service/TcpServer/Mapper/TcpClientsManager.cs new file mode 100644 index 0000000..a6f8d0c --- /dev/null +++ b/NearCardAttendance.Service/TcpServer/Mapper/TcpClientsManager.cs @@ -0,0 +1,23 @@ +using DotNetty.Transport.Channels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.Service.TcpServer.Mapper +{ + public class TcpClientsManager : ConcurrentDictionary + { + private readonly ILogger _logger; + + public TcpClientsManager(ILogger logger) + { + _logger = logger; + } + + + } +} diff --git a/NearCardAttendance.Service/TcpServer/Protocol/ProtocolParser.cs b/NearCardAttendance.Service/TcpServer/Protocol/ProtocolParser.cs new file mode 100644 index 0000000..3d37675 --- /dev/null +++ b/NearCardAttendance.Service/TcpServer/Protocol/ProtocolParser.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.Service.TcpServer.Protocol +{ + + public class ProtocolParser + { + public int MessageLength { get; private set; } = default!; + + public string FuncNo { get; private set; } = default!; + + public string SeqNo { get; private set; } = default!; + + public string Data { get; private set; } = default!; + //private string _protocolString { get; set; } = default!; + public ProtocolParser(string protocolString) + { + ParseProtocolString(protocolString); + //_protocolString = protocolString; + } + + private void ParseProtocolString(string protocolString) + { + try + { + _ = int.TryParse(protocolString.AsSpan(0, 4), out int messageLength); + MessageLength = messageLength; + FuncNo = protocolString.Substring(4,2); + SeqNo = protocolString.Substring(6,4); + Data = protocolString.Substring(10).TrimEnd(); + } + catch (Exception e) + { + + Console.WriteLine($"Error: {e.Message}"); + // throw new ArgumentException("Invalid protocol string format."); + } + } + } + + + +} diff --git a/NearCardAttendance.Service/TcpServer/Protocol/ProtocolWrapper.cs b/NearCardAttendance.Service/TcpServer/Protocol/ProtocolWrapper.cs new file mode 100644 index 0000000..d9e3628 --- /dev/null +++ b/NearCardAttendance.Service/TcpServer/Protocol/ProtocolWrapper.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.Service.TcpServer.Protocol +{ + public class ProtocolWrapper + { + private string Length { get; } + + private string FuncNo { get; } + private string SeqNo { get; } + + private string Data { get; } + + + public ProtocolWrapper(string funcNo,string seqNo,string data) + { + + FuncNo =funcNo; + SeqNo=seqNo; + Data=data; + Length = CalculateMessageLength(); + } + + private string CalculateMessageLength() + { + return string.Format("{0:D4}", GenerateProtocolString().Length + 4); + } + + public string GenerateProtocolString() + { + return $"{Length}{FuncNo}{SeqNo}{Data}"; + } + } + +} diff --git a/NearCardAttendance.TcpServer/Config/ThreadInfoEnricher.cs b/NearCardAttendance.TcpServer/Config/ThreadInfoEnricher.cs new file mode 100644 index 0000000..1a96922 --- /dev/null +++ b/NearCardAttendance.TcpServer/Config/ThreadInfoEnricher.cs @@ -0,0 +1,31 @@ +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.TcpServer.Config +{ + public class ThreadInfoEnricher : ILogEventEnricher + { + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadId", Thread.CurrentThread.ManagedThreadId)); + } + } + + public static class EnricherExtensions + { + public static LoggerConfiguration WithThreadInfo(this LoggerEnrichmentConfiguration enrich) + { + if (enrich == null) + throw new ArgumentNullException(nameof(enrich)); + return enrich.With(); + } + } +} diff --git a/NearCardAttendance.TcpServer/Dockerfile b/NearCardAttendance.TcpServer/Dockerfile new file mode 100644 index 0000000..c3a2433 --- /dev/null +++ b/NearCardAttendance.TcpServer/Dockerfile @@ -0,0 +1,33 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +#FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 16662 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["NearCardAttendance.TcpServer/NearCardAttendance.TcpServer.csproj", "NearCardAttendance.TcpServer/"] +COPY ["NearCardAttendance.Common/NearCardAttendance.Common.csproj", "NearCardAttendance.Common/"] +COPY ["NearCardAttendance.Service/NearCardAttendance.Service.csproj", "NearCardAttendance.Service/"] +RUN dotnet restore "./NearCardAttendance.TcpServer/./NearCardAttendance.TcpServer.csproj" +COPY . . +WORKDIR "/src/NearCardAttendance.TcpServer" +RUN dotnet build "./NearCardAttendance.TcpServer.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./NearCardAttendance.TcpServer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . + +ENV environment=Development +ENV TimeZone=Asia/Shanghai +ENV LANG C.UTF-8 +RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone + +#ENTRYPOINT ["dotnet", "NearCardAttendance.TcpServer.dll"] +ENTRYPOINT ["sh", "-c", "dotnet NearCardAttendance.TcpServer.dll --environment=$environment"] \ No newline at end of file diff --git a/NearCardAttendance.TcpServer/NearCardAttendance.TcpServer.csproj b/NearCardAttendance.TcpServer/NearCardAttendance.TcpServer.csproj new file mode 100644 index 0000000..b258ee0 --- /dev/null +++ b/NearCardAttendance.TcpServer/NearCardAttendance.TcpServer.csproj @@ -0,0 +1,44 @@ + + + + Exe + net6.0 + enable + enable + Linux + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + + + + + + diff --git a/NearCardAttendance.TcpServer/Program.cs b/NearCardAttendance.TcpServer/Program.cs new file mode 100644 index 0000000..e308bf8 --- /dev/null +++ b/NearCardAttendance.TcpServer/Program.cs @@ -0,0 +1,112 @@ +using DotNetty.Codecs; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NearCardAttendance.Common; +using NearCardAttendance.Common.helper; +using NearCardAttendance.Service.TcpServer.Handler; +using NearCardAttendance.Service.TcpServer.Mapper; +using NearCardAttendance.TcpServer.Config; +using Serilog; +using System.Text; + +namespace NearCardAttendance.TcpServer +{ + internal class Program + { + static void Main(string[] args) + { + var config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .Build(); + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(config).Enrich.WithThreadInfo() + .Filter.ByExcluding(logEvent => logEvent.Level == Serilog.Events.LogEventLevel.Verbose) // 过滤掉VRB级别的日志 + .CreateLogger(); + try + { + Log.Information("Starting up"); + + CreateHostBuilder(args).Build().Run(); + + + + } + catch (Exception ex) + { + Log.Fatal(ex, "Application start-up failed"); + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog() + .ConfigureServices((hostContext, services) => { + var configuration = hostContext.Configuration; + + #region 配置信息 + //services + // .Configure(configuration.GetSection("ServiceConfig")) + // ; + #endregion + + #region Http请求 + services + .AddSingleton() + .AddHttpClient(Consts.DEFAULT_HTTPCLIENT_NAME, c => + { + c.Timeout = TimeSpan.FromSeconds(10); //超时限制 + c.DefaultRequestHeaders.Add("Accept", "application/json"); + }) + ; + #endregion + + #region TcpService + services + .AddSingleton(provider => + { + var bossGroup = new MultithreadEventLoopGroup(); + var workerGroup = new MultithreadEventLoopGroup(); + + var bootstrap = new ServerBootstrap(); + bootstrap.Group(bossGroup, workerGroup) + .Channel() + .Option(ChannelOption.SoBacklog, 100) + .ChildOption(ChannelOption.TcpNodelay, true) // 低延迟 + .ChildHandler(new ActionChannelInitializer(channel => + { + // var handler = provider.GetRequiredService(); + // var handler = provider.GetRequiredService(); + var pipeline = channel.Pipeline; + pipeline.AddLast(new StringEncoder(Encoding.ASCII)); + pipeline.AddLast( + provider.GetRequiredService(), + provider.GetRequiredService() + //provider.GetRequiredService(), + //provider.GetRequiredService() + ); // Add the injected handler + })); + return bootstrap; + }) + .AddSingleton() + .AddSingleton() + .AddTransient() + .AddTransient() + //.AddTransient() + //.AddTransient() + .AddHostedService() + ; + #endregion + + + }); + } +} diff --git a/NearCardAttendance.TcpServer/Properties/launchSettings.json b/NearCardAttendance.TcpServer/Properties/launchSettings.json new file mode 100644 index 0000000..a8fe532 --- /dev/null +++ b/NearCardAttendance.TcpServer/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "NearCardAttendance.TcpServer": { + "commandName": "Project", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "debug" + } + } + } +} \ No newline at end of file diff --git a/NearCardAttendance.TcpServer/Server.cs b/NearCardAttendance.TcpServer/Server.cs new file mode 100644 index 0000000..62731ee --- /dev/null +++ b/NearCardAttendance.TcpServer/Server.cs @@ -0,0 +1,77 @@ +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace NearCardAttendance.TcpServer +{ + public class Server : BackgroundService + { + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly ServerBootstrap _serverBootstrap; + private IChannel _serverChannel = default!; + private CancellationTokenSource _tokenSource = null!; + + + public Server( + ILogger logger, + ServerBootstrap serverBootstrap, + IServiceProvider serviceProvider) + { + _logger = logger; + _serviceProvider = serviceProvider; + _serverBootstrap = serverBootstrap; + + } + public override Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("------StartAsync"); + _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + return base.StartAsync(cancellationToken); + } + + public override Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("------StopAsync"); + _tokenSource.Cancel(); //停止工作线程 + return base.StopAsync(cancellationToken); + } + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + + _logger.LogInformation("DotNetty server starting..."); + + //var address = new IPEndPoint(IPAddress.Any, 12345); + + //IChannel _serverChannel = await _serverBootstrap.BindAsync(address); + + string ipAddress = "0.0.0.0"; + int port = 16662; + var endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); + IChannel _serverChannel = await _serverBootstrap.BindAsync(endPoint); + // _serverChannel.GetAttribute(MessageIdAttribute.Key).Set("SomeRequestId"); + + //_logger.LogInformation("DotNetty server started on {0}.", address); + _logger.LogInformation("DotNetty server started on {0}.", endPoint); + + + // Wait until the service is stopped + stoppingToken.WaitHandle.WaitOne(); + + _logger.LogInformation("DotNetty server stopping..."); + + // Close the server channel and release resources + await _serverChannel.CloseAsync(); + _logger.LogInformation("DotNetty server stopped."); + } + + + } +} diff --git a/NearCardAttendance.TcpServer/appsettings.debug.json b/NearCardAttendance.TcpServer/appsettings.debug.json new file mode 100644 index 0000000..4f5035a --- /dev/null +++ b/NearCardAttendance.TcpServer/appsettings.debug.json @@ -0,0 +1,4 @@ +{ + "AllowedHosts": "*" + +} diff --git a/NearCardAttendance.TcpServer/appsettings.json b/NearCardAttendance.TcpServer/appsettings.json new file mode 100644 index 0000000..988b142 --- /dev/null +++ b/NearCardAttendance.TcpServer/appsettings.json @@ -0,0 +1,121 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "HttpClient": "Information" + } + }, + + "Serilog": { + "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Async", "Serilog.Sinks.Console", "Serilog.Expressions" ], + "MinimumLevel": { + "Default": "Verbose", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "HttpClient": "Information" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "restrictedToMinimumLevel": "Verbose", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff }[{Level:u3}] [Thread-{ThreadId}] [{SourceContext:l}] [{RequestId}] {Message:lj}{NewLine}{Exception}", + "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console" + } + }, + { + "Name": "Logger", + "Args": { + "ConfigureLogger": { + "WriteTo": [ + { + "Name": "File", + "Args": { + "RestrictedToMinimumLevel": "Information", + "RollingInterval": "Day", + "RollOnFileSizeLimit": "true", + "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff }[{Level:u3}] [Thread-{ThreadId}] [{SourceContext:l}] [{RequestId}] {Message:lj}{NewLine}{Exception}", + "Path": "/var/near_card_attendance/logs/infos/info.log", + "RetainedFileCountLimit": 10 // "--设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件", "等于null时永远保留文件": null + //"FileSizeLimitBytes": 20971520, //设置单个文件大小为3M 默认1G + //"RollOnFileSizeLimit": true //超过文件大小后创建新的 + } + } + ], + "Filter": [ + { + "Name": "ByIncludingOnly", + "Args": { + "Expression": "@l = 'Information'" + } + } + ] + } + } + }, + { + "Name": "Logger", + "Args": { + "ConfigureLogger": { + "WriteTo": [ + { + "Name": "File", + "Args": { + "RestrictedToMinimumLevel": "Warning", + "RollingInterval": "Day", + "RollOnFileSizeLimit": "true", + "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff }[{Level:u3}] [Thread-{ThreadId}] [{SourceContext:l}] [{RequestId}] {Message:lj}{NewLine}", + "Path": "/var/near_card_attendance/logs/warnings/warn.log", + "RetainedFileCountLimit": 10 + } + } + ], + "Filter": [ + { + "Name": "ByIncludingOnly", + "Args": { + "Expression": "@l = 'Warning'" + } + } + ] + } + } + }, + { + "Name": "Logger", + "Args": { + "ConfigureLogger": { + "WriteTo": [ + { + "Name": "File", + "Args": { + "RestrictedToMinimumLevel": "Error", + "RollingInterval": "Day", + "RollOnFileSizeLimit": "true", + "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff }[{Level:u3}] [Thread-{ThreadId}][{SourceContext:l}] [{RequestId}] {Message:lj}{NewLine}{Exception}", + "Path": "/var/near_card_attendance/logs/errors/error.log", + "RetainedFileCountLimit": 15 // "--设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件", "等于null时永远保留文件": null + //"FileSizeLimitBytes": 20971520, //设置单个文件大小为3M 默认1G + //"RollOnFileSizeLimit": true //超过文件大小后创建新的 + + } + } + ], + "Filter": [ + { + "Name": "ByIncludingOnly", + "Args": { + "Expression": "@l = 'Error'" + } + } + ] + } + } + } + ] + } +} diff --git a/NearCardAttendance.TcpServer/appsettings.test.json b/NearCardAttendance.TcpServer/appsettings.test.json new file mode 100644 index 0000000..a2b524b --- /dev/null +++ b/NearCardAttendance.TcpServer/appsettings.test.json @@ -0,0 +1,3 @@ +{ + "AllowedHosts": "*" +} diff --git a/NearCardAttendance.sln b/NearCardAttendance.sln new file mode 100644 index 0000000..c754be8 --- /dev/null +++ b/NearCardAttendance.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NearCardAttendance.TcpServer", "NearCardAttendance.TcpServer\NearCardAttendance.TcpServer.csproj", "{46A2FEAF-7333-4D41-83F2-5ACDDDD850F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NearCardAttendance.Service", "NearCardAttendance.Service\NearCardAttendance.Service.csproj", "{2394B5EA-8A6A-4FC4-9218-2EED87E3FC14}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NearCardAttendance.Common", "NearCardAttendance.Common\NearCardAttendance.Common.csproj", "{CC1A7D30-569C-497A-8D94-C23BA11C870D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NearCardAttendance.Model", "NearCardAttendance.Model\NearCardAttendance.Model.csproj", "{10C81D50-6839-4F3C-92C9-C4E6554B8566}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {46A2FEAF-7333-4D41-83F2-5ACDDDD850F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46A2FEAF-7333-4D41-83F2-5ACDDDD850F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46A2FEAF-7333-4D41-83F2-5ACDDDD850F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46A2FEAF-7333-4D41-83F2-5ACDDDD850F5}.Release|Any CPU.Build.0 = Release|Any CPU + {2394B5EA-8A6A-4FC4-9218-2EED87E3FC14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2394B5EA-8A6A-4FC4-9218-2EED87E3FC14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2394B5EA-8A6A-4FC4-9218-2EED87E3FC14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2394B5EA-8A6A-4FC4-9218-2EED87E3FC14}.Release|Any CPU.Build.0 = Release|Any CPU + {CC1A7D30-569C-497A-8D94-C23BA11C870D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC1A7D30-569C-497A-8D94-C23BA11C870D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC1A7D30-569C-497A-8D94-C23BA11C870D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC1A7D30-569C-497A-8D94-C23BA11C870D}.Release|Any CPU.Build.0 = Release|Any CPU + {10C81D50-6839-4F3C-92C9-C4E6554B8566}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10C81D50-6839-4F3C-92C9-C4E6554B8566}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10C81D50-6839-4F3C-92C9-C4E6554B8566}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10C81D50-6839-4F3C-92C9-C4E6554B8566}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2A15CF2F-2E7D-42D5-B8D7-9A22592049A0} + EndGlobalSection +EndGlobal diff --git a/near_card_attendance_run.sh b/near_card_attendance_run.sh new file mode 100644 index 0000000..00897df --- /dev/null +++ b/near_card_attendance_run.sh @@ -0,0 +1,29 @@ +#!/bin/bash +environment=$1 +version=$2 +echo "环境变量为${environment},版本为$version!" +if [[ ${environment} == 'production' ]]; then + echo "开始远程构建容器" + docker stop near_card_attendance || true + docker rm near_card_attendance || true + docker rmi -f $(docker images | grep registry.cn-shanghai.aliyuncs.com/gps_card/near_card_attendance | awk '{print $3}') + #docker login --username=telpo_linwl@1111649216405698 --password=telpo#1234 registry.cn-shanghai.aliyuncs.com + docker login --username=rzl_wangjx@1111649216405698 --password=telpo.123 registry.cn-shanghai.aliyuncs.com + docker pull registry.cn-shanghai.aliyuncs.com/gps_card/near_card_attendance:$version + docker run --network=host -p 16662:16662 -d -e environment=production -v /home/data/near_card_attendance/log:/var/near_card_attendance/logs --restart=always --name near_card_attendance registry.cn-shanghai.aliyuncs.com/gps_card/near_card_attendance:$version; + #删除产生的None镜像 + docker rmi -f $(docker images | grep none | awk '{print $3}') + docker ps -a + +elif [[ ${environment} == 'test' || ${environment} == 'presure' ]]; then + echo "开始在测试环境远程构建容器" + docker stop near_card_attendance || true + docker rm near_card_attendance || true + docker rmi -f $(docker images | grep 139.224.254.18:5000/near_card_attendance | awk '{print $3}') + docker pull 139.224.254.18:5000/near_card_attendance:$version + docker run --network=host -d -p 16662:16662 -e environment=${environment} -v /home/data/near_card_attendance/log:/var/near_card_attendance/logs --restart=always --name near_card_attendance 139.224.254.18:5000/near_card_attendance:$version; + #删除产生的None镜像 + docker rmi -f $(docker images | grep none | awk '{print $3}') + docker ps -a + +fi \ No newline at end of file diff --git a/setup_test.sh b/setup_test.sh new file mode 100644 index 0000000..195b660 --- /dev/null +++ b/setup_test.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +image_version=$version +# 删除镜像 +docker rmi -f $( + docker images | grep 139.224.254.18:5000/near_card_attendance | awk '{print $3}' +) +# 构建telpo/mrp:$image_version镜像 +docker build -f ./NearCardAttendance.TcpServer/Dockerfile . -t telpo/near_card_attendance:$image_version +#TODO:推送镜像到私有仓库 +echo '=================开始推送镜像=======================' +docker tag telpo/near_card_attendance:$image_version 139.224.254.18:5000/near_card_attendance:$image_version +docker push 139.224.254.18:5000/near_card_attendance:$image_version +echo '=================推送镜像完成=======================' +#删除产生的None镜像 +docker rmi -f $(docker images | grep none | awk '{print $3}') +# 查看镜像列表 +docker images \ No newline at end of file