@@ -25,7 +25,7 @@ | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="JT1078" Version="1.0.3" /> | |||
<PackageReference Include="JT1078" Version="1.0.4-preview1" /> | |||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.7" /> | |||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.7" /> | |||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.7" /> | |||
@@ -0,0 +1,37 @@ | |||
<?xml version="1.0" encoding="utf-8" ?> | |||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
autoReload="true" | |||
internalLogFile="/data/serviceslogs/JT1078.Gateway.TestNormalHosting/internalLog.txt" | |||
internalLogLevel="Debug"> | |||
<variable name="Directory" value="/data/serviceslogs/JT1078.Gateway.TestNormalHosting"/> | |||
<targets> | |||
<target name="all" xsi:type="File" | |||
fileName="${Directory}/all.${shortdate}.log" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||
<target name="JT1078FlvNormalMsgHostedService" xsi:type="File" | |||
fileName="${Directory}/JT1078FlvNormalMsgHostedService.${shortdate}.log" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||
<target name="JT1078TcpServer" xsi:type="File" | |||
fileName="${Directory}/JT1078TcpServer.${shortdate}.log" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||
<target name="JT1078Logging" xsi:type="File" | |||
fileName="${Directory}/JT1078Logging.${shortdate}.log" | |||
layout="${message}"/> | |||
<target name="console" xsi:type="ColoredConsole" | |||
useDefaultRowHighlightingRules="false" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level} ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"> | |||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> | |||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> | |||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> | |||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> | |||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> | |||
</target> | |||
</targets> | |||
<rules> | |||
<logger name="*" minlevel="Trace" writeTo="all"/> | |||
<logger name="JT1078.Gateway.TestNormalHosting.Services.JT1078FlvNormalMsgHostedService" minlevel="Trace" writeTo="JT1078FlvNormalMsgHostedService"/> | |||
<logger name="JT1078.Gateway.JT1078TcpServer" minlevel="Trace" writeTo="JT1078TcpServer"/> | |||
<logger name="JT1078Logging" minlevel="Trace" writeTo="JT1078Logging"/> | |||
</rules> | |||
</nlog> |
@@ -0,0 +1,44 @@ | |||
<?xml version="1.0" encoding="utf-8" ?> | |||
<!-- | |||
参考:http://www.cnblogs.com/fuchongjundream/p/3936431.html | |||
autoReload:自动再配置 | |||
internalLogFile:可以让NLog把内部的调试和异常信息都写入指定文件里程序没问题了,日志却出了问题。这个该怎么办,到底是哪里不正确了?假如日志本身除了bug该如何解决?这就需要日志排错。把日志的错误信息写入日志。 | |||
<nlog throwExceptions="true" /> | |||
<nlog internalLogFile="file.txt" />- 设置internalLogFile属性可以让NLog把内部的调试和异常信息都写入指定文件里。 | |||
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" /> - 决定内部日志的级别,级别越高,输出的日志信息越简洁。 | |||
<nlog internalLogToConsole="false|true" /> - 是否把内部日志输出到标准控制台。 | |||
<nlog internalLogToConsoleError="false|true" /> - 是否把内部日志输出到标准错误控制台 (stderr)。 | |||
设置throwExceptions属性为“true”可以让NLog不再阻挡这类异常,而是把它们抛给调用者。在部署是这样做可以帮我们快速定位问题。一旦应用程序已经正确配置了,我们建议把throwExceptions的值设为“false”,这样由于日志引发的问题不至于导致应用程序的崩溃。 | |||
--> | |||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
autoReload="true" | |||
internalLogFile="/wwwroot/logs/JT1078.Gateway.TestNormalHosting/internalLog.txt" | |||
internalLogLevel="Debug" > | |||
<variable name="Directory" value="/wwwroot/logs/JT1078.Gateway.TestNormalHosting"/> | |||
<targets> | |||
<target name="all" xsi:type="File" | |||
fileName="${Directory}/all.${shortdate}.log" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||
<target name="JT1078TcpServer" xsi:type="File" | |||
fileName="${Directory}/JT1078TcpServer.${shortdate}.log" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level}:${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/> | |||
<target name="JT1078Logging" xsi:type="File" | |||
fileName="${Directory}/JT1078Logging.${shortdate}.log" | |||
layout="${message}"/> | |||
<target name="console" xsi:type="ColoredConsole" | |||
useDefaultRowHighlightingRules="false" | |||
layout="${date:format=yyyyMMddHHmmss} ${callsite} ${level} ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"> | |||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> | |||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> | |||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> | |||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> | |||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> | |||
</target> | |||
</targets> | |||
<rules> | |||
<logger name="*" minlevel="Trace" writeTo="all,console"/> | |||
<logger name="JT1078.Gateway.JT1078TcpServer" minlevel="Trace" writeTo="JT1078TcpServer,console"/> | |||
<logger name="JT1078Logging" minlevel="Trace" writeTo="JT1078Logging,console"/> | |||
</rules> | |||
</nlog> |
@@ -1,4 +1,4 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<OutputType>Exe</OutputType> | |||
@@ -6,12 +6,13 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="JT1078.Flv" Version="1.0.0-preview8" /> | |||
<PackageReference Include="JT1078.Flv" Version="1.0.0-preview11" /> | |||
<PackageReference Include="JT1078.Hls" Version="1.0.0-preview2" /> | |||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" /> | |||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" /> | |||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.7" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.7" /> | |||
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.4" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
@@ -23,6 +24,15 @@ | |||
<None Update="appsettings.json"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Update="Configs\nlog.Unix.config"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Update="Configs\nlog.Win32NT.config"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Update="Configs\NLog.xsd"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Update="wwwroot\demo\demo.m3u8"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Hosting; | |||
using Microsoft.Extensions.Logging; | |||
using NLog.Extensions.Logging; | |||
using System; | |||
using System.Threading.Tasks; | |||
@@ -24,8 +25,10 @@ namespace JT1078.Gateway.TestNormalHosting | |||
}) | |||
.ConfigureLogging((context, logging) => | |||
{ | |||
logging.AddConsole(); | |||
logging.SetMinimumLevel(LogLevel.Trace); | |||
Console.WriteLine($"Environment.OSVersion.Platform:{Environment.OSVersion.Platform.ToString()}"); | |||
NLog.LogManager.LoadConfiguration($"Configs/nlog.{Environment.OSVersion.Platform.ToString()}.config"); | |||
logging.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); | |||
}) | |||
.ConfigureServices((hostContext, services) => | |||
{ | |||
@@ -8,6 +8,12 @@ using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using System.Linq; | |||
using Microsoft.Extensions.Logging; | |||
using JT1078.Flv.Extensions; | |||
using Microsoft.Extensions.Caching.Memory; | |||
using JT1078.Protocol; | |||
using System.Text.Json; | |||
using System.Text.Json.Serialization; | |||
namespace JT1078.Gateway.TestNormalHosting.Services | |||
{ | |||
@@ -16,36 +22,84 @@ namespace JT1078.Gateway.TestNormalHosting.Services | |||
private IJT1078PackageConsumer PackageConsumer; | |||
private JT1078HttpSessionManager HttpSessionManager; | |||
private FlvEncoder FlvEncoder; | |||
private ILogger Logger; | |||
private IMemoryCache memoryCache; | |||
private const string ikey = "IKEY"; | |||
public JT1078FlvNormalMsgHostedService( | |||
IMemoryCache memoryCache, | |||
ILoggerFactory loggerFactory, | |||
FlvEncoder flvEncoder, | |||
JT1078HttpSessionManager httpSessionManager, | |||
IJT1078PackageConsumer packageConsumer) | |||
{ | |||
Logger = loggerFactory.CreateLogger<JT1078FlvNormalMsgHostedService>(); | |||
PackageConsumer = packageConsumer; | |||
HttpSessionManager = httpSessionManager; | |||
FlvEncoder = flvEncoder; | |||
this.memoryCache = memoryCache; | |||
} | |||
protected override Task ExecuteAsync(CancellationToken stoppingToken) | |||
{ | |||
PackageConsumer.OnMessage((Message) => | |||
{ | |||
var merge = JT1078.Protocol.JT1078Serializer.Merge(Message.Data); | |||
if (merge != null) | |||
try | |||
{ | |||
var httpSessions = HttpSessionManager.GetAllBySimAndChannelNo(Message.Data.SIM, Message.Data.LogicChannelNumber); | |||
var firstHttpSessions = httpSessions.Where(w => !w.FirstSend).ToList(); | |||
if (firstHttpSessions.Count > 0) | |||
if (Logger.IsEnabled(LogLevel.Debug)) | |||
{ | |||
var flvVideoBuffer = FlvEncoder.EncoderVideoTag(merge, true); | |||
HttpSessionManager.SendAVData(firstHttpSessions, flvVideoBuffer, true); | |||
Logger.LogDebug(JsonSerializer.Serialize(HttpSessionManager.GetAll())); | |||
Logger.LogDebug($"{Message.Data.SIM},{Message.Data.SN},{Message.Data.LogicChannelNumber},{Message.Data.Label3.DataType.ToString()},{Message.Data.Label3.SubpackageType.ToString()},{Message.Data.Bodies.ToHexString()}"); | |||
} | |||
var otherHttpSessions = httpSessions.Where(w => w.FirstSend).ToList(); | |||
if (otherHttpSessions.Count > 0) | |||
var merge = JT1078.Protocol.JT1078Serializer.Merge(Message.Data); | |||
string key = $"{Message.Data.GetKey()}_{ikey}"; | |||
if (merge != null) | |||
{ | |||
var flvVideoBuffer = FlvEncoder.EncoderVideoTag(merge, false); | |||
HttpSessionManager.SendAVData(otherHttpSessions, flvVideoBuffer, false); | |||
if (merge.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||
{ | |||
memoryCache.Set(key, merge); | |||
} | |||
var httpSessions = HttpSessionManager.GetAllBySimAndChannelNo(Message.Data.SIM.TrimStart('0'), Message.Data.LogicChannelNumber); | |||
var firstHttpSessions = httpSessions.Where(w => !w.FirstSend).ToList(); | |||
if (firstHttpSessions.Count > 0) | |||
{ | |||
if (memoryCache.TryGetValue(key, out JT1078Package idata)) | |||
{ | |||
try | |||
{ | |||
var flvVideoBuffer = FlvEncoder.EncoderVideoTag(idata, true); | |||
foreach (var session in firstHttpSessions) | |||
{ | |||
HttpSessionManager.SendAVData(session, flvVideoBuffer, true); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Logger.LogError(ex, $"{Message.Data.SIM},{true},{Message.Data.SN},{Message.Data.LogicChannelNumber},{Message.Data.Label3.DataType.ToString()},{Message.Data.Label3.SubpackageType.ToString()},{Message.Data.Bodies.ToHexString()}"); | |||
} | |||
} | |||
} | |||
var otherHttpSessions = httpSessions.Where(w => w.FirstSend).ToList(); | |||
if (otherHttpSessions.Count > 0) | |||
{ | |||
try | |||
{ | |||
var flvVideoBuffer = FlvEncoder.EncoderVideoTag(merge, false); | |||
foreach (var session in otherHttpSessions) | |||
{ | |||
HttpSessionManager.SendAVData(session, flvVideoBuffer, false); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Logger.LogError(ex, $"{Message.Data.SIM},{false},{Message.Data.SN},{Message.Data.LogicChannelNumber},{Message.Data.Label3.DataType.ToString()},{Message.Data.Label3.SubpackageType.ToString()},{Message.Data.Bodies.ToHexString()}"); | |||
} | |||
} | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Logger.LogError(ex, $"{Message.Data.SIM},{Message.Data.SN},{Message.Data.LogicChannelNumber},{Message.Data.Label3.DataType.ToString()},{Message.Data.Label3.SubpackageType.ToString()},{Message.Data.Bodies.ToHexString()}"); | |||
} | |||
}); | |||
return Task.CompletedTask; | |||
} | |||
@@ -0,0 +1,3 @@ | |||
pm2 start "dotnet JT1078.Gateway.TestNormalHosting.dll ASPNETCORE_ENVIRONMENT=Production" --max-restarts=1 -n "JT1078.Gateway.TestNormalHosting" -o "/data/pm2Logs/JT1078.Gateway.TestNormalHosting/out.log" -e "/data/pm2Logs/JT1078.Gateway.TestNormalHosting/error.log" | |||
pm2 start "dotnet JT1078.Gateway.TestNormalHosting.dll ASPNETCORE_ENVIRONMENT=Development" --max-restarts=1 -n "JT1078.Gateway.TestNormalHosting.Dev" -o "/data/pm2Logs/JT1078.Gateway.TestNormalHosting.Dev/out.log" -e "/data/pm2Logs/JT1078.Gateway.TestNormalHosting.Dev/error.log" |
@@ -0,0 +1,41 @@ | |||
<!DOCTYPE html> | |||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<title></title> | |||
<script src="flv.min.js"></script> | |||
</head> | |||
<body> | |||
<video muted="muted" webkit-playsinline="true" autoplay="true" id="player"></video> | |||
<script> | |||
if (flvjs.isSupported()) { | |||
var player = document.getElementById('player'); | |||
var flvPlayer = flvjs.createPlayer({ | |||
type: 'flv', | |||
isLive: true, | |||
url: "ws://127.0.0.1:15555/?sim=11234567810&channel=1&token=123456" | |||
}); | |||
flvPlayer.attachMediaElement(player); | |||
flvPlayer.load(); | |||
flvPlayer.play(); | |||
function componentDidMount() { | |||
this.cleanBuff = setInterval(function () { | |||
let buffered = player.buffered | |||
console.log("start...") | |||
if (buffered.length > 0) { | |||
let end = buffered.end(0) | |||
if (end - player.currentTime > 0.15) { | |||
player.currentTime = end - 0.1; | |||
console.log("exe... start") | |||
} | |||
} | |||
console.log("end...") | |||
}, 3 * 10 * 1000) | |||
}; | |||
componentDidMount(); | |||
} | |||
</script> | |||
</body> | |||
</html> |
@@ -27,6 +27,7 @@ namespace JT1078.Gateway | |||
private readonly JT1078Configuration Configuration; | |||
private readonly IJT1078Authorization authorization; | |||
private IMemoryCache memoryCache; | |||
private HttpListener listener; | |||
@@ -102,15 +103,16 @@ namespace JT1078.Gateway | |||
context.Http404(); | |||
return; | |||
} | |||
var uri = new Uri(context.Request.RawUrl); | |||
string url = uri.AbsolutePath; | |||
var queryParams = uri.Query.Substring(1, uri.Query.Length - 1).Split('&'); | |||
if (queryParams.Length < 2) { | |||
context.Http404(); | |||
return; | |||
} | |||
if (url.EndsWith(".m3u8") || url.EndsWith(".ts")) | |||
if (context.Request.RawUrl.EndsWith(".m3u8") || context.Request.RawUrl.EndsWith(".ts")) | |||
{ | |||
var uri = new Uri(context.Request.RawUrl); | |||
string url = uri.AbsolutePath; | |||
var queryParams = uri.Query.Substring(1, uri.Query.Length - 1).Split('&'); | |||
if (queryParams.Length < 2) | |||
{ | |||
context.Http404(); | |||
return; | |||
} | |||
string key = $"{queryParams[0].Split('=')[1]}_{queryParams[1].Split('=')[1]}";//默认queryParams第一个参数是终端号,第二个参数是通道号 | |||
memoryCache.GetOrCreate(key, (cacheEntry) => { | |||
cacheEntry.SetSlidingExpiration(TimeSpan.FromSeconds(20)); | |||
@@ -175,7 +177,8 @@ namespace JT1078.Gateway | |||
jT1078HttpContext.Sim = sim; | |||
jT1078HttpContext.ChannelNo = channelNo; | |||
SessionManager.TryAdd(jT1078HttpContext); | |||
await jT1078HttpContext.WebSocketSendHelloAsync(); | |||
//这个发送出去,flv.js就报错了 | |||
//await jT1078HttpContext.WebSocketSendHelloAsync(); | |||
await Task.Factory.StartNew(async(state) => | |||
{ | |||
//https://www.bejson.com/httputil/websocket/ | |||
@@ -3,6 +3,7 @@ using System.Buffers; | |||
using System.Buffers.Binary; | |||
using System.Collections.Generic; | |||
using System.IO.Pipelines; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
@@ -27,13 +28,15 @@ namespace JT1078.Gateway | |||
private readonly ILogger Logger; | |||
private readonly ILogger LogLogger; | |||
private readonly JT1078Configuration Configuration; | |||
private readonly JT1078SessionManager SessionManager; | |||
private readonly IJT1078PackageProducer jT1078PackageProducer; | |||
private readonly IJT1078PackageProducer jT1078PackageProducer; | |||
private readonly IJT1078MsgProducer jT1078MsgProducer; | |||
private readonly IJT1078MsgProducer jT1078MsgProducer; | |||
private readonly JT1078UseType jT1078UseType; | |||
@@ -53,6 +56,7 @@ namespace JT1078.Gateway | |||
SessionManager = jT1078SessionManager; | |||
jT1078UseType = JT1078UseType.Normal; | |||
Logger = loggerFactory.CreateLogger<JT1078TcpServer>(); | |||
LogLogger = loggerFactory.CreateLogger("JT1078Logging"); | |||
Configuration = jT1078ConfigurationAccessor.Value; | |||
this.jT1078PackageProducer = jT1078PackageProducer; | |||
InitServer(); | |||
@@ -74,6 +78,7 @@ namespace JT1078.Gateway | |||
SessionManager = jT1078SessionManager; | |||
jT1078UseType = JT1078UseType.Queue; | |||
Logger = loggerFactory.CreateLogger<JT1078TcpServer>(); | |||
LogLogger = loggerFactory.CreateLogger("JT1078Logging"); | |||
Configuration = jT1078ConfigurationAccessor.Value; | |||
this.jT1078MsgProducer = jT1078MsgProducer; | |||
InitServer(); | |||
@@ -199,29 +204,30 @@ namespace JT1078.Gateway | |||
} | |||
private void ReaderBuffer(ref ReadOnlySequence<byte> buffer, FixedHeaderInfo fixedHeaderInfo, JT1078TcpSession session, out SequencePosition consumed, out SequencePosition examined) | |||
{ | |||
consumed = buffer.Start; | |||
examined = buffer.End; | |||
SequenceReader<byte> seqReader = new SequenceReader<byte>(buffer); | |||
long totalConsumed = 0; | |||
while (!seqReader.End) | |||
{ | |||
if (seqReader.Remaining < 30) | |||
{ | |||
fixedHeaderInfo.Reset(); | |||
break; | |||
} | |||
if (!fixedHeaderInfo.FoundHeader) | |||
{ | |||
if (seqReader.Length < 4) | |||
throw new ArgumentException("not JT1078 package"); | |||
var header = seqReader.Sequence.Slice(seqReader.Consumed, 4); | |||
var header = seqReader.Sequence.Slice(0, 4); | |||
var headerValue = BinaryPrimitives.ReadUInt32BigEndian(header.FirstSpan); | |||
if (JT1078Package.FH == headerValue) | |||
{ | |||
//sim | |||
fixedHeaderInfo.SIM = ReadBCD(seqReader.Sequence.Slice(seqReader.Consumed + 8, 6).FirstSpan, 12); | |||
fixedHeaderInfo.SIM = ReadBCD(seqReader.Sequence.Slice(8, 6).FirstSpan, 12); | |||
if (string.IsNullOrEmpty(fixedHeaderInfo.SIM)) | |||
{ | |||
fixedHeaderInfo.SIM = session.SessionID; | |||
} | |||
//根据数据类型处理对应的数据长度 | |||
fixedHeaderInfo.TotalSize += 15; | |||
var dataType = seqReader.Sequence.Slice(seqReader.Consumed+fixedHeaderInfo.TotalSize, 1).FirstSpan[0]; | |||
var dataType = seqReader.Sequence.Slice(fixedHeaderInfo.TotalSize, 1).FirstSpan[0]; | |||
fixedHeaderInfo.TotalSize += 1; | |||
JT1078Label3 label3 = new JT1078Label3(dataType); | |||
int bodyLength = 0; | |||
@@ -240,33 +246,43 @@ namespace JT1078.Gateway | |||
bodyLength += 4; | |||
} | |||
fixedHeaderInfo.TotalSize += bodyLength; | |||
var bodyLengthFirstSpan = seqReader.Sequence.Slice(seqReader.Consumed + fixedHeaderInfo.TotalSize, 2).FirstSpan; | |||
var bodyLengthFirstSpan = seqReader.Sequence.Slice(fixedHeaderInfo.TotalSize, 2).FirstSpan; | |||
//数据体长度 | |||
fixedHeaderInfo.TotalSize += 2; | |||
bodyLength = BinaryPrimitives.ReadUInt16BigEndian(bodyLengthFirstSpan); | |||
if (bodyLength < 0) | |||
{ | |||
fixedHeaderInfo.Reset(); | |||
throw new ArgumentException("jt1078 package body length Error."); | |||
} | |||
if (bodyLength == 0)//数据体长度为0 | |||
{ | |||
seqReader.Advance(fixedHeaderInfo.TotalSize); | |||
var package1 = seqReader.Sequence.Slice(totalConsumed, seqReader.Consumed - totalConsumed); | |||
var package1 = seqReader.Sequence.Slice(0, fixedHeaderInfo.TotalSize).ToArray(); | |||
if (LogLogger.IsEnabled(LogLevel.Trace)) | |||
{ | |||
LogLogger.LogTrace($"{package1.ToHexString()}"); | |||
} | |||
try | |||
{ | |||
SessionManager.TryLink(fixedHeaderInfo.SIM, session); | |||
if (jT1078UseType == JT1078UseType.Queue) | |||
{ | |||
jT1078MsgProducer.ProduceAsync(fixedHeaderInfo.SIM, package1.FirstSpan.ToArray()); | |||
jT1078MsgProducer.ProduceAsync(fixedHeaderInfo.SIM, package1.ToArray()); | |||
} | |||
else | |||
{ | |||
jT1078PackageProducer.ProduceAsync(fixedHeaderInfo.SIM, JT1078Serializer.Deserialize(package1.FirstSpan)); | |||
jT1078PackageProducer.ProduceAsync(fixedHeaderInfo.SIM, JT1078Serializer.Deserialize(package1)); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Logger.LogError(ex, $"[Parse]:{package1.ToArray().ToHexString()}"); | |||
LogLogger.LogError($"[Error Parse 1]:{package1.ToHexString()}"); | |||
Logger.LogError(ex, $"[Error Parse 1]:{package1.ToHexString()}"); | |||
} | |||
finally | |||
{ | |||
totalConsumed += (seqReader.Consumed - totalConsumed); | |||
totalConsumed += seqReader.Consumed; | |||
fixedHeaderInfo.Reset(); | |||
} | |||
continue; | |||
@@ -275,44 +291,67 @@ namespace JT1078.Gateway | |||
fixedHeaderInfo.TotalSize += bodyLength; | |||
fixedHeaderInfo.FoundHeader = true; | |||
} | |||
else | |||
{ | |||
fixedHeaderInfo.Reset(); | |||
throw new ArgumentException("not JT1078 package."); | |||
} | |||
} | |||
if (seqReader.Length < fixedHeaderInfo.TotalSize) break; | |||
if ((seqReader.Remaining - fixedHeaderInfo.TotalSize) < 0) break; | |||
seqReader.Advance(fixedHeaderInfo.TotalSize); | |||
var package = seqReader.Sequence.Slice(totalConsumed, seqReader.Consumed - totalConsumed); | |||
var package = seqReader.Sequence.Slice(0, fixedHeaderInfo.TotalSize).ToArray(); | |||
if (LogLogger.IsEnabled(LogLevel.Trace)) | |||
{ | |||
LogLogger.LogTrace($"{package.ToHexString()}"); | |||
} | |||
try | |||
{ | |||
SessionManager.TryLink(fixedHeaderInfo.SIM, session); | |||
if (jT1078UseType == JT1078UseType.Queue) | |||
{ | |||
jT1078MsgProducer.ProduceAsync(fixedHeaderInfo.SIM, package.ToArray()); | |||
jT1078MsgProducer.ProduceAsync(fixedHeaderInfo.SIM, package); | |||
} | |||
else | |||
{ | |||
jT1078PackageProducer.ProduceAsync(fixedHeaderInfo.SIM, JT1078Serializer.Deserialize(package.FirstSpan)); | |||
jT1078PackageProducer.ProduceAsync(fixedHeaderInfo.SIM, JT1078Serializer.Deserialize(package)); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Logger.LogError(ex, $"[Parse]:{package.ToArray().ToHexString()}"); | |||
LogLogger.LogError($"[Error Parse 2]:{package.ToHexString()}"); | |||
Logger.LogError(ex, $"[Error Parse 2]:{package.ToHexString()}"); | |||
} | |||
finally | |||
{ | |||
totalConsumed += (seqReader.Consumed - totalConsumed); | |||
totalConsumed += seqReader.Consumed; | |||
fixedHeaderInfo.Reset(); | |||
seqReader = new SequenceReader<byte>(seqReader.Sequence.Slice(seqReader.Consumed)); | |||
} | |||
} | |||
if (seqReader.Length == totalConsumed) | |||
if (seqReader.End) | |||
{ | |||
examined = consumed = buffer.End; | |||
} | |||
else | |||
{ | |||
consumed = buffer.GetPosition(totalConsumed); | |||
examined = buffer.End; | |||
} | |||
} | |||
public Task StopAsync(CancellationToken cancellationToken) | |||
{ | |||
Logger.LogInformation("JT1078 Tcp Server Stop"); | |||
SessionManager.GetTcpAll().ForEach(session => | |||
{ | |||
try | |||
{ | |||
session.Close(); | |||
} | |||
catch (Exception ex) | |||
{ | |||
} | |||
}); | |||
if (server?.Connected ?? false) | |||
server.Shutdown(SocketShutdown.Both); | |||
server?.Close(); | |||
@@ -4,13 +4,16 @@ using System.Net; | |||
using System.Net.WebSockets; | |||
using System.Security.Principal; | |||
using System.Text; | |||
using System.Text.Json.Serialization; | |||
namespace JT1078.Gateway.Metadata | |||
{ | |||
public class JT1078HttpContext | |||
{ | |||
public string SessionId { get; } | |||
[JsonIgnore] | |||
public HttpListenerContext Context { get; } | |||
[JsonIgnore] | |||
public HttpListenerWebSocketContext WebSocketContext { get; } | |||
public IPrincipal User { get; } | |||
public string Sim { get; set; } | |||
@@ -88,72 +88,65 @@ namespace JT1078.Gateway.Sessions | |||
/// <summary> | |||
/// 发送音视频数据 | |||
/// </summary> | |||
/// <param name="httpContexts"></param> | |||
/// <param name="httpContext"></param> | |||
/// <param name="data"></param> | |||
/// <param name="firstSend"></param> | |||
public void SendAVData(List<JT1078HttpContext> httpContexts, byte[] data, bool firstSend) | |||
public async void SendAVData(JT1078HttpContext httpContext, byte[] data, bool firstSend) | |||
{ | |||
ParallelLoopResult parallelLoopResult = Parallel.ForEach(httpContexts, async (context) => | |||
if (httpContext.IsWebSocket) | |||
{ | |||
if (context.IsWebSocket) | |||
if (firstSend) | |||
{ | |||
if (firstSend) | |||
httpContext.FirstSend = firstSend; | |||
Sessions.TryUpdate(httpContext.SessionId, httpContext, httpContext); | |||
} | |||
try | |||
{ | |||
await httpContext.WebSocketSendBinaryAsync(data); | |||
} | |||
catch (Exception ex) | |||
{ | |||
if (Logger.IsEnabled(LogLevel.Information)) | |||
{ | |||
context.FirstSend = firstSend; | |||
Sessions.TryUpdate(context.SessionId, context, context); | |||
Logger.LogInformation($"[ws close]:{httpContext.SessionId}-{httpContext.Sim}-{httpContext.ChannelNo}-{httpContext.StartTime:yyyyMMddhhmmss}"); | |||
} | |||
remove(httpContext.SessionId); | |||
} | |||
} | |||
else | |||
{ | |||
if (firstSend) | |||
{ | |||
httpContext.FirstSend = firstSend; | |||
Sessions.TryUpdate(httpContext.SessionId, httpContext, httpContext); | |||
try | |||
{ | |||
await context.WebSocketSendBinaryAsync(data); | |||
await httpContext.HttpSendFirstChunked(data); | |||
} | |||
catch (Exception ex) | |||
{ | |||
if (Logger.IsEnabled(LogLevel.Information)) | |||
{ | |||
Logger.LogInformation($"[ws close]:{context.SessionId}-{context.Sim}-{context.ChannelNo}-{context.StartTime:yyyyMMddhhmmss}"); | |||
Logger.LogInformation($"[http close]:{httpContext.SessionId}-{httpContext.Sim}-{httpContext.ChannelNo}-{httpContext.StartTime:yyyyMMddhhmmss}"); | |||
} | |||
remove(context.SessionId); | |||
} | |||
remove(httpContext.SessionId); | |||
} | |||
} | |||
else | |||
{ | |||
if (firstSend) | |||
try | |||
{ | |||
context.FirstSend = firstSend; | |||
Sessions.TryUpdate(context.SessionId, context, context); | |||
try | |||
{ | |||
await context.HttpSendFirstChunked(data); | |||
} | |||
catch (Exception ex) | |||
{ | |||
if (Logger.IsEnabled(LogLevel.Information)) | |||
{ | |||
Logger.LogInformation($"[http close]:{context.SessionId}-{context.Sim}-{context.ChannelNo}-{context.StartTime:yyyyMMddhhmmss}"); | |||
} | |||
remove(context.SessionId); | |||
} | |||
await httpContext.HttpSendChunked(data); | |||
} | |||
else | |||
catch (Exception ex) | |||
{ | |||
try | |||
{ | |||
await context.HttpSendChunked(data); | |||
} | |||
catch (Exception ex) | |||
if (Logger.IsEnabled(LogLevel.Information)) | |||
{ | |||
if (Logger.IsEnabled(LogLevel.Information)) | |||
{ | |||
Logger.LogInformation($"[http close]:{context.SessionId}-{context.Sim}-{context.ChannelNo}-{context.StartTime:yyyyMMddhhmmss}"); | |||
} | |||
remove(context.SessionId); | |||
Logger.LogInformation($"[http close]:{httpContext.SessionId}-{httpContext.Sim}-{httpContext.ChannelNo}-{httpContext.StartTime:yyyyMMddhhmmss}"); | |||
} | |||
remove(httpContext.SessionId); | |||
} | |||
} | |||
}); | |||
if (parallelLoopResult.IsCompleted) | |||
{ | |||
} | |||
} | |||