@@ -1 +0,0 @@ | |||||
dotnet nuget push .\nupkgs\*.nupkg -k apikey -s https://api.nuget.org/v3/index.json |
@@ -1,5 +0,0 @@ | |||||
dotnet pack .\src\JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj --no-build --output ../../nupkgs | |||||
dotnet pack .\src\JT1078.DotNetty.Http\JT1078.DotNetty.Http.csproj --no-build --output ../../nupkgs | |||||
dotnet pack .\src\JT1078.DotNetty.Tcp\JT1078.DotNetty.Tcp.csproj --no-build --output ../../nupkgs | |||||
dotnet pack .\src\JT1078.DotNetty.Udp\JT1078.DotNetty.Udp.csproj --no-build --output ../../nupkgs | |||||
pause |
@@ -1,20 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using System.Collections.Generic; | |||||
using DotNetty.Transport.Channels; | |||||
using JT1078.Protocol; | |||||
using System; | |||||
namespace JT1078.DotNetty.Core.Codecs | |||||
{ | |||||
public class JT1078TcpDecoder : ByteToMessageDecoder | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) | |||||
{ | |||||
byte[] buffer = new byte[input.Capacity+4]; | |||||
input.ReadBytes(buffer, 4, input.Capacity); | |||||
Array.Copy(JT1078Package.FH_Bytes, 0,buffer, 0, 4); | |||||
output.Add(buffer); | |||||
} | |||||
} | |||||
} |
@@ -1,21 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Transport.Channels; | |||||
using System.Collections.Generic; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
namespace JT1078.DotNetty.Core.Codecs | |||||
{ | |||||
public class JT1078UdpDecoder : MessageToMessageDecoder<DatagramPacket> | |||||
{ | |||||
protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List<object> output) | |||||
{ | |||||
if (!message.Content.IsReadable()) return; | |||||
IByteBuffer byteBuffer = message.Content; | |||||
byte[] buffer = new byte[byteBuffer.ReadableBytes]; | |||||
byteBuffer.ReadBytes(buffer); | |||||
output.Add(new JT1078UdpPackage(buffer, message.Sender)); | |||||
} | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Configurations | |||||
{ | |||||
public class JT1078ClientConfiguration | |||||
{ | |||||
public string Host { get; set; } | |||||
public int Port { get; set; } | |||||
private EndPoint endPoint; | |||||
public EndPoint EndPoint | |||||
{ | |||||
get | |||||
{ | |||||
if (endPoint == null) | |||||
{ | |||||
if (IPAddress.TryParse(Host, out IPAddress ip)) | |||||
{ | |||||
endPoint = new IPEndPoint(ip, Port); | |||||
} | |||||
} | |||||
return endPoint; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,37 +0,0 @@ | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Configurations | |||||
{ | |||||
public class JT1078Configuration : IOptions<JT1078Configuration> | |||||
{ | |||||
public int TcpPort { get; set; } = 1808; | |||||
public int UdpPort { get; set; } = 1808; | |||||
public int HttpPort { get; set; } = 1818; | |||||
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; | |||||
public JT1078RemoteServerOptions RemoteServerOptions { get; set; } | |||||
public JT1078Configuration Value => this; | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using Microsoft.Extensions.Options; | |||||
using System.Collections.Generic; | |||||
namespace JT1078.DotNetty.Core.Configurations | |||||
{ | |||||
public class JT1078RemoteServerOptions:IOptions<JT1078RemoteServerOptions> | |||||
{ | |||||
public List<string> RemoteServers { get; set; } | |||||
public JT1078RemoteServerOptions Value => this; | |||||
} | |||||
} |
@@ -1,26 +0,0 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Converters | |||||
{ | |||||
public class JsonIPAddressConverter : JsonConverter | |||||
{ | |||||
public override bool CanConvert(Type objectType) | |||||
{ | |||||
return (objectType == typeof(IPAddress)); | |||||
} | |||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||||
{ | |||||
writer.WriteValue(value.ToString()); | |||||
} | |||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
{ | |||||
return IPAddress.Parse((string)reader.Value); | |||||
} | |||||
} | |||||
} |
@@ -1,32 +0,0 @@ | |||||
using Newtonsoft.Json; | |||||
using Newtonsoft.Json.Linq; | |||||
using System; | |||||
using System.Net; | |||||
namespace JT1078.DotNetty.Core.Converters | |||||
{ | |||||
public class JsonIPEndPointConverter: JsonConverter | |||||
{ | |||||
public override bool CanConvert(Type objectType) | |||||
{ | |||||
return (objectType == typeof(IPEndPoint)); | |||||
} | |||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||||
{ | |||||
IPEndPoint ep = (IPEndPoint)value; | |||||
JObject jo = new JObject(); | |||||
jo.Add("Host", JToken.FromObject(ep.Address, serializer)); | |||||
jo.Add("Port", ep.Port); | |||||
jo.WriteTo(writer); | |||||
} | |||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
{ | |||||
JObject jo = JObject.Load(reader); | |||||
IPAddress address = jo["Host"].ToObject<IPAddress>(serializer); | |||||
int port = (int)jo["Port"]; | |||||
return new IPEndPoint(address, port); | |||||
} | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Enums | |||||
{ | |||||
/// <summary> | |||||
/// 传输协议类型 | |||||
/// </summary> | |||||
public enum JT1078TransportProtocolType | |||||
{ | |||||
tcp=1, | |||||
udp = 2 | |||||
} | |||||
} |
@@ -1,36 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using DotNetty.Common.Utilities; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Extensions | |||||
{ | |||||
public static class JT1078HttpSessionExtensions | |||||
{ | |||||
private static readonly AsciiString ServerName = AsciiString.Cached("JT1078Netty"); | |||||
private static readonly AsciiString DateEntity = HttpHeaderNames.Date; | |||||
private static readonly AsciiString ServerEntity = HttpHeaderNames.Server; | |||||
public static void SendBinaryWebSocketAsync(this JT1078HttpSession session,byte[] data) | |||||
{ | |||||
session.Channel.WriteAndFlushAsync(new BinaryWebSocketFrame(Unpooled.WrappedBuffer(data))); | |||||
} | |||||
public static void SendHttpFirstChunkAsync(this JT1078HttpSession session, byte[] data) | |||||
{ | |||||
DefaultHttpResponse firstRes = new DefaultHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK); | |||||
firstRes.Headers.Set(ServerEntity, ServerName); | |||||
firstRes.Headers.Set(DateEntity, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); | |||||
firstRes.Headers.Set(HttpHeaderNames.ContentType, (AsciiString)"video/x-flv"); | |||||
HttpUtil.SetTransferEncodingChunked(firstRes, true); | |||||
session.Channel.WriteAsync(firstRes); | |||||
session.Channel.WriteAndFlushAsync(Unpooled.CopiedBuffer(data)); | |||||
} | |||||
public static void SendHttpOtherChunkAsync(this JT1078HttpSession session, byte[] data) | |||||
{ | |||||
session.Channel.WriteAndFlushAsync(Unpooled.CopiedBuffer(data)); | |||||
} | |||||
} | |||||
} |
@@ -1,19 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
namespace JT1078.DotNetty.Core.Impl | |||||
{ | |||||
sealed class JT1078BuilderDefault : IJT1078Builder | |||||
{ | |||||
public IServiceCollection Services { get; } | |||||
public JT1078BuilderDefault(IServiceCollection services) | |||||
{ | |||||
Services = services; | |||||
} | |||||
} | |||||
} |
@@ -1,14 +0,0 @@ | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Security.Principal; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IHttpMiddleware | |||||
{ | |||||
void Next(IChannelHandlerContext ctx, IFullHttpRequest req, IPrincipal principal); | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using DotNetty.Codecs.Http; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Security.Principal; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078Authorization | |||||
{ | |||||
bool Authorization(IFullHttpRequest request, out IPrincipal principal); | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078Builder | |||||
{ | |||||
IServiceCollection Services { get; } | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078HttpBuilder | |||||
{ | |||||
IJT1078Builder Instance { get; } | |||||
IJT1078Builder Builder(); | |||||
IJT1078HttpBuilder Replace<T>() where T : IJT1078Authorization; | |||||
IJT1078HttpBuilder UseHttpMiddleware<T>() where T : IHttpMiddleware; | |||||
} | |||||
} |
@@ -1,14 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078TcpBuilder | |||||
{ | |||||
IJT1078Builder Instance { get;} | |||||
IJT1078Builder Builder(); | |||||
IJT1078TcpBuilder Replace<T>() where T : IJT1078TcpMessageHandlers; | |||||
} | |||||
} |
@@ -1,11 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.Protocol; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078TcpMessageHandlers | |||||
{ | |||||
Task<JT1078Response> Processor(JT1078Request request); | |||||
} | |||||
} |
@@ -1,14 +0,0 @@ | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078UdpBuilder | |||||
{ | |||||
IJT1078Builder Instance { get; } | |||||
IJT1078Builder Builder(); | |||||
IJT1078UdpBuilder Replace<T>() where T : IJT1078UdpMessageHandlers; | |||||
} | |||||
} |
@@ -1,11 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.Protocol; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.DotNetty.Core.Interfaces | |||||
{ | |||||
public interface IJT1078UdpMessageHandlers | |||||
{ | |||||
Task<JT1078Response> Processor(JT1078Request request); | |||||
} | |||||
} |
@@ -1,43 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Import Project="..\Version.props" /> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>7.3</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | |||||
<Authors>SmallChi(Koike)</Authors> | |||||
<PackageId>JT1078.DotNetty.Core</PackageId> | |||||
<Product>JT1078.DotNetty.Core</Product> | |||||
<Description>基于DotNetty实现的JT1078DotNetty的核心库</Description> | |||||
<PackageReleaseNotes>基于DotNetty实现的JT1078DotNetty的核心库</PackageReleaseNotes> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT1078DotNetty</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078DotNetty</PackageProjectUrl> | |||||
<licenseUrl>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</licenseUrl> | |||||
<license>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</license> | |||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<SignAssembly>false</SignAssembly> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||||
<Version>$(JT1078DotNettyPackageVersion)</Version> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<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="DotNetty.Codecs" Version="0.6.0" /> | |||||
<PackageReference Include="JT1078" Version="1.0.1" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" /> | |||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> | |||||
<PackageReference Include="Microsoft.Extensions.Options" Version="3.0.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Folder Include="Properties\" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,77 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Configurations; | |||||
using JT1078.DotNetty.Core.Converters; | |||||
using JT1078.DotNetty.Core.Impl; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Services; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Core.Test")] | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Tcp.Test")] | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Udp.Test")] | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Tcp")] | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Udp")] | |||||
namespace JT1078.DotNetty.Core | |||||
{ | |||||
public static class JT1078CoreDotnettyExtensions | |||||
{ | |||||
static JT1078CoreDotnettyExtensions() | |||||
{ | |||||
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings(); | |||||
//日期类型默认格式化处理 | |||||
settings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat; | |||||
settings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; | |||||
//空值处理 | |||||
settings.NullValueHandling = NullValueHandling.Ignore; | |||||
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; | |||||
settings.Converters.Add(new JsonIPAddressConverter()); | |||||
settings.Converters.Add(new JsonIPEndPointConverter()); | |||||
return settings; | |||||
}); | |||||
} | |||||
public static IJT1078Builder AddJT1078Core(this IServiceCollection serviceDescriptors, IConfiguration configuration, Newtonsoft.Json.JsonSerializerSettings settings=null) | |||||
{ | |||||
if (settings != null) | |||||
{ | |||||
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
settings.Converters.Add(new JsonIPAddressConverter()); | |||||
settings.Converters.Add(new JsonIPEndPointConverter()); | |||||
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; | |||||
return settings; | |||||
}); | |||||
} | |||||
IJT1078Builder builder = new JT1078BuilderDefault(serviceDescriptors); | |||||
builder.Services.Configure<JT1078Configuration>(configuration.GetSection("JT1078Configuration")); | |||||
builder.Services.TryAddSingleton<JT1078AtomicCounterServiceFactory>(); | |||||
return builder; | |||||
} | |||||
public static IJT1078Builder AddJT1078Core(this IServiceCollection serviceDescriptors, Action<JT1078Configuration> jt1078Options, Newtonsoft.Json.JsonSerializerSettings settings = null) | |||||
{ | |||||
if (settings != null) | |||||
{ | |||||
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
settings.Converters.Add(new JsonIPAddressConverter()); | |||||
settings.Converters.Add(new JsonIPEndPointConverter()); | |||||
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; | |||||
return settings; | |||||
}); | |||||
} | |||||
IJT1078Builder builder = new JT1078BuilderDefault(serviceDescriptors); | |||||
builder.Services.Configure(jt1078Options); | |||||
builder.Services.TryAddSingleton<JT1078AtomicCounterService>(); | |||||
builder.Services.TryAddSingleton<JT1078AtomicCounterServiceFactory>(); | |||||
return builder; | |||||
} | |||||
} | |||||
} |
@@ -1,49 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | |||||
namespace JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// <see cref="Grpc.Core.Internal"/> | |||||
/// </summary> | |||||
internal class JT1078AtomicCounter | |||||
{ | |||||
long counter = 0; | |||||
public JT1078AtomicCounter(long initialCount = 0) | |||||
{ | |||||
this.counter = initialCount; | |||||
} | |||||
public void Reset() | |||||
{ | |||||
Interlocked.Exchange(ref counter, 0); | |||||
} | |||||
public long Increment() | |||||
{ | |||||
return Interlocked.Increment(ref counter); | |||||
} | |||||
public long Add(long len) | |||||
{ | |||||
return Interlocked.Add(ref counter,len); | |||||
} | |||||
public long Decrement() | |||||
{ | |||||
return Interlocked.Decrement(ref counter); | |||||
} | |||||
public long Count | |||||
{ | |||||
get | |||||
{ | |||||
return Interlocked.Read(ref counter); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using System.Net; | |||||
namespace JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT1078HttpSession | |||||
{ | |||||
public JT1078HttpSession( | |||||
IChannel channel, | |||||
string userId) | |||||
{ | |||||
Channel = channel; | |||||
UserId = userId; | |||||
StartTime = DateTime.Now; | |||||
LastActiveTime = DateTime.Now; | |||||
} | |||||
public JT1078HttpSession() { } | |||||
public string UserId { get; set; } | |||||
public string AttachInfo { get; set; } | |||||
public IChannel Channel { get; set; } | |||||
public DateTime LastActiveTime { get; set; } | |||||
public DateTime StartTime { get; set; } | |||||
} | |||||
} |
@@ -1,20 +0,0 @@ | |||||
using JT1078.Protocol; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT1078Request | |||||
{ | |||||
public JT1078Request(JT1078Package package,byte[] src) | |||||
{ | |||||
Package = package; | |||||
Src = src; | |||||
} | |||||
public JT1078Package Package { get; } | |||||
public byte[] Src { get; } | |||||
} | |||||
} |
@@ -1,10 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT1078Response | |||||
{ | |||||
} | |||||
} |
@@ -1,29 +0,0 @@ | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
namespace JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT1078TcpSession | |||||
{ | |||||
public JT1078TcpSession(IChannel channel, string terminalPhoneNo) | |||||
{ | |||||
Channel = channel; | |||||
TerminalPhoneNo = terminalPhoneNo; | |||||
StartTime = DateTime.Now; | |||||
LastActiveTime = DateTime.Now; | |||||
} | |||||
public JT1078TcpSession() { } | |||||
/// <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 JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT1078UdpPackage | |||||
{ | |||||
public JT1078UdpPackage(byte[] buffer, EndPoint sender) | |||||
{ | |||||
Buffer = buffer; | |||||
Sender = sender; | |||||
} | |||||
public byte[] Buffer { get; } | |||||
public EndPoint Sender { get; } | |||||
} | |||||
} |
@@ -1,35 +0,0 @@ | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using System.Net; | |||||
namespace JT1078.DotNetty.Core.Metadata | |||||
{ | |||||
public class JT1078UdpSession | |||||
{ | |||||
public JT1078UdpSession(IChannel channel, | |||||
EndPoint sender, | |||||
string terminalPhoneNo) | |||||
{ | |||||
Channel = channel; | |||||
TerminalPhoneNo = terminalPhoneNo; | |||||
StartTime = DateTime.Now; | |||||
LastActiveTime = DateTime.Now; | |||||
Sender = sender; | |||||
} | |||||
public EndPoint Sender { get; set; } | |||||
public JT1078UdpSession() { } | |||||
/// <summary> | |||||
/// 终端手机号 | |||||
/// </summary> | |||||
public string TerminalPhoneNo { get; set; } | |||||
public IChannel Channel { get; set; } | |||||
public DateTime LastActiveTime { get; set; } | |||||
public DateTime StartTime { get; set; } | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<!-- | |||||
https://go.microsoft.com/fwlink/?LinkID=208121. | |||||
--> | |||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<PublishProtocol>FileSystem</PublishProtocol> | |||||
<Configuration>Release</Configuration> | |||||
<Platform>Any CPU</Platform> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<PublishDir>..\..\publish\</PublishDir> | |||||
</PropertyGroup> | |||||
</Project> |
@@ -1,52 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
namespace JT1078.DotNetty.Core.Services | |||||
{ | |||||
/// <summary> | |||||
/// 计数包服务 | |||||
/// </summary> | |||||
public class JT1078AtomicCounterService | |||||
{ | |||||
private readonly JT1078AtomicCounter MsgSuccessCounter; | |||||
private readonly JT1078AtomicCounter MsgFailCounter; | |||||
public JT1078AtomicCounterService() | |||||
{ | |||||
MsgSuccessCounter=new JT1078AtomicCounter(); | |||||
MsgFailCounter = new JT1078AtomicCounter(); | |||||
} | |||||
public void Reset() | |||||
{ | |||||
MsgSuccessCounter.Reset(); | |||||
MsgFailCounter.Reset(); | |||||
} | |||||
public long MsgSuccessIncrement() | |||||
{ | |||||
return MsgSuccessCounter.Increment(); | |||||
} | |||||
public long MsgSuccessCount | |||||
{ | |||||
get | |||||
{ | |||||
return MsgSuccessCounter.Count; | |||||
} | |||||
} | |||||
public long MsgFailIncrement() | |||||
{ | |||||
return MsgFailCounter.Increment(); | |||||
} | |||||
public long MsgFailCount | |||||
{ | |||||
get | |||||
{ | |||||
return MsgFailCounter.Count; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,30 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Enums; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
namespace JT1078.DotNetty.Core.Services | |||||
{ | |||||
public class JT1078AtomicCounterServiceFactory | |||||
{ | |||||
private readonly ConcurrentDictionary<JT1078TransportProtocolType, JT1078AtomicCounterService> cache; | |||||
public JT1078AtomicCounterServiceFactory() | |||||
{ | |||||
cache = new ConcurrentDictionary<JT1078TransportProtocolType, JT1078AtomicCounterService>(); | |||||
} | |||||
public JT1078AtomicCounterService Create(JT1078TransportProtocolType type) | |||||
{ | |||||
if(cache.TryGetValue(type,out var service)) | |||||
{ | |||||
return service; | |||||
} | |||||
else | |||||
{ | |||||
var serviceNew = new JT1078AtomicCounterService(); | |||||
cache.TryAdd(type, serviceNew); | |||||
return serviceNew; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,64 +0,0 @@ | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using DotNetty.Transport.Channels; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
namespace JT1078.DotNetty.Core.Session | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 http会话管理 | |||||
/// </summary> | |||||
public class JT1078HttpSessionManager | |||||
{ | |||||
private readonly ILogger<JT1078HttpSessionManager> logger; | |||||
public JT1078HttpSessionManager( | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
logger = loggerFactory.CreateLogger<JT1078HttpSessionManager>(); | |||||
} | |||||
private ConcurrentDictionary<string, JT1078HttpSession> SessionDict = new ConcurrentDictionary<string, JT1078HttpSession>(); | |||||
public int SessionCount | |||||
{ | |||||
get | |||||
{ | |||||
return SessionDict.Count; | |||||
} | |||||
} | |||||
public List<JT1078HttpSession> GetSessions(string userId) | |||||
{ | |||||
return SessionDict.Where(m => m.Value.UserId == userId).Select(m=>m.Value).ToList(); | |||||
} | |||||
public void TryAdd(string userId,IChannel channel) | |||||
{ | |||||
SessionDict.TryAdd(channel.Id.AsShortText(), new JT1078HttpSession(channel, userId)); | |||||
if (logger.IsEnabled(LogLevel.Information)) | |||||
{ | |||||
logger.LogInformation($">>>{userId},{channel.Id.AsShortText()} Channel Connection."); | |||||
} | |||||
} | |||||
public void RemoveSessionByChannel(IChannel channel) | |||||
{ | |||||
if (channel.Open&& SessionDict.TryRemove(channel.Id.AsShortText(), out var session)) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Information)) | |||||
{ | |||||
logger.LogInformation($">>>{session.UserId},{session.Channel.Id.AsShortText()} Channel Remove."); | |||||
} | |||||
} | |||||
} | |||||
public List<JT1078HttpSession> GetAll() | |||||
{ | |||||
return SessionDict.Select(s => s.Value).ToList(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,100 +0,0 @@ | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using DotNetty.Transport.Channels; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
namespace JT1078.DotNetty.Core.Session | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 Tcp会话管理 | |||||
/// </summary> | |||||
public class JT1078TcpSessionManager | |||||
{ | |||||
private readonly ILogger<JT1078TcpSessionManager> logger; | |||||
public JT1078TcpSessionManager( | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
logger = loggerFactory.CreateLogger<JT1078TcpSessionManager>(); | |||||
} | |||||
private ConcurrentDictionary<string, JT1078TcpSession> SessionIdDict = new ConcurrentDictionary<string, JT1078TcpSession>(StringComparer.OrdinalIgnoreCase); | |||||
public int SessionCount | |||||
{ | |||||
get | |||||
{ | |||||
return SessionIdDict.Count; | |||||
} | |||||
} | |||||
public JT1078TcpSession GetSession(string terminalPhoneNo) | |||||
{ | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) | |||||
return default; | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT1078TcpSession targetSession)) | |||||
{ | |||||
return targetSession; | |||||
} | |||||
else | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
public void TryAdd(string terminalPhoneNo,IChannel channel) | |||||
{ | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT1078TcpSession oldSession)) | |||||
{ | |||||
oldSession.LastActiveTime = DateTime.Now; | |||||
oldSession.Channel = channel; | |||||
SessionIdDict.TryUpdate(terminalPhoneNo, oldSession, oldSession); | |||||
} | |||||
else | |||||
{ | |||||
JT1078TcpSession session = new JT1078TcpSession(channel, terminalPhoneNo); | |||||
if (SessionIdDict.TryAdd(terminalPhoneNo, session)) | |||||
{ | |||||
} | |||||
} | |||||
} | |||||
public JT1078TcpSession RemoveSession(string terminalPhoneNo) | |||||
{ | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) return default; | |||||
if (SessionIdDict.TryRemove(terminalPhoneNo, out JT1078TcpSession sessionRemove)) | |||||
{ | |||||
logger.LogInformation($">>>{terminalPhoneNo} Session Remove."); | |||||
return sessionRemove; | |||||
} | |||||
else | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
public void RemoveSessionByChannel(IChannel channel) | |||||
{ | |||||
var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList(); | |||||
if (terminalPhoneNos.Count > 0) | |||||
{ | |||||
foreach (var key in terminalPhoneNos) | |||||
{ | |||||
SessionIdDict.TryRemove(key, out JT1078TcpSession sessionRemove); | |||||
} | |||||
string nos = string.Join(",", terminalPhoneNos); | |||||
logger.LogInformation($">>>{nos} Channel Remove."); | |||||
} | |||||
} | |||||
public IEnumerable<JT1078TcpSession> GetAll() | |||||
{ | |||||
return SessionIdDict.Select(s => s.Value).ToList(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,116 +0,0 @@ | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.Extensions.Options; | |||||
using System.Net; | |||||
using JT1078.DotNetty.Core.Configurations; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
namespace JT1078.DotNetty.Core.Session | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 udp会话管理 | |||||
/// 估计要轮询下 | |||||
/// </summary> | |||||
public class JT1078UdpSessionManager | |||||
{ | |||||
private readonly ILogger<JT1078UdpSessionManager> logger; | |||||
public JT1078UdpSessionManager( | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
logger = loggerFactory.CreateLogger<JT1078UdpSessionManager>(); | |||||
} | |||||
private ConcurrentDictionary<string, JT1078UdpSession> SessionIdDict = new ConcurrentDictionary<string, JT1078UdpSession>(StringComparer.OrdinalIgnoreCase); | |||||
public int SessionCount | |||||
{ | |||||
get | |||||
{ | |||||
return SessionIdDict.Count; | |||||
} | |||||
} | |||||
public JT1078UdpSession GetSession(string terminalPhoneNo) | |||||
{ | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) | |||||
return default; | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT1078UdpSession targetSession)) | |||||
{ | |||||
return targetSession; | |||||
} | |||||
else | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
public void TryAdd(IChannel channel,EndPoint sender,string terminalPhoneNo) | |||||
{ | |||||
//1.先判断是否在缓存里面 | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT1078UdpSession UdpSession)) | |||||
{ | |||||
UdpSession.LastActiveTime=DateTime.Now; | |||||
UdpSession.Sender = sender; | |||||
UdpSession.Channel = channel; | |||||
SessionIdDict.TryUpdate(terminalPhoneNo, UdpSession, UdpSession); | |||||
} | |||||
else | |||||
{ | |||||
SessionIdDict.TryAdd(terminalPhoneNo, new JT1078UdpSession(channel, sender, terminalPhoneNo)); | |||||
} | |||||
} | |||||
public void Heartbeat(string terminalPhoneNo) | |||||
{ | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) return; | |||||
if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT1078UdpSession oldSession)) | |||||
{ | |||||
oldSession.LastActiveTime = DateTime.Now; | |||||
SessionIdDict.TryUpdate(terminalPhoneNo, oldSession, oldSession); | |||||
} | |||||
} | |||||
public JT1078UdpSession RemoveSession(string terminalPhoneNo) | |||||
{ | |||||
//设备离线可以进行通知 | |||||
//使用Redis 发布订阅 | |||||
if (string.IsNullOrEmpty(terminalPhoneNo)) return default; | |||||
if (SessionIdDict.TryRemove(terminalPhoneNo, out JT1078UdpSession SessionRemove)) | |||||
{ | |||||
logger.LogInformation($">>>{terminalPhoneNo} Session Remove."); | |||||
return SessionRemove; | |||||
} | |||||
else | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
public void RemoveSessionByChannel(IChannel channel) | |||||
{ | |||||
//设备离线可以进行通知 | |||||
//使用Redis 发布订阅 | |||||
var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList(); | |||||
if (terminalPhoneNos.Count > 0) | |||||
{ | |||||
foreach (var key in terminalPhoneNos) | |||||
{ | |||||
SessionIdDict.TryRemove(key, out JT1078UdpSession SessionRemove); | |||||
} | |||||
string nos = string.Join(",", terminalPhoneNos); | |||||
logger.LogInformation($">>>{nos} Channel Remove."); | |||||
} | |||||
} | |||||
public IEnumerable<JT1078UdpSession> GetAll() | |||||
{ | |||||
return SessionIdDict.Select(s => s.Value).ToList(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,32 +0,0 @@ | |||||
using DotNetty.Codecs.Http; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Security.Claims; | |||||
using System.Security.Principal; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Http.Authorization | |||||
{ | |||||
class JT1078AuthorizationDefault : IJT1078Authorization | |||||
{ | |||||
public bool Authorization(IFullHttpRequest request, out IPrincipal principal) | |||||
{ | |||||
var uriSpan = request.Uri.AsSpan(); | |||||
var uriParamStr = uriSpan.Slice(uriSpan.IndexOf('?')+1).ToString().ToLower(); | |||||
var uriParams = uriParamStr.Split('&'); | |||||
var tokenParam = uriParams.FirstOrDefault(m => m.Contains("token")); | |||||
if (!string.IsNullOrEmpty(tokenParam)) | |||||
{ | |||||
principal = new ClaimsPrincipal(new GenericIdentity(tokenParam.Split('=')[1])); | |||||
return true; | |||||
} | |||||
else | |||||
{ | |||||
principal = null; | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,183 +0,0 @@ | |||||
using System; | |||||
using System.Diagnostics; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using DotNetty.Common.Utilities; | |||||
using DotNetty.Transport.Channels; | |||||
using static DotNetty.Codecs.Http.HttpVersion; | |||||
using static DotNetty.Codecs.Http.HttpResponseStatus; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using System.Text.RegularExpressions; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
namespace JT1078.DotNetty.Http.Handlers | |||||
{ | |||||
public sealed class JT1078HttpServerHandler : SimpleChannelInboundHandler<object> | |||||
{ | |||||
const string WebsocketPath = "/jt1078live"; | |||||
WebSocketServerHandshaker handshaker; | |||||
private static readonly AsciiString ServerName = AsciiString.Cached("JT1078Netty"); | |||||
private static readonly AsciiString DateEntity = HttpHeaderNames.Date; | |||||
private static readonly AsciiString ServerEntity = HttpHeaderNames.Server; | |||||
private readonly ILogger<JT1078HttpServerHandler> logger; | |||||
private readonly JT1078HttpSessionManager jT1078HttpSessionManager; | |||||
private readonly IJT1078Authorization iJT1078Authorization; | |||||
private readonly IHttpMiddleware httpMiddleware; | |||||
public JT1078HttpServerHandler( | |||||
JT1078HttpSessionManager jT1078HttpSessionManager, | |||||
IJT1078Authorization iJT1078Authorization, | |||||
ILoggerFactory loggerFactory, | |||||
IHttpMiddleware httpMiddleware = null) | |||||
{ | |||||
this.jT1078HttpSessionManager = jT1078HttpSessionManager; | |||||
this.iJT1078Authorization = iJT1078Authorization; | |||||
this.httpMiddleware = httpMiddleware; | |||||
logger = loggerFactory.CreateLogger<JT1078HttpServerHandler>(); | |||||
} | |||||
public override void ChannelInactive(IChannelHandlerContext context) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Information)) | |||||
{ | |||||
logger.LogInformation(context.Channel.Id.AsShortText()); | |||||
} | |||||
jT1078HttpSessionManager.RemoveSessionByChannel(context.Channel); | |||||
base.ChannelInactive(context); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, object msg) | |||||
{ | |||||
if (msg is IFullHttpRequest request) | |||||
{ | |||||
this.HandleHttpRequest(ctx, request); | |||||
} | |||||
else if (msg is WebSocketFrame frame) | |||||
{ | |||||
this.HandleWebSocketFrame(ctx, frame); | |||||
} | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); | |||||
void HandleHttpRequest(IChannelHandlerContext ctx, IFullHttpRequest req) | |||||
{ | |||||
// Handle a bad request. | |||||
if (!req.Result.IsSuccess) | |||||
{ | |||||
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, BadRequest)); | |||||
return; | |||||
} | |||||
if ("/favicon.ico".Equals(req.Uri)) | |||||
{ | |||||
var res = new DefaultFullHttpResponse(Http11, NotFound); | |||||
SendHttpResponse(ctx, req, res); | |||||
return; | |||||
} | |||||
if (iJT1078Authorization.Authorization(req, out var principal)) | |||||
{ | |||||
if (req.Uri.StartsWith(WebsocketPath)) | |||||
{ | |||||
// Handshake | |||||
var wsFactory = new WebSocketServerHandshakerFactory(GetWebSocketLocation(req), null, true, 5 * 1024 * 1024); | |||||
this.handshaker = wsFactory.NewHandshaker(req); | |||||
if (this.handshaker == null) | |||||
{ | |||||
WebSocketServerHandshakerFactory.SendUnsupportedVersionResponse(ctx.Channel); | |||||
} | |||||
else | |||||
{ | |||||
this.handshaker.HandshakeAsync(ctx.Channel, req); | |||||
jT1078HttpSessionManager.TryAdd(principal.Identity.Name, ctx.Channel); | |||||
httpMiddleware?.Next(ctx, req, principal); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
jT1078HttpSessionManager.TryAdd(principal.Identity.Name, ctx.Channel); | |||||
httpMiddleware?.Next(ctx, req, principal); | |||||
} | |||||
} | |||||
else { | |||||
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, Unauthorized)); | |||||
return; | |||||
} | |||||
} | |||||
void HandleWebSocketFrame(IChannelHandlerContext ctx, WebSocketFrame frame) | |||||
{ | |||||
// Check for closing frame | |||||
if (frame is CloseWebSocketFrame) | |||||
{ | |||||
this.handshaker.CloseAsync(ctx.Channel, (CloseWebSocketFrame)frame.Retain()); | |||||
return; | |||||
} | |||||
if (frame is PingWebSocketFrame) | |||||
{ | |||||
ctx.WriteAsync(new PongWebSocketFrame((IByteBuffer)frame.Content.Retain())); | |||||
return; | |||||
} | |||||
if (frame is TextWebSocketFrame) | |||||
{ | |||||
// Echo the frame | |||||
ctx.WriteAsync(frame.Retain()); | |||||
return; | |||||
} | |||||
if (frame is BinaryWebSocketFrame) | |||||
{ | |||||
// Echo the frame | |||||
ctx.WriteAsync(frame.Retain()); | |||||
} | |||||
} | |||||
static void SendHttpResponse(IChannelHandlerContext ctx, IFullHttpRequest req, IFullHttpResponse res) | |||||
{ | |||||
// Generate an error page if response getStatus code is not OK (200). | |||||
if (res.Status.Code != 200) | |||||
{ | |||||
res.Headers.Set(ServerEntity, ServerName); | |||||
res.Headers.Set(DateEntity, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); | |||||
IByteBuffer buf = Unpooled.CopiedBuffer(Encoding.UTF8.GetBytes(res.Status.ToString())); | |||||
res.Content.WriteBytes(buf); | |||||
buf.Release(); | |||||
HttpUtil.SetContentLength(res, res.Content.ReadableBytes); | |||||
} | |||||
// Send the response and close the connection if necessary. | |||||
Task task = ctx.Channel.WriteAndFlushAsync(res); | |||||
if (!HttpUtil.IsKeepAlive(req) || res.Status.Code != 200) | |||||
{ | |||||
task.ContinueWith((t, c) => ((IChannelHandlerContext)c).CloseAsync(), ctx, TaskContinuationOptions.ExecuteSynchronously); | |||||
} | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
logger.LogError(exception, context.Channel.Id.AsShortText()); | |||||
context.Channel.WriteAndFlushAsync(new DefaultFullHttpResponse(Http11, InternalServerError)); | |||||
jT1078HttpSessionManager.RemoveSessionByChannel(context.Channel); | |||||
CloseAsync(context); | |||||
base.ExceptionCaught(context, exception); | |||||
} | |||||
public override Task CloseAsync(IChannelHandlerContext context) | |||||
{ | |||||
jT1078HttpSessionManager.RemoveSessionByChannel(context.Channel); | |||||
return base.CloseAsync(context); | |||||
} | |||||
static string GetWebSocketLocation(IFullHttpRequest req) | |||||
{ | |||||
bool result = req.Headers.TryGet(HttpHeaderNames.Host, out ICharSequence value); | |||||
string location= value.ToString() + WebsocketPath; | |||||
return "ws://" + location; | |||||
} | |||||
} | |||||
} |
@@ -1,30 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Import Project="..\Version.props" /> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>7.3</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | |||||
<Authors>SmallChi(Koike)</Authors> | |||||
<PackageId>JT1078.DotNetty.Http</PackageId> | |||||
<Product>JT1078.DotNetty.Http</Product> | |||||
<Description>基于DotNetty实现的JT1078DotNetty的http服务</Description> | |||||
<PackageReleaseNotes>基于DotNetty实现的JT1078DotNetty的http服务</PackageReleaseNotes> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT1078DotNetty</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078DotNetty</PackageProjectUrl> | |||||
<licenseUrl>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</licenseUrl> | |||||
<license>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</license> | |||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<SignAssembly>false</SignAssembly> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||||
<Version>$(JT1078DotNettyPackageVersion)</Version> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,36 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Http | |||||
{ | |||||
class JT1078HttpBuilderDefault : IJT1078HttpBuilder | |||||
{ | |||||
public IJT1078Builder Instance { get; } | |||||
public JT1078HttpBuilderDefault(IJT1078Builder builder) | |||||
{ | |||||
Instance = builder; | |||||
} | |||||
public IJT1078Builder Builder() | |||||
{ | |||||
return Instance; | |||||
} | |||||
public IJT1078HttpBuilder Replace<T>() where T : IJT1078Authorization | |||||
{ | |||||
Instance.Services.Replace(new ServiceDescriptor(typeof(IJT1078Authorization), typeof(T), ServiceLifetime.Singleton)); | |||||
return this; | |||||
} | |||||
public IJT1078HttpBuilder UseHttpMiddleware<T>() where T : IHttpMiddleware | |||||
{ | |||||
Instance.Services.TryAdd(new ServiceDescriptor(typeof(IHttpMiddleware), typeof(T), ServiceLifetime.Singleton)); | |||||
return this; | |||||
} | |||||
} | |||||
} |
@@ -1,25 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Codecs; | |||||
using JT1078.DotNetty.Core.Impl; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Http.Authorization; | |||||
using JT1078.DotNetty.Http.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System.Runtime.CompilerServices; | |||||
namespace JT1078.DotNetty.Http | |||||
{ | |||||
public static class JT1078HttpDotnettyExtensions | |||||
{ | |||||
public static IJT1078HttpBuilder AddJT1078HttpHost(this IJT1078Builder builder) | |||||
{ | |||||
builder.Services.TryAddSingleton<JT1078HttpSessionManager>(); | |||||
builder.Services.TryAddSingleton<IJT1078Authorization, JT1078AuthorizationDefault>(); | |||||
builder.Services.AddScoped<JT1078HttpServerHandler>(); | |||||
builder.Services.AddHostedService<JT1078HttpServerHost>(); | |||||
return new JT1078HttpBuilderDefault(builder); | |||||
} | |||||
} | |||||
} |
@@ -1,99 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Codecs.Http.Cors; | |||||
using DotNetty.Common.Utilities; | |||||
using DotNetty.Handlers.Streams; | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Libuv; | |||||
using JT1078.DotNetty.Core.Codecs; | |||||
using JT1078.DotNetty.Core.Configurations; | |||||
using JT1078.DotNetty.Http.Handlers; | |||||
using JT1078.Protocol; | |||||
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 JT1078.DotNetty.Http | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 http服务 | |||||
/// </summary> | |||||
internal class JT1078HttpServerHost : IHostedService | |||||
{ | |||||
private readonly JT1078Configuration configuration; | |||||
private readonly ILogger<JT1078HttpServerHost> logger; | |||||
private DispatcherEventLoopGroup bossGroup; | |||||
private WorkerEventLoopGroup workerGroup; | |||||
private IChannel bootstrapChannel; | |||||
private IByteBufferAllocator serverBufferAllocator; | |||||
private readonly IServiceProvider serviceProvider; | |||||
public JT1078HttpServerHost( | |||||
IServiceProvider serviceProvider, | |||||
ILoggerFactory loggerFactory, | |||||
IOptions<JT1078Configuration> configurationAccessor) | |||||
{ | |||||
this.serviceProvider = serviceProvider; | |||||
configuration = configurationAccessor.Value; | |||||
logger=loggerFactory.CreateLogger<JT1078HttpServerHost>(); | |||||
} | |||||
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, 8192) | |||||
.ChildOption(ChannelOption.Allocator, serverBufferAllocator) | |||||
.ChildHandler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
pipeline.AddLast(new HttpServerCodec()); | |||||
pipeline.AddLast(new CorsHandler(CorsConfigBuilder | |||||
.ForAnyOrigin() | |||||
.AllowNullOrigin() | |||||
.AllowedRequestMethods(HttpMethod.Get, HttpMethod.Post, HttpMethod.Options, HttpMethod.Delete) | |||||
.AllowedRequestHeaders((AsciiString)"origin", (AsciiString)"range", (AsciiString)"accept-encoding", (AsciiString)"referer", (AsciiString)"Cache-Control", (AsciiString)"X-Proxy-Authorization", (AsciiString)"X-Requested-With", (AsciiString)"Content-Type") | |||||
.ExposeHeaders((StringCharSequence)"Server", (StringCharSequence)"range", (StringCharSequence)"Content-Length", (StringCharSequence)"Content-Range") | |||||
.AllowCredentials() | |||||
.Build())); | |||||
pipeline.AddLast(new HttpObjectAggregator(int.MaxValue)); | |||||
using (var scope = serviceProvider.CreateScope()) | |||||
{ | |||||
pipeline.AddLast("JT1078HttpServerHandler", scope.ServiceProvider.GetRequiredService<JT1078HttpServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"JT1078 Http Server start at {IPAddress.Any}:{configuration.HttpPort}."); | |||||
return bootstrap.BindAsync(configuration.HttpPort) | |||||
.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,99 +0,0 @@ | |||||
using DotNetty.Handlers.Timeout; | |||||
using DotNetty.Transport.Channels; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.DotNetty.Tcp.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT1078服务通道处理程序 | |||||
/// </summary> | |||||
internal class JT1078TcpConnectionHandler : ChannelHandlerAdapter | |||||
{ | |||||
private readonly ILogger<JT1078TcpConnectionHandler> logger; | |||||
private readonly JT1078TcpSessionManager SessionManager; | |||||
public JT1078TcpConnectionHandler( | |||||
JT1078TcpSessionManager sessionManager, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.SessionManager = sessionManager; | |||||
logger = loggerFactory.CreateLogger<JT1078TcpConnectionHandler>(); | |||||
} | |||||
/// <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."); | |||||
SessionManager.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."); | |||||
SessionManager.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是设备发心跳,如果很久没有上报数据,那么就由服务器主动关闭连接。 | |||||
SessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
base.UserEventTriggered(context, evt); | |||||
} | |||||
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) | |||||
{ | |||||
string channelId = context.Channel.Id.AsShortText(); | |||||
logger.LogError(exception,$"{channelId} {exception.Message}" ); | |||||
SessionManager.RemoveSessionByChannel(context.Channel); | |||||
context.CloseAsync(); | |||||
} | |||||
} | |||||
} | |||||
@@ -1,18 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.Protocol; | |||||
namespace JT1078.DotNetty.Tcp.Handlers | |||||
{ | |||||
class JT1078TcpMessageProcessorEmptyImpl : IJT1078TcpMessageHandlers | |||||
{ | |||||
public Task<JT1078Response> Processor(JT1078Request request) | |||||
{ | |||||
return Task.FromResult<JT1078Response>(default); | |||||
} | |||||
} | |||||
} |
@@ -1,69 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT1078.DotNetty.Core.Enums; | |||||
using JT1078.DotNetty.Core.Services; | |||||
using JT1078.Protocol; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
namespace JT1078.DotNetty.Tcp.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 服务端处理程序 | |||||
/// </summary> | |||||
internal class JT1078TcpServerHandler : SimpleChannelInboundHandler<byte[]> | |||||
{ | |||||
private readonly JT1078TcpSessionManager SessionManager; | |||||
private readonly JT1078AtomicCounterService AtomicCounterService; | |||||
private readonly ILogger<JT1078TcpServerHandler> logger; | |||||
private readonly IJT1078TcpMessageHandlers handlers; | |||||
public JT1078TcpServerHandler( | |||||
IJT1078TcpMessageHandlers handlers, | |||||
ILoggerFactory loggerFactory, | |||||
JT1078AtomicCounterServiceFactory atomicCounterServiceFactory, | |||||
JT1078TcpSessionManager sessionManager) | |||||
{ | |||||
this.handlers = handlers; | |||||
this.SessionManager = sessionManager; | |||||
this.AtomicCounterService = atomicCounterServiceFactory.Create(JT1078TransportProtocolType.tcp); | |||||
logger = loggerFactory.CreateLogger<JT1078TcpServerHandler>(); | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, byte[] msg) | |||||
{ | |||||
try | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Trace)) | |||||
{ | |||||
logger.LogTrace("accept package success count<<<" + AtomicCounterService.MsgSuccessCount.ToString()); | |||||
logger.LogTrace("accept msg <<< " + ByteBufferUtil.HexDump(msg)); | |||||
} | |||||
JT1078Package package = JT1078Serializer.Deserialize(msg); | |||||
AtomicCounterService.MsgSuccessIncrement(); | |||||
SessionManager.TryAdd(package.SIM, ctx.Channel); | |||||
handlers.Processor(new JT1078Request(package, msg)); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug("accept package success count<<<" + AtomicCounterService.MsgSuccessCount.ToString()); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
AtomicCounterService.MsgFailIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError("accept package fail count<<<" + AtomicCounterService.MsgFailCount.ToString()); | |||||
logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Import Project="..\Version.props" /> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>7.3</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | |||||
<Authors>SmallChi(Koike)</Authors> | |||||
<PackageId>JT1078.DotNetty.Tcp</PackageId> | |||||
<Product>JT1078.DotNetty.Tcp</Product> | |||||
<Description>基于DotNetty实现的JT1078DotNetty的Tcp服务</Description> | |||||
<PackageReleaseNotes>基于DotNetty实现的JT1078DotNetty的Tcp服务</PackageReleaseNotes> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT1078DotNetty</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078DotNetty</PackageProjectUrl> | |||||
<licenseUrl>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</licenseUrl> | |||||
<license>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</license> | |||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<SignAssembly>false</SignAssembly> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||||
<Version>$(JT1078DotNettyPackageVersion)</Version> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,30 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Tcp | |||||
{ | |||||
class JT1078TcpBuilderDefault : IJT1078TcpBuilder | |||||
{ | |||||
public IJT1078Builder Instance { get; } | |||||
public JT1078TcpBuilderDefault(IJT1078Builder builder) | |||||
{ | |||||
Instance = builder; | |||||
} | |||||
public IJT1078Builder Builder() | |||||
{ | |||||
return Instance; | |||||
} | |||||
public IJT1078TcpBuilder Replace<T>() where T : IJT1078TcpMessageHandlers | |||||
{ | |||||
Instance.Services.Replace(new ServiceDescriptor(typeof(IJT1078TcpMessageHandlers), typeof(T), ServiceLifetime.Singleton)); | |||||
return this; | |||||
} | |||||
} | |||||
} |
@@ -1,26 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Codecs; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Tcp.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System.Runtime.CompilerServices; | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Tcp.Test")] | |||||
namespace JT1078.DotNetty.Tcp | |||||
{ | |||||
public static class JT1078TcpDotnettyExtensions | |||||
{ | |||||
public static IJT1078TcpBuilder AddJT1078TcpHost(this IJT1078Builder builder) | |||||
{ | |||||
builder.Services.TryAddSingleton<JT1078TcpSessionManager>(); | |||||
builder.Services.TryAddScoped<JT1078TcpConnectionHandler>(); | |||||
builder.Services.TryAddScoped<JT1078TcpDecoder>(); | |||||
builder.Services.TryAddSingleton<IJT1078TcpMessageHandlers, JT1078TcpMessageProcessorEmptyImpl>(); | |||||
builder.Services.TryAddScoped<JT1078TcpServerHandler>(); | |||||
builder.Services.AddHostedService<JT1078TcpServerHost>(); | |||||
return new JT1078TcpBuilderDefault(builder); | |||||
} | |||||
} | |||||
} |
@@ -1,94 +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 JT1078.DotNetty.Core.Codecs; | |||||
using JT1078.DotNetty.Core.Configurations; | |||||
using JT1078.DotNetty.Tcp.Handlers; | |||||
using JT1078.Protocol; | |||||
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 JT1078.DotNetty.Tcp | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 Tcp网关服务 | |||||
/// </summary> | |||||
internal class JT1078TcpServerHost : IHostedService | |||||
{ | |||||
private readonly IServiceProvider serviceProvider; | |||||
private readonly JT1078Configuration configuration; | |||||
private readonly ILogger<JT1078TcpServerHost> logger; | |||||
private DispatcherEventLoopGroup bossGroup; | |||||
private WorkerEventLoopGroup workerGroup; | |||||
private IChannel bootstrapChannel; | |||||
private IByteBufferAllocator serverBufferAllocator; | |||||
public JT1078TcpServerHost( | |||||
IServiceProvider provider, | |||||
ILoggerFactory loggerFactory, | |||||
IOptions<JT1078Configuration> configurationAccessor) | |||||
{ | |||||
serviceProvider = provider; | |||||
configuration = configurationAccessor.Value; | |||||
logger=loggerFactory.CreateLogger<JT1078TcpServerHost>(); | |||||
} | |||||
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("JT1078TcpBuffer", new DelimiterBasedFrameDecoder(int.MaxValue,true, | |||||
Unpooled.CopiedBuffer(JT1078Package.FH_Bytes))); | |||||
channel.Pipeline.AddLast("JT1078TcpDecode", scope.ServiceProvider.GetRequiredService<JT1078TcpDecoder>()); | |||||
channel.Pipeline.AddLast("JT1078SystemIdleState", new IdleStateHandler( | |||||
configuration.ReaderIdleTimeSeconds, | |||||
configuration.WriterIdleTimeSeconds, | |||||
configuration.AllIdleTimeSeconds)); | |||||
channel.Pipeline.AddLast("JT1078TcpConnection", scope.ServiceProvider.GetRequiredService<JT1078TcpConnectionHandler>()); | |||||
channel.Pipeline.AddLast("JT1078TcpService", scope.ServiceProvider.GetRequiredService<JT1078TcpServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"JT1078 TCP Server start at {IPAddress.Any}:{configuration.TcpPort}."); | |||||
return bootstrap.BindAsync(configuration.TcpPort) | |||||
.ContinueWith(i => bootstrapChannel = i.Result); | |||||
} | |||||
public async Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
await bootstrapChannel.CloseAsync(); | |||||
var quietPeriod = configuration.QuietPeriodTimeSpan; | |||||
var shutdownTimeout = configuration.ShutdownTimeoutTimeSpan; | |||||
await workerGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); | |||||
await bossGroup.ShutdownGracefullyAsync(quietPeriod, shutdownTimeout); | |||||
} | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<!-- | |||||
https://go.microsoft.com/fwlink/?LinkID=208121. | |||||
--> | |||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<PublishProtocol>FileSystem</PublishProtocol> | |||||
<Configuration>Release</Configuration> | |||||
<Platform>Any CPU</Platform> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<PublishDir>..\..\publish\</PublishDir> | |||||
</PropertyGroup> | |||||
</Project> |
@@ -1,56 +0,0 @@ | |||||
<?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/internalLog.txt" | |||||
internalLogLevel="Debug" > | |||||
<variable name="Directory" value="/data/logs/JT1078.Gateway"/> | |||||
<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="flvencoding" xsi:type="File" | |||||
fileName="${Directory}/flvencoding/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss}:${message}"/> | |||||
<target name="JT1078TcpMessageHandlers" xsi:type="File" | |||||
fileName="${Directory}/JT1078TcpMessageHandlers/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${message}"/> | |||||
<target name="JT1078TcpMessageHandlersHex" xsi:type="File" | |||||
fileName="${Directory}/JT1078TcpMessageHandlersHex/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${message}"/> | |||||
<target name="FlvEncoder" xsi:type="File" | |||||
fileName="${Directory}/FlvEncoder/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${message}"/> | |||||
<target name="JT1078TcpConnectionHandler" xsi:type="File" | |||||
fileName="${Directory}/JT1078TcpConnectionHandler/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${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="Error" maxlevel="Fatal" writeTo="all"/> | |||||
<logger name="FlvEncoding" minlevel="Debug" maxlevel="Fatal" writeTo="flvencoding"/> | |||||
<logger name="JT1078.DotNetty.Tcp.Handlers.JT1078TcpConnectionHandler" minlevel="Debug" maxlevel="Fatal" writeTo="JT1078TcpConnectionHandler,console"/> | |||||
<logger name="JT1078TcpMessageHandlers" minlevel="Debug" maxlevel="Fatal" writeTo="JT1078TcpMessageHandlers"/> | |||||
<logger name="JT1078TcpMessageHandlersHex" minlevel="Debug" maxlevel="Fatal" writeTo="JT1078TcpMessageHandlersHex"/> | |||||
<logger name="FlvEncoder" minlevel="Debug" maxlevel="Fatal" writeTo="FlvEncoder"/> | |||||
</rules> | |||||
</nlog> |
@@ -1,48 +0,0 @@ | |||||
<?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/internalLog.txt" | |||||
internalLogLevel="Debug" > | |||||
<variable name="Directory" value="${basedir}/wwwroot/logs/JT1078.Gateway"/> | |||||
<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="JT1078TcpMessageHandlers" xsi:type="File" | |||||
fileName="${Directory}/JT1078TcpMessageHandlers/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${message}"/> | |||||
<target name="JT1078TcpMessageHandlersHex" xsi:type="File" | |||||
fileName="${Directory}/JT1078TcpMessageHandlersHex/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${message}"/> | |||||
<target name="JT1078TcpConnectionHandler" xsi:type="File" | |||||
fileName="${Directory}/JT1078TcpConnectionHandler/${shortdate}.log" | |||||
layout="${date:format=yyyyMMddHHmmss},${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="Debug" maxlevel="Fatal" writeTo="all,console"/> | |||||
<logger name="JT1078.DotNetty.Tcp.Handlers.JT1078TcpConnectionHandler" minlevel="Debug" maxlevel="Fatal" writeTo="JT1078TcpConnectionHandler,console"/> | |||||
<logger name="JT1078TcpMessageHandlers" minlevel="Debug" maxlevel="Fatal" writeTo="JT1078TcpMessageHandlers,console"/> | |||||
<logger name="JT1078TcpMessageHandlersHex" minlevel="Debug" maxlevel="Fatal" writeTo="JT1078TcpMessageHandlersHex,console"/> | |||||
</rules> | |||||
</nlog> |
@@ -1,18 +0,0 @@ | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Transport.Channels; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Security.Principal; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
public class CustomHttpMiddleware : IHttpMiddleware | |||||
{ | |||||
public void Next(IChannelHandlerContext ctx, IFullHttpRequest req, IPrincipal principal) | |||||
{ | |||||
Console.WriteLine("CustomHttpMiddleware"); | |||||
} | |||||
} | |||||
} |
@@ -1,109 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT1078.Protocol; | |||||
using System.Collections.Concurrent; | |||||
using JT1078.Protocol.Enums; | |||||
using System.Diagnostics; | |||||
using System.IO.Pipes; | |||||
using Newtonsoft.Json; | |||||
using DotNetty.Common.Utilities; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Handlers.Streams; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using System.Net; | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Cors; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using JT1078.DotNetty.TestHosting.HLS; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// -hls_list_size 10 m3u8内部文件内部保留10个集合 | |||||
/// -segment_time 10秒切片 | |||||
/// -hls_wrap 可以让切片文件进行循环 就不会导致产生很多文件了 占用很多空间 | |||||
/// ./ffmpeg -f dshow -i video="USB2.0 PC CAMERA" -hls_wrap 20 -start_number 0 -hls_list_size 10 -f hls "D:\v\sample.m3u8 -segment_time 10" | |||||
/// </summary> | |||||
class FFMPEGHLSHostedService : IHostedService | |||||
{ | |||||
private readonly Process process; | |||||
private const string FileName= "hls_ch1.m3u8"; | |||||
private const string DirectoryName = "hlsvideo"; | |||||
private readonly IWebHost webHost; | |||||
public FFMPEGHLSHostedService() | |||||
{ | |||||
string directoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DirectoryName); | |||||
if (Directory.Exists(directoryPath)) | |||||
{ | |||||
Directory.Delete(directoryPath,true); | |||||
Directory.CreateDirectory(directoryPath); | |||||
} | |||||
else | |||||
{ | |||||
Directory.CreateDirectory(directoryPath); | |||||
} | |||||
string filePath =$"\"{Path.Combine(directoryPath, FileName)}\""; | |||||
process = new Process | |||||
{ | |||||
StartInfo = | |||||
{ | |||||
FileName = @"C:\ffmpeg\bin\ffmpeg.exe", | |||||
Arguments = $@"-f dshow -i video={HardwareCamera.CameraName} -vcodec h264 -hls_wrap 10 -start_number 0 -hls_list_size 10 -f hls {filePath} -segment_time 10", | |||||
UseShellExecute = false, | |||||
CreateNoWindow = true | |||||
} | |||||
}; | |||||
webHost= new WebHostBuilder() | |||||
.ConfigureLogging((_, factory) => | |||||
{ | |||||
factory.SetMinimumLevel(LogLevel.Debug); | |||||
factory.AddConsole(); | |||||
}) | |||||
.ConfigureAppConfiguration((hostingContext, config) => | |||||
{ | |||||
config.SetBasePath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"HLS")); | |||||
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); | |||||
}) | |||||
.UseKestrel(ksOptions => | |||||
{ | |||||
ksOptions.ListenAnyIP(5001); | |||||
}) | |||||
.UseWebRoot(AppDomain.CurrentDomain.BaseDirectory) | |||||
.UseStartup<Startup>() | |||||
.Build(); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
process.Start(); | |||||
webHost.RunAsync(cancellationToken); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
try | |||||
{ | |||||
process.Kill(); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
webHost.WaitForShutdownAsync(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,25 +0,0 @@ | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.StaticFiles; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.TestHosting.HLS | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) | |||||
{ | |||||
//mime | |||||
//https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/DeployingHTTPLiveStreaming/DeployingHTTPLiveStreaming.html | |||||
var Provider = new FileExtensionContentTypeProvider(); | |||||
Provider.Mappings[".m3u8"] = "application/x-mpegURL,vnd.apple.mpegURL"; | |||||
Provider.Mappings[".ts"] = "video/MP2T"; | |||||
app.UseStaticFiles(new StaticFileOptions() | |||||
{ | |||||
ContentTypeProvider = Provider | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,9 +0,0 @@ | |||||
{ | |||||
"Logging": { | |||||
"LogLevel": { | |||||
"Default": "Debug" //Warning | |||||
} | |||||
}, | |||||
"AllowedHosts": "*", | |||||
"AllowedOrigins": "*" | |||||
} |
@@ -1,25 +0,0 @@ | |||||
<!DOCTYPE html> | |||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> | |||||
<head> | |||||
<meta charset="utf-8" /> | |||||
<title>hls demo</title> | |||||
<script src="hls.min.js"></script> | |||||
</head> | |||||
<body> | |||||
<h6>https://poanchen.github.io/blog/2016/11/17/how-to-play-mp4-video-using-hls</h6> | |||||
<video muted="muted" webkit-playsinline="true" autoplay="true" id="player"></video> | |||||
<script type="text/javascript"> | |||||
var video = document.getElementById("player"); | |||||
var videoSrcHls = "/hlsvideo/hls_ch1.m3u8"; | |||||
if (Hls.isSupported()) { | |||||
var hls = new Hls(); | |||||
hls.loadSource(videoSrcHls); | |||||
hls.attachMedia(video); | |||||
hls.on(Hls.Events.MANIFEST_PARSED, function () { | |||||
video.play(); | |||||
}); | |||||
} | |||||
</script> | |||||
</body> | |||||
</html> |
@@ -1,134 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT1078.Protocol; | |||||
using System.Collections.Concurrent; | |||||
using JT1078.Protocol.Enums; | |||||
using System.Diagnostics; | |||||
using System.IO.Pipes; | |||||
using Newtonsoft.Json; | |||||
using DotNetty.Common.Utilities; | |||||
using DotNetty.Codecs.Http; | |||||
using DotNetty.Handlers.Streams; | |||||
using DotNetty.Transport.Channels; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using JT1078.DotNetty.Core.Extensions; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
class FFMPEGHTTPFLVHostedService : IHostedService,IDisposable | |||||
{ | |||||
private readonly Process process; | |||||
private readonly NamedPipeServerStream pipeServerOut; | |||||
private const string PipeNameOut = "demo1serverout"; | |||||
private readonly JT1078HttpSessionManager jT1078HttpSessionManager; | |||||
/// <summary> | |||||
/// 需要缓存flv的第一包数据,当新用户进来先推送第一包的数据 | |||||
/// </summary> | |||||
private byte[] flvFirstPackage; | |||||
private ConcurrentDictionary<string, byte> exists = new ConcurrentDictionary<string, byte>(); | |||||
public FFMPEGHTTPFLVHostedService(JT1078HttpSessionManager jT1078HttpSessionManager) | |||||
{ | |||||
pipeServerOut = new NamedPipeServerStream(PipeNameOut, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous,10240,10240); | |||||
process = new Process | |||||
{ | |||||
StartInfo = | |||||
{ | |||||
FileName = @"C:\ffmpeg\bin\ffmpeg.exe", | |||||
Arguments = $@"-f dshow -i video={HardwareCamera.CameraName} -c copy -f flv -vcodec h264 -y \\.\pipe\{PipeNameOut}", | |||||
UseShellExecute = false, | |||||
CreateNoWindow = true | |||||
} | |||||
}; | |||||
this.jT1078HttpSessionManager = jT1078HttpSessionManager; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
process.Start(); | |||||
Task.Run(() => | |||||
{ | |||||
while (true) | |||||
{ | |||||
try | |||||
{ | |||||
Console.WriteLine("IsConnected>>>" + pipeServerOut.IsConnected); | |||||
if (pipeServerOut.IsConnected) | |||||
{ | |||||
if (pipeServerOut.CanRead) | |||||
{ | |||||
Span<byte> v1 = new byte[2048]; | |||||
var length = pipeServerOut.Read(v1); | |||||
var realValue = v1.Slice(0, length).ToArray(); | |||||
if (realValue.Length <= 0) continue; | |||||
if (flvFirstPackage == null) | |||||
{ | |||||
flvFirstPackage = realValue; | |||||
} | |||||
if (jT1078HttpSessionManager.GetAll().Count() > 0) | |||||
{ | |||||
foreach (var session in jT1078HttpSessionManager.GetAll()) | |||||
{ | |||||
if (!exists.ContainsKey(session.Channel.Id.AsShortText())) | |||||
{ | |||||
session.SendHttpFirstChunkAsync(flvFirstPackage); | |||||
exists.TryAdd(session.Channel.Id.AsShortText(), 0); | |||||
} | |||||
session.SendHttpOtherChunkAsync(realValue); | |||||
} | |||||
} | |||||
//Console.WriteLine(JsonConvert.SerializeObject(realValue)+"-"+ length.ToString()); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (!pipeServerOut.IsConnected) | |||||
{ | |||||
Console.WriteLine("WaitForConnection Star..."); | |||||
pipeServerOut.WaitForConnectionAsync().Wait(300); | |||||
Console.WriteLine("WaitForConnection End..."); | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Console.WriteLine(ex); | |||||
} | |||||
} | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
pipeServerOut.Dispose(); | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
try | |||||
{ | |||||
process.Kill(); | |||||
pipeServerOut.Flush(); | |||||
pipeServerOut.Close(); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,26 +0,0 @@ | |||||
<!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: "http://127.0.0.1:1818/demo.flv?token=" + Math.floor((Math.random() * 1000000) + 1) | |||||
}); | |||||
flvPlayer.attachMediaElement(player); | |||||
flvPlayer.load(); | |||||
flvPlayer.play(); | |||||
} | |||||
</script> | |||||
</body> | |||||
</html> |
@@ -1,42 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.DotNetty.TestHosting.JT1078WSFlv; | |||||
using JT1078.Protocol; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.DotNetty.TestHosting.Handlers | |||||
{ | |||||
public class JT1078TcpMessageHandlers : IJT1078TcpMessageHandlers | |||||
{ | |||||
private readonly ILogger logger; | |||||
private readonly ILogger hexLogger; | |||||
private readonly JT1078WSFlvDataService jT1078WSFlvDataService; | |||||
public JT1078TcpMessageHandlers( | |||||
JT1078WSFlvDataService jT1078WSFlvDataServic, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.jT1078WSFlvDataService = jT1078WSFlvDataServic; | |||||
logger = loggerFactory.CreateLogger("JT1078TcpMessageHandlers"); | |||||
hexLogger = loggerFactory.CreateLogger("JT1078TcpMessageHandlersHex"); | |||||
} | |||||
public Task<JT1078Response> Processor(JT1078Request request) | |||||
{ | |||||
logger.LogInformation(JsonConvert.SerializeObject(request.Package)); | |||||
//hexLogger.LogInformation($"{request.Package.SIM},{request.Package.Label3.DataType.ToString()},{request.Package.LastFrameInterval},{request.Package.LastIFrameInterval},{request.Package.Timestamp},{request.Package.SN},{request.Package.LogicChannelNumber},{request.Package.Label3.SubpackageType.ToString()},{ByteBufferUtil.HexDump(request.Src)}"); | |||||
hexLogger.LogInformation($"{request.Package.SIM},{request.Package.SN},{request.Package.LogicChannelNumber},{request.Package.Label3.DataType.ToString()},{request.Package.Label3.SubpackageType.ToString()},{ByteBufferUtil.HexDump(request.Src)}"); | |||||
var mergePackage = JT1078Serializer.Merge(request.Package); | |||||
if (mergePackage != null) | |||||
{ | |||||
jT1078WSFlvDataService.JT1078Packages.Add(mergePackage); | |||||
} | |||||
return Task.FromResult<JT1078Response>(default); | |||||
} | |||||
} | |||||
} |
@@ -1,25 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.Protocol; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.DotNetty.TestHosting.Handlers | |||||
{ | |||||
public class JT1078UdpMessageHandlers : IJT1078UdpMessageHandlers | |||||
{ | |||||
private readonly ILogger<JT1078UdpMessageHandlers> logger; | |||||
public JT1078UdpMessageHandlers(ILoggerFactory loggerFactory) | |||||
{ | |||||
logger = loggerFactory.CreateLogger<JT1078UdpMessageHandlers>(); | |||||
} | |||||
public Task<JT1078Response> Processor(JT1078Request request) | |||||
{ | |||||
logger.LogDebug(JsonConvert.SerializeObject(request.Package)); | |||||
return Task.FromResult<JT1078Response>(default); | |||||
} | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// .\ffmpeg -list_options true -f dshow -i video = "USB2.0 PC CAMERA" | |||||
/// .\ffmpeg -f dshow -i video = "USB2.0 PC CAMERA" -vcodec libx264 "D:\mycamera.flv" | |||||
/// | |||||
/// .\ffmpeg -f dshow -i video = "USB2.0 PC CAMERA" - c copy -f flv -vcodec h264 "rtmp://127.0.0.1/living/streamName" | |||||
/// .\ffplay rtmp://127.0.0.1/living/streamName | |||||
/// | |||||
/// .\ffmpeg -f dshow -i video = "USB2.0 PC CAMERA" - c copy -f -y flv -vcodec h264 "pipe://demoserverout" | |||||
/// | |||||
/// ref:https://www.cnblogs.com/lidabo/p/8662955.html | |||||
/// </summary> | |||||
public static class HardwareCamera | |||||
{ | |||||
public const string CameraName = "\"USB2.0 PC CAMERA\""; | |||||
public const string RTMPURL = "rtmp://127.0.0.1/living/streamName"; | |||||
} | |||||
} |
@@ -1,80 +0,0 @@ | |||||
using System; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
public static partial class BinaryExtensions | |||||
{ | |||||
public static string ToHexString(this byte[] source) | |||||
{ | |||||
return HexUtil.DoHexDump(source, 0, source.Length).ToUpper(); | |||||
} | |||||
/// <summary> | |||||
/// 16进制字符串转16进制数组 | |||||
/// </summary> | |||||
/// <param name="hexString"></param> | |||||
/// <param name="separator"></param> | |||||
/// <returns></returns> | |||||
public static byte[] ToHexBytes(this string hexString) | |||||
{ | |||||
hexString = hexString.Replace(" ", ""); | |||||
byte[] buf = new byte[hexString.Length / 2]; | |||||
ReadOnlySpan<char> readOnlySpan = hexString.AsSpan(); | |||||
for (int i = 0; i < hexString.Length; i++) | |||||
{ | |||||
if (i % 2 == 0) | |||||
{ | |||||
buf[i / 2] = Convert.ToByte(readOnlySpan.Slice(i, 2).ToString(), 16); | |||||
} | |||||
} | |||||
return buf; | |||||
} | |||||
} | |||||
public static class HexUtil | |||||
{ | |||||
static readonly char[] HexdumpTable = new char[256 * 4]; | |||||
static HexUtil() | |||||
{ | |||||
char[] digits = "0123456789ABCDEF".ToCharArray(); | |||||
for (int i = 0; i < 256; i++) | |||||
{ | |||||
HexdumpTable[i << 1] = digits[(int)((uint)i >> 4 & 0x0F)]; | |||||
HexdumpTable[(i << 1) + 1] = digits[i & 0x0F]; | |||||
} | |||||
} | |||||
public static string DoHexDump(ReadOnlySpan<byte> buffer, int fromIndex, int length) | |||||
{ | |||||
if (length == 0) | |||||
{ | |||||
return ""; | |||||
} | |||||
int endIndex = fromIndex + length; | |||||
var buf = new char[length << 1]; | |||||
int srcIdx = fromIndex; | |||||
int dstIdx = 0; | |||||
for (; srcIdx < endIndex; srcIdx++, dstIdx += 2) | |||||
{ | |||||
Array.Copy(HexdumpTable, buffer[srcIdx] << 1, buf, dstIdx, 2); | |||||
} | |||||
return new string(buf); | |||||
} | |||||
public static string DoHexDump(byte[] array, int fromIndex, int length) | |||||
{ | |||||
if (length == 0) | |||||
{ | |||||
return ""; | |||||
} | |||||
int endIndex = fromIndex + length; | |||||
var buf = new char[length << 1]; | |||||
int srcIdx = fromIndex; | |||||
int dstIdx = 0; | |||||
for (; srcIdx < endIndex; srcIdx++, dstIdx += 2) | |||||
{ | |||||
Array.Copy(HexdumpTable, (array[srcIdx] & 0xFF) << 1, buf, dstIdx, 2); | |||||
} | |||||
return new string(buf); | |||||
} | |||||
} | |||||
} |
@@ -1,72 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>netcoreapp3.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="JT1078.Flv" Version="1.0.0-preview2" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" /> | |||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.DotNetty.Http\JT1078.DotNetty.Http.csproj" /> | |||||
<ProjectReference Include="..\JT1078.DotNetty.Tcp\JT1078.DotNetty.Tcp.csproj" /> | |||||
<ProjectReference Include="..\JT1078.DotNetty.Udp\JT1078.DotNetty.Udp.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Reference Include="JT1078.Flv"> | |||||
<HintPath>dll\JT1078.Flv.dll</HintPath> | |||||
</Reference> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Update="appsettings.json"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\nlog.unix.config"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\nlog.win.config"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="Configs\NLog.xsd"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="HLS\appsettings.json"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="HLS\hls.html"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="HLS\hls.min.js"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="HTTPFLV\flv.html"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="HTTPFLV\flv.min.js"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="JT1078WSFlv\flv.min.js"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Update="JT1078WSFlv\index.html"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,17 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Collections.Concurrent; | |||||
using JT1078.Protocol; | |||||
namespace JT1078.DotNetty.TestHosting.JT1078WSFlv | |||||
{ | |||||
public class JT1078WSFlvDataService | |||||
{ | |||||
public JT1078WSFlvDataService() | |||||
{ | |||||
JT1078Packages = new BlockingCollection<JT1078Package>(); | |||||
} | |||||
public BlockingCollection<JT1078Package> JT1078Packages { get; set; } | |||||
} | |||||
} |
@@ -1,105 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Core.Extensions; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT1078.Protocol; | |||||
using System.Collections.Concurrent; | |||||
using JT1078.Protocol.Enums; | |||||
using System.Diagnostics; | |||||
using System.IO.Pipes; | |||||
using Newtonsoft.Json; | |||||
using JT1078.DotNetty.TestHosting.JT1078WSFlv; | |||||
using JT1078.Flv; | |||||
using JT1078.Flv.H264; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
class JT1078WSFlvHostedService : IHostedService | |||||
{ | |||||
private readonly JT1078HttpSessionManager jT1078HttpSessionManager; | |||||
private ConcurrentDictionary<string, byte> exists = new ConcurrentDictionary<string, byte>(); | |||||
private readonly JT1078WSFlvDataService jT1078WSFlvDataService; | |||||
private readonly FlvEncoder FlvEncoder; | |||||
private readonly ILogger logger; | |||||
private readonly ILogger flvEncodingLogger; | |||||
public JT1078WSFlvHostedService( | |||||
ILoggerFactory loggerFactory, | |||||
JT1078WSFlvDataService jT1078WSFlvDataServic, | |||||
JT1078HttpSessionManager jT1078HttpSessionManager) | |||||
{ | |||||
logger = loggerFactory.CreateLogger("JT1078WSFlvHostedService"); | |||||
flvEncodingLogger = loggerFactory.CreateLogger("FlvEncoding"); | |||||
this.jT1078WSFlvDataService = jT1078WSFlvDataServic; | |||||
this.jT1078HttpSessionManager = jT1078HttpSessionManager; | |||||
FlvEncoder = new FlvEncoder(loggerFactory); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
Task.Run(() => | |||||
{ | |||||
try | |||||
{ | |||||
Stopwatch stopwatch = new Stopwatch(); | |||||
foreach (var item in jT1078WSFlvDataService.JT1078Packages.GetConsumingEnumerable()) | |||||
{ | |||||
stopwatch.Start(); | |||||
var flv3 = FlvEncoder.CreateFlvFrame(item); | |||||
stopwatch.Stop(); | |||||
if(flvEncodingLogger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
long times = stopwatch.ElapsedMilliseconds; | |||||
flvEncodingLogger.LogDebug($"flv encoding {times.ToString()}ms"); | |||||
} | |||||
stopwatch.Reset(); | |||||
if (flv3 == null) continue; | |||||
if (jT1078HttpSessionManager.GetAll().Count() > 0) | |||||
{ | |||||
foreach (var session in jT1078HttpSessionManager.GetAll()) | |||||
{ | |||||
if (!exists.ContainsKey(session.Channel.Id.AsShortText())) | |||||
{ | |||||
exists.TryAdd(session.Channel.Id.AsShortText(), 0); | |||||
string key = item.GetKey(); | |||||
//ws-flv | |||||
//session.SendBinaryWebSocketAsync(FlvEncoder.GetFirstFlvFrame(key, flv3)); | |||||
//http-flv | |||||
session.SendHttpFirstChunkAsync(FlvEncoder.GetFirstFlvFrame(key, flv3)); | |||||
continue; | |||||
} | |||||
//ws-flv | |||||
//session.SendBinaryWebSocketAsync(flv3); | |||||
//http-flv | |||||
session.SendHttpOtherChunkAsync(flv3); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Console.WriteLine(ex); | |||||
} | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,41 +0,0 @@ | |||||
<!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:1818/jt1078live?token=" + Math.floor((Math.random() * 1000000) + 1) | |||||
}); | |||||
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> |
@@ -1,89 +0,0 @@ | |||||
using JT1078.DotNetty.Core; | |||||
using JT1078.DotNetty.Tcp; | |||||
using JT1078.DotNetty.TestHosting.Handlers; | |||||
using JT1078.DotNetty.Udp; | |||||
using JT1078.DotNetty.Http; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using Newtonsoft.Json.Converters; | |||||
using NLog.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT1078.DotNetty.TestHosting.JT1078WSFlv; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
class Program | |||||
{ | |||||
static Program() | |||||
{ | |||||
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings(); | |||||
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
setting.Converters.Add(new StringEnumConverter()); | |||||
return setting; | |||||
}); | |||||
} | |||||
static async Task Main(string[] args) | |||||
{ | |||||
//3031636481E2108801123456781001100000016BB392CA7C02800028002E0000000161E1A2BF0098CFC0EE1E17283407788E39A403FDDBD1D546BFB063013F59AC34C97A021AB96A28A42C08 | |||||
var serverHostBuilder = new HostBuilder() | |||||
.ConfigureAppConfiguration((hostingContext, config) => | |||||
{ | |||||
config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); | |||||
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); | |||||
}) | |||||
.ConfigureLogging((context, logging) => | |||||
{ | |||||
if (Environment.OSVersion.Platform == PlatformID.Unix) | |||||
{ | |||||
NLog.LogManager.LoadConfiguration("Configs/nlog.unix.config"); | |||||
} | |||||
else | |||||
{ | |||||
NLog.LogManager.LoadConfiguration("Configs/nlog.win.config"); | |||||
} | |||||
logging.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); | |||||
logging.SetMinimumLevel(LogLevel.Trace); | |||||
}) | |||||
.ConfigureServices((hostContext, services) => | |||||
{ | |||||
services.AddSingleton<JT1078WSFlvDataService>(); | |||||
services.AddSingleton<ILoggerFactory, LoggerFactory>(); | |||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); | |||||
services.AddJT1078Core(hostContext.Configuration) | |||||
.AddJT1078TcpHost() | |||||
.Replace<JT1078TcpMessageHandlers>() | |||||
.Builder() | |||||
//.AddJT1078UdpHost() | |||||
//.Replace<JT1078UdpMessageHandlers>() | |||||
// .Builder() | |||||
.AddJT1078HttpHost() | |||||
//.UseHttpMiddleware<CustomHttpMiddleware>() | |||||
//.Builder() | |||||
; | |||||
//使用ffmpeg工具 | |||||
//1.success | |||||
//services.AddHostedService<FFMPEGRTMPHostedService>(); | |||||
//2.success | |||||
//services.AddHostedService<FFMPEGHTTPFLVHostedService>(); | |||||
//3.success | |||||
//services.AddHostedService<FFMPEGWSFLVPHostedService>(); | |||||
//4.success | |||||
//http://127.0.0.1:5001/HLS/hls.html | |||||
//services.AddHostedService<FFMPEGHLSHostedService>(); | |||||
services.AddHostedService<JT1078WSFlvHostedService>(); | |||||
}); | |||||
await serverHostBuilder.RunConsoleAsync(); | |||||
} | |||||
} | |||||
} |
@@ -1,59 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT1078.Protocol; | |||||
using System.Collections.Concurrent; | |||||
using JT1078.Protocol.Enums; | |||||
using System.Diagnostics; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
/// <summary> | |||||
/// 1.部署 RTMP 服务器 https://github.com/a1q123456/Harmonic | |||||
/// 2.使用ffplay播放器查看 ./ffplay rtmp://127.0.0.1/living/streamName | |||||
/// ref: | |||||
/// https://stackoverflow.com/questions/32157774/ffmpeg-output-pipeing-to-named-windows-pipe | |||||
/// https://mathewsachin.github.io/blog/2017/07/28/ffmpeg-pipe-csharp.html | |||||
/// https://csharp.hotexamples.com/examples/-/NamedPipeServerStream/-/php-namedpipeserverstream-class-examples.html | |||||
/// | |||||
/// ffmpeg pipe作为客户端 | |||||
/// NamedPipeServerStream作为服务端 | |||||
/// </summary> | |||||
class FFMPEGRTMPHostedService : IHostedService | |||||
{ | |||||
private readonly Process process; | |||||
public FFMPEGRTMPHostedService() | |||||
{ | |||||
process = new Process | |||||
{ | |||||
StartInfo = | |||||
{ | |||||
FileName = @"C:\ffmpeg\bin\ffmpeg.exe", | |||||
Arguments = $@"-f dshow -i video={HardwareCamera.CameraName} -c copy -f flv -vcodec h264 {HardwareCamera.RTMPURL}", | |||||
UseShellExecute = false, | |||||
CreateNoWindow = true | |||||
} | |||||
}; | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
process.Start(); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
process.Kill(); | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,129 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Codecs.Http.WebSockets; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Core.Extensions; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using JT1078.Protocol; | |||||
using System.Collections.Concurrent; | |||||
using JT1078.Protocol.Enums; | |||||
using System.Diagnostics; | |||||
using System.IO.Pipes; | |||||
using Newtonsoft.Json; | |||||
namespace JT1078.DotNetty.TestHosting | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
class FFMPEGWSFLVHostedService : IHostedService,IDisposable | |||||
{ | |||||
private readonly Process process; | |||||
private readonly NamedPipeServerStream pipeServerOut; | |||||
private const string PipeNameOut = "demo2serverout"; | |||||
private readonly JT1078HttpSessionManager jT1078HttpSessionManager; | |||||
/// <summary> | |||||
/// 需要缓存flv的第一包数据,当新用户进来先推送第一包的数据 | |||||
/// </summary> | |||||
private byte[] flvFirstPackage; | |||||
private ConcurrentDictionary<string,byte> exists = new ConcurrentDictionary<string, byte>(); | |||||
public FFMPEGWSFLVHostedService( | |||||
JT1078HttpSessionManager jT1078HttpSessionManager) | |||||
{ | |||||
pipeServerOut = new NamedPipeServerStream(PipeNameOut, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous,102400,102400); | |||||
process = new Process | |||||
{ | |||||
StartInfo = | |||||
{ | |||||
FileName = @"C:\ffmpeg\bin\ffmpeg.exe", | |||||
Arguments = $@"-f dshow -i video={HardwareCamera.CameraName} -c copy -f flv -vcodec h264 -y \\.\pipe\{PipeNameOut}", | |||||
UseShellExecute = false, | |||||
CreateNoWindow = true, | |||||
} | |||||
}; | |||||
this.jT1078HttpSessionManager = jT1078HttpSessionManager; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
pipeServerOut.Dispose(); | |||||
} | |||||
public Task StartAsync(CancellationToken cancellationToken) | |||||
{ | |||||
process.Start(); | |||||
Task.Run(() => | |||||
{ | |||||
while (true) | |||||
{ | |||||
try | |||||
{ | |||||
Console.WriteLine("IsConnected>>>" + pipeServerOut.IsConnected); | |||||
if (pipeServerOut.IsConnected) | |||||
{ | |||||
if (pipeServerOut.CanRead) | |||||
{ | |||||
Span<byte> v1 = new byte[2048]; | |||||
var length = pipeServerOut.Read(v1); | |||||
var realValue = v1.Slice(0, length).ToArray(); | |||||
if (realValue.Length <= 0) continue; | |||||
if (flvFirstPackage == null) | |||||
{ | |||||
flvFirstPackage = realValue; | |||||
} | |||||
if (jT1078HttpSessionManager.GetAll().Count() > 0) | |||||
{ | |||||
foreach (var session in jT1078HttpSessionManager.GetAll()) | |||||
{ | |||||
if (!exists.ContainsKey(session.Channel.Id.AsShortText())) | |||||
{ | |||||
session.SendBinaryWebSocketAsync(flvFirstPackage); | |||||
exists.TryAdd(session.Channel.Id.AsShortText(), 0); | |||||
} | |||||
session.SendBinaryWebSocketAsync(realValue); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (!pipeServerOut.IsConnected) | |||||
{ | |||||
Console.WriteLine("WaitForConnection Star..."); | |||||
pipeServerOut.WaitForConnectionAsync().Wait(300); | |||||
Console.WriteLine("WaitForConnection End..."); | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Console.WriteLine(ex); | |||||
} | |||||
} | |||||
}); | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task StopAsync(CancellationToken cancellationToken) | |||||
{ | |||||
try | |||||
{ | |||||
process.Kill(); | |||||
pipeServerOut.Flush(); | |||||
pipeServerOut.Close(); | |||||
} | |||||
catch | |||||
{ | |||||
} | |||||
return Task.CompletedTask; | |||||
} | |||||
} | |||||
} |
@@ -1,26 +0,0 @@ | |||||
<!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:1818/jt1078live?token=" + Math.floor((Math.random() * 1000000) + 1) | |||||
}); | |||||
flvPlayer.attachMediaElement(player); | |||||
flvPlayer.load(); | |||||
flvPlayer.play(); | |||||
} | |||||
</script> | |||||
</body> | |||||
</html> |
@@ -1,23 +0,0 @@ | |||||
{ | |||||
"Logging": { | |||||
"IncludeScopes": false, | |||||
"Debug": { | |||||
"LogLevel": { | |||||
"Default": "Trace" | |||||
} | |||||
}, | |||||
"Console": { | |||||
"LogLevel": { | |||||
"Default": "Trace" | |||||
} | |||||
} | |||||
}, | |||||
"JT1078Configuration": { | |||||
"TcpPort": 1808, | |||||
"UdpPort": 1808, | |||||
"HttpPort": 1818, | |||||
"RemoteServerOptions": { | |||||
} | |||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.Protocol; | |||||
namespace JT1078.DotNetty.Udp.Handlers | |||||
{ | |||||
class JT1078UdpMessageProcessorEmptyImpl : IJT1078UdpMessageHandlers | |||||
{ | |||||
public Task<JT1078Response> Processor(JT1078Request request) | |||||
{ | |||||
return Task.FromResult<JT1078Response>(default); | |||||
} | |||||
} | |||||
} |
@@ -1,70 +0,0 @@ | |||||
using DotNetty.Buffers; | |||||
using DotNetty.Transport.Channels; | |||||
using System; | |||||
using Microsoft.Extensions.Logging; | |||||
using JT1078.DotNetty.Core.Metadata; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Core.Services; | |||||
using JT1078.DotNetty.Core.Enums; | |||||
using JT1078.Protocol; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
namespace JT1078.DotNetty.Udp.Handlers | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 Udp服务端处理程序 | |||||
/// </summary> | |||||
internal class JT1078UdpServerHandler : SimpleChannelInboundHandler<JT1078UdpPackage> | |||||
{ | |||||
private readonly ILogger<JT1078UdpServerHandler> logger; | |||||
private readonly JT1078UdpSessionManager SessionManager; | |||||
private readonly JT1078AtomicCounterService AtomicCounterService; | |||||
private readonly IJT1078UdpMessageHandlers handlers; | |||||
public JT1078UdpServerHandler( | |||||
ILoggerFactory loggerFactory, | |||||
JT1078AtomicCounterServiceFactory atomicCounterServiceFactory, | |||||
IJT1078UdpMessageHandlers handlers, | |||||
JT1078UdpSessionManager sessionManager) | |||||
{ | |||||
this.AtomicCounterService = atomicCounterServiceFactory.Create(JT1078TransportProtocolType.udp); | |||||
this.SessionManager = sessionManager; | |||||
logger = loggerFactory.CreateLogger<JT1078UdpServerHandler>(); | |||||
this.handlers = handlers; | |||||
} | |||||
protected override void ChannelRead0(IChannelHandlerContext ctx, JT1078UdpPackage msg) | |||||
{ | |||||
try | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Trace)) | |||||
{ | |||||
logger.LogTrace("accept package success count<<<" + AtomicCounterService.MsgSuccessCount.ToString()); | |||||
logger.LogTrace("accept msg <<< " + ByteBufferUtil.HexDump(msg.Buffer)); | |||||
} | |||||
JT1078Package package = JT1078Serializer.Deserialize(msg.Buffer); | |||||
AtomicCounterService.MsgSuccessIncrement(); | |||||
SessionManager.TryAdd(ctx.Channel, msg.Sender, package.SIM); | |||||
handlers.Processor(new JT1078Request(package, msg.Buffer)); | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug("accept package success count<<<" + AtomicCounterService.MsgSuccessCount.ToString()); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
AtomicCounterService.MsgFailIncrement(); | |||||
if (logger.IsEnabled(LogLevel.Error)) | |||||
{ | |||||
logger.LogError("accept package fail count<<<" + AtomicCounterService.MsgFailCount.ToString()); | |||||
logger.LogError(ex, "accept msg<<<" + ByteBufferUtil.HexDump(msg.Buffer)); | |||||
} | |||||
} | |||||
} | |||||
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); | |||||
} | |||||
} |
@@ -1,29 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Import Project="..\Version.props" /> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>7.3</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | |||||
<Authors>SmallChi(Koike)</Authors> | |||||
<PackageId>JT1078.DotNetty.Udp</PackageId> | |||||
<Product>JT1078.DotNetty.Udp</Product> | |||||
<Description>基于DotNetty实现的JT1078DotNetty的Udp服务</Description> | |||||
<PackageReleaseNotes>基于DotNetty实现的JT1078DotNetty的Udp服务</PackageReleaseNotes> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT1078DotNetty</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078DotNetty</PackageProjectUrl> | |||||
<licenseUrl>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</licenseUrl> | |||||
<license>https://github.com/SmallChi/JT1078DotNetty/blob/master/LICENSE</license> | |||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<SignAssembly>false</SignAssembly> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||||
<Version>$(JT1078DotNettyPackageVersion)</Version> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,30 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.DotNetty.Udp | |||||
{ | |||||
class JT1078UdpBuilderDefault : IJT1078UdpBuilder | |||||
{ | |||||
public IJT1078Builder Instance { get; } | |||||
public JT1078UdpBuilderDefault(IJT1078Builder builder) | |||||
{ | |||||
Instance = builder; | |||||
} | |||||
public IJT1078Builder Builder() | |||||
{ | |||||
return Instance; | |||||
} | |||||
public IJT1078UdpBuilder Replace<T>() where T : IJT1078UdpMessageHandlers | |||||
{ | |||||
Instance.Services.Replace(new ServiceDescriptor(typeof(IJT1078UdpMessageHandlers), typeof(T), ServiceLifetime.Singleton)); | |||||
return this; | |||||
} | |||||
} | |||||
} |
@@ -1,26 +0,0 @@ | |||||
using JT1078.DotNetty.Core.Codecs; | |||||
using JT1078.DotNetty.Core.Interfaces; | |||||
using JT1078.DotNetty.Core.Session; | |||||
using JT1078.DotNetty.Udp; | |||||
using JT1078.DotNetty.Udp.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||||
using System.Runtime.CompilerServices; | |||||
[assembly: InternalsVisibleTo("JT1078.DotNetty.Udp.Test")] | |||||
namespace JT1078.DotNetty.Udp | |||||
{ | |||||
public static class JT1078UdpDotnettyExtensions | |||||
{ | |||||
public static IJT1078UdpBuilder AddJT1078UdpHost(this IJT1078Builder builder) | |||||
{ | |||||
builder.Services.TryAddSingleton<JT1078UdpSessionManager>(); | |||||
builder.Services.TryAddSingleton<IJT1078UdpMessageHandlers, JT1078UdpMessageProcessorEmptyImpl>(); | |||||
builder.Services.TryAddScoped<JT1078UdpDecoder>(); | |||||
builder.Services.TryAddScoped<JT1078UdpServerHandler>(); | |||||
builder.Services.AddHostedService<JT1078UdpServerHost>(); | |||||
return new JT1078UdpBuilderDefault(builder); | |||||
} | |||||
} | |||||
} |
@@ -1,76 +0,0 @@ | |||||
using DotNetty.Transport.Bootstrapping; | |||||
using DotNetty.Transport.Channels; | |||||
using DotNetty.Transport.Channels.Sockets; | |||||
using JT1078.DotNetty.Core.Codecs; | |||||
using JT1078.DotNetty.Core.Configurations; | |||||
using JT1078.DotNetty.Udp.Handlers; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Net; | |||||
using System.Runtime.InteropServices; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.DotNetty.Udp | |||||
{ | |||||
/// <summary> | |||||
/// JT1078 Udp网关服务 | |||||
/// </summary> | |||||
internal class JT1078UdpServerHost : IHostedService | |||||
{ | |||||
private readonly IServiceProvider serviceProvider; | |||||
private readonly JT1078Configuration configuration; | |||||
private readonly ILogger<JT1078UdpServerHost> logger; | |||||
private MultithreadEventLoopGroup group; | |||||
private IChannel bootstrapChannel; | |||||
public JT1078UdpServerHost( | |||||
IServiceProvider provider, | |||||
ILoggerFactory loggerFactory, | |||||
IOptions<JT1078Configuration> jT808ConfigurationAccessor) | |||||
{ | |||||
serviceProvider = provider; | |||||
configuration = jT808ConfigurationAccessor.Value; | |||||
logger=loggerFactory.CreateLogger<JT1078UdpServerHost>(); | |||||
} | |||||
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.SoBroadcast, true) | |||||
.Handler(new ActionChannelInitializer<IChannel>(channel => | |||||
{ | |||||
IChannelPipeline pipeline = channel.Pipeline; | |||||
using (var scope = serviceProvider.CreateScope()) | |||||
{ | |||||
pipeline.AddLast("JT1078UdpDecoder", scope.ServiceProvider.GetRequiredService<JT1078UdpDecoder>()); | |||||
pipeline.AddLast("JT1078UdpService", scope.ServiceProvider.GetRequiredService<JT1078UdpServerHandler>()); | |||||
} | |||||
})); | |||||
logger.LogInformation($"JT1078 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,13 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<!-- | |||||
https://go.microsoft.com/fwlink/?LinkID=208121. | |||||
--> | |||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<PublishProtocol>FileSystem</PublishProtocol> | |||||
<Configuration>Release</Configuration> | |||||
<Platform>Any CPU</Platform> | |||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<PublishDir>..\..\publish\</PublishDir> | |||||
</PropertyGroup> | |||||
</Project> |
@@ -1,54 +0,0 @@ | |||||
| |||||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||||
# Visual Studio Version 16 | |||||
VisualStudioVersion = 16.0.29009.5 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.Core", "JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj", "{71D35280-24BE-4BF1-AFA4-07DFBC696FA5}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.Tcp", "JT1078.DotNetty.Tcp\JT1078.DotNetty.Tcp.csproj", "{C4B4BBEB-4597-469F-B99A-A9F380DDB8FB}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.TestHosting", "JT1078.DotNetty.TestHosting\JT1078.DotNetty.TestHosting.csproj", "{DCCA6BB7-C2B5-490A-B43F-C28ABEA922D7}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.Udp", "JT1078.DotNetty.Udp\JT1078.DotNetty.Udp.csproj", "{6405D7FA-3B6E-4545-827E-BA13EB5BB268}" | |||||
EndProject | |||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1C26DF6A-2978-46B7-B921-BB7776CC6EE8}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.Http", "JT1078.DotNetty.Http\JT1078.DotNetty.Http.csproj", "{C6B9DB90-8A6C-4285-A03F-C03E2E8DF7CC}" | |||||
EndProject | |||||
Global | |||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||||
Debug|Any CPU = Debug|Any CPU | |||||
Release|Any CPU = Release|Any CPU | |||||
EndGlobalSection | |||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||||
{71D35280-24BE-4BF1-AFA4-07DFBC696FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{71D35280-24BE-4BF1-AFA4-07DFBC696FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{71D35280-24BE-4BF1-AFA4-07DFBC696FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{71D35280-24BE-4BF1-AFA4-07DFBC696FA5}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{C4B4BBEB-4597-469F-B99A-A9F380DDB8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{C4B4BBEB-4597-469F-B99A-A9F380DDB8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{C4B4BBEB-4597-469F-B99A-A9F380DDB8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{C4B4BBEB-4597-469F-B99A-A9F380DDB8FB}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{DCCA6BB7-C2B5-490A-B43F-C28ABEA922D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{DCCA6BB7-C2B5-490A-B43F-C28ABEA922D7}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{DCCA6BB7-C2B5-490A-B43F-C28ABEA922D7}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{DCCA6BB7-C2B5-490A-B43F-C28ABEA922D7}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{6405D7FA-3B6E-4545-827E-BA13EB5BB268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{6405D7FA-3B6E-4545-827E-BA13EB5BB268}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{6405D7FA-3B6E-4545-827E-BA13EB5BB268}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{6405D7FA-3B6E-4545-827E-BA13EB5BB268}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{C6B9DB90-8A6C-4285-A03F-C03E2E8DF7CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{C6B9DB90-8A6C-4285-A03F-C03E2E8DF7CC}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{C6B9DB90-8A6C-4285-A03F-C03E2E8DF7CC}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{C6B9DB90-8A6C-4285-A03F-C03E2E8DF7CC}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | |||||
GlobalSection(SolutionProperties) = preSolution | |||||
HideSolutionNode = FALSE | |||||
EndGlobalSection | |||||
GlobalSection(NestedProjects) = preSolution | |||||
{DCCA6BB7-C2B5-490A-B43F-C28ABEA922D7} = {1C26DF6A-2978-46B7-B921-BB7776CC6EE8} | |||||
EndGlobalSection | |||||
GlobalSection(ExtensibilityGlobals) = postSolution | |||||
SolutionGuid = {07ED058A-2CEB-43FD-8478-7EEC7E56F868} | |||||
EndGlobalSection | |||||
EndGlobal |
@@ -1,5 +0,0 @@ | |||||
<Project> | |||||
<PropertyGroup> | |||||
<JT1078DotNettyPackageVersion>1.0.0-preview3</JT1078DotNettyPackageVersion> | |||||
</PropertyGroup> | |||||
</Project> |