@@ -10,8 +10,7 @@ namespace JT1078.DotNetty.Core.Configurations | |||
public int TcpPort { get; set; } = 1808; | |||
public int UdpPort { get; set; } = 1808; | |||
public int WebSocketPort { get; set; } = 1818; | |||
public int HttpPort { get; set; } = 1818; | |||
public int QuietPeriodSeconds { get; set; } = 1; | |||
@@ -0,0 +1,13 @@ | |||
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(); | |||
} | |||
} |
@@ -4,9 +4,9 @@ using System.Net; | |||
namespace JT1078.DotNetty.Core.Metadata | |||
{ | |||
public class JT1078WebSocketSession | |||
public class JT1078HttpSession | |||
{ | |||
public JT1078WebSocketSession( | |||
public JT1078HttpSession( | |||
IChannel channel, | |||
string userId) | |||
{ | |||
@@ -16,7 +16,7 @@ namespace JT1078.DotNetty.Core.Metadata | |||
LastActiveTime = DateTime.Now; | |||
} | |||
public JT1078WebSocketSession() { } | |||
public JT1078HttpSession() { } | |||
public string UserId { get; set; } | |||
@@ -9,19 +9,19 @@ using JT1078.DotNetty.Core.Metadata; | |||
namespace JT1078.DotNetty.Core.Session | |||
{ | |||
/// <summary> | |||
/// JT1078 WebSocket会话管理 | |||
/// JT1078 http会话管理 | |||
/// </summary> | |||
public class JT1078WebSocketSessionManager | |||
public class JT1078HttpSessionManager | |||
{ | |||
private readonly ILogger<JT1078WebSocketSessionManager> logger; | |||
private readonly ILogger<JT1078HttpSessionManager> logger; | |||
public JT1078WebSocketSessionManager( | |||
public JT1078HttpSessionManager( | |||
ILoggerFactory loggerFactory) | |||
{ | |||
logger = loggerFactory.CreateLogger<JT1078WebSocketSessionManager>(); | |||
logger = loggerFactory.CreateLogger<JT1078HttpSessionManager>(); | |||
} | |||
private ConcurrentDictionary<string, JT1078WebSocketSession> SessionDict = new ConcurrentDictionary<string,JT1078WebSocketSession>(); | |||
private ConcurrentDictionary<string, JT1078HttpSession> SessionDict = new ConcurrentDictionary<string, JT1078HttpSession>(); | |||
public int SessionCount | |||
{ | |||
@@ -31,14 +31,14 @@ namespace JT1078.DotNetty.Core.Session | |||
} | |||
} | |||
public List<JT1078WebSocketSession> GetSessions(string userId) | |||
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 JT1078WebSocketSession(channel, userId)); | |||
SessionDict.TryAdd(channel.Id.AsShortText(), new JT1078HttpSession(channel, userId)); | |||
if (logger.IsEnabled(LogLevel.Information)) | |||
{ | |||
logger.LogInformation($">>>{userId},{channel.Id.AsShortText()} Channel Connection."); | |||
@@ -55,7 +55,7 @@ namespace JT1078.DotNetty.Core.Session | |||
} | |||
} | |||
} | |||
public IEnumerable<JT1078WebSocketSession> GetAll() | |||
public List<JT1078HttpSession> GetAll() | |||
{ | |||
return SessionDict.Select(s => s.Value).ToList(); | |||
} |
@@ -7,7 +7,7 @@ using System.Security.Claims; | |||
using System.Security.Principal; | |||
using System.Text; | |||
namespace JT1078.DotNetty.WebSocket.Authorization | |||
namespace JT1078.DotNetty.Http.Authorization | |||
{ | |||
class JT1078AuthorizationDefault : IJT1078Authorization | |||
{ |
@@ -14,27 +14,30 @@ using JT1078.DotNetty.Core.Session; | |||
using System.Text.RegularExpressions; | |||
using JT1078.DotNetty.Core.Interfaces; | |||
namespace JT1078.DotNetty.WebSocket.Handlers | |||
namespace JT1078.DotNetty.Http.Handlers | |||
{ | |||
public sealed class JT1078WebSocketServerHandler : SimpleChannelInboundHandler<object> | |||
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 ILogger<JT1078WebSocketServerHandler> logger; | |||
private readonly JT1078HttpSessionManager jT1078HttpSessionManager; | |||
private readonly JT1078WebSocketSessionManager jT1078WebSocketSessionManager; | |||
private readonly IJT1078Authorization iJT1078Authorization; | |||
public JT1078WebSocketServerHandler( | |||
JT1078WebSocketSessionManager jT1078WebSocketSessionManager, | |||
public JT1078HttpServerHandler( | |||
JT1078HttpSessionManager jT1078HttpSessionManager, | |||
IJT1078Authorization iJT1078Authorization, | |||
ILoggerFactory loggerFactory) | |||
{ | |||
this.jT1078WebSocketSessionManager = jT1078WebSocketSessionManager; | |||
this.jT1078HttpSessionManager = jT1078HttpSessionManager; | |||
this.iJT1078Authorization = iJT1078Authorization; | |||
logger = loggerFactory.CreateLogger<JT1078WebSocketServerHandler>(); | |||
logger = loggerFactory.CreateLogger<JT1078HttpServerHandler>(); | |||
} | |||
public override void ChannelInactive(IChannelHandlerContext context) | |||
@@ -43,7 +46,7 @@ namespace JT1078.DotNetty.WebSocket.Handlers | |||
{ | |||
logger.LogInformation(context.Channel.Id.AsShortText()); | |||
} | |||
jT1078WebSocketSessionManager.RemoveSessionByChannel(context.Channel); | |||
jT1078HttpSessionManager.RemoveSessionByChannel(context.Channel); | |||
base.ChannelInactive(context); | |||
} | |||
@@ -69,12 +72,6 @@ namespace JT1078.DotNetty.WebSocket.Handlers | |||
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, BadRequest)); | |||
return; | |||
} | |||
// Allow only GET methods. | |||
if (!Equals(req.Method, HttpMethod.Get)) | |||
{ | |||
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, Forbidden)); | |||
return; | |||
} | |||
if ("/favicon.ico".Equals(req.Uri)) | |||
{ | |||
var res = new DefaultFullHttpResponse(Http11, NotFound); | |||
@@ -83,17 +80,24 @@ namespace JT1078.DotNetty.WebSocket.Handlers | |||
} | |||
if (iJT1078Authorization.Authorization(req, out var principal)) | |||
{ | |||
// Handshake | |||
var wsFactory = new WebSocketServerHandshakerFactory(GetWebSocketLocation(req), null, true, 5 * 1024 * 1024); | |||
this.handshaker = wsFactory.NewHandshaker(req); | |||
if (this.handshaker == null) | |||
if (req.Uri.StartsWith(WebsocketPath)) | |||
{ | |||
WebSocketServerHandshakerFactory.SendUnsupportedVersionResponse(ctx.Channel); | |||
// 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); | |||
} | |||
} | |||
else | |||
{ | |||
this.handshaker.HandshakeAsync(ctx.Channel, req); | |||
jT1078WebSocketSessionManager.TryAdd(principal.Identity.Name, ctx.Channel); | |||
jT1078HttpSessionManager.TryAdd(principal.Identity.Name, ctx.Channel); | |||
} | |||
} | |||
else { | |||
@@ -133,6 +137,8 @@ namespace JT1078.DotNetty.WebSocket.Handlers | |||
// 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(); | |||
@@ -142,17 +148,23 @@ namespace JT1078.DotNetty.WebSocket.Handlers | |||
Task task = ctx.Channel.WriteAndFlushAsync(res); | |||
if (!HttpUtil.IsKeepAlive(req) || res.Status.Code != 200) | |||
{ | |||
task.ContinueWith((t, c) => ((IChannelHandlerContext)c).CloseAsync(), | |||
ctx, TaskContinuationOptions.ExecuteSynchronously); | |||
task.ContinueWith((t, c) => ((IChannelHandlerContext)c).CloseAsync(), ctx, TaskContinuationOptions.ExecuteSynchronously); | |||
} | |||
} | |||
public override void ExceptionCaught(IChannelHandlerContext ctx, Exception e) | |||
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) | |||
{ | |||
logger.LogError(e, ctx.Channel.Id.AsShortText()); | |||
ctx.Channel.WriteAndFlushAsync(new DefaultFullHttpResponse(Http11, InternalServerError)); | |||
jT1078WebSocketSessionManager.RemoveSessionByChannel(ctx.Channel); | |||
ctx.CloseAsync(); | |||
jT1078HttpSessionManager.RemoveSessionByChannel(context.Channel); | |||
return base.CloseAsync(context); | |||
} | |||
static string GetWebSocketLocation(IFullHttpRequest req) |
@@ -1,14 +1,15 @@ | |||
<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.WebSocket</PackageId> | |||
<Product>JT1078.DotNetty.WebSocket</Product> | |||
<Description>基于DotNetty实现的JT1078DotNetty的WebSocket服务</Description> | |||
<PackageReleaseNotes>基于DotNetty实现的JT1078DotNetty的WebSocket服务</PackageReleaseNotes> | |||
<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> | |||
@@ -25,11 +26,10 @@ | |||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj" /> | |||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | |||
<ProjectReference Include="..\JT1078.DotNetty.Core\JT1078.DotNetty.Core.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@@ -0,0 +1,24 @@ | |||
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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
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); | |||
} | |||
} | |||
} |
@@ -8,7 +8,7 @@ using DotNetty.Transport.Channels; | |||
using DotNetty.Transport.Libuv; | |||
using JT1078.DotNetty.Core.Codecs; | |||
using JT1078.DotNetty.Core.Configurations; | |||
using JT1078.DotNetty.WebSocket.Handlers; | |||
using JT1078.DotNetty.Http.Handlers; | |||
using JT1078.Protocol; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Hosting; | |||
@@ -20,28 +20,28 @@ using System.Runtime.InteropServices; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace JT1078.DotNetty.WebSocket | |||
namespace JT1078.DotNetty.Http | |||
{ | |||
/// <summary> | |||
/// JT1078 WebSocket服务 | |||
/// JT1078 http服务 | |||
/// </summary> | |||
internal class JT1078WebSocketServerHost : IHostedService | |||
internal class JT1078HttpServerHost : IHostedService | |||
{ | |||
private readonly JT1078Configuration configuration; | |||
private readonly ILogger<JT1078WebSocketServerHost> logger; | |||
private readonly ILogger<JT1078HttpServerHost> logger; | |||
private DispatcherEventLoopGroup bossGroup; | |||
private WorkerEventLoopGroup workerGroup; | |||
private IChannel bootstrapChannel; | |||
private IByteBufferAllocator serverBufferAllocator; | |||
private readonly IServiceProvider serviceProvider; | |||
public JT1078WebSocketServerHost( | |||
public JT1078HttpServerHost( | |||
IServiceProvider serviceProvider, | |||
ILoggerFactory loggerFactory, | |||
IOptions<JT1078Configuration> configurationAccessor) | |||
{ | |||
this.serviceProvider = serviceProvider; | |||
configuration = configurationAccessor.Value; | |||
logger=loggerFactory.CreateLogger<JT1078WebSocketServerHost>(); | |||
logger=loggerFactory.CreateLogger<JT1078HttpServerHost>(); | |||
} | |||
public Task StartAsync(CancellationToken cancellationToken) | |||
@@ -66,14 +66,15 @@ namespace JT1078.DotNetty.WebSocket | |||
{ | |||
IChannelPipeline pipeline = channel.Pipeline; | |||
pipeline.AddLast(new HttpServerCodec()); | |||
pipeline.AddLast(new HttpObjectAggregator(65536)); | |||
pipeline.AddLast(new HttpObjectAggregator(int.MaxValue)); | |||
pipeline.AddLast("chunkedWriter", new ChunkedWriteHandler<IHttpContent>()); | |||
using (var scope = serviceProvider.CreateScope()) | |||
{ | |||
pipeline.AddLast("JT1078WebSocketServerHandler", scope.ServiceProvider.GetRequiredService<JT1078WebSocketServerHandler>()); | |||
pipeline.AddLast("JT1078HttpServerHandler", scope.ServiceProvider.GetRequiredService<JT1078HttpServerHandler>()); | |||
} | |||
})); | |||
logger.LogInformation($"JT1078 WebSocket Server start at {IPAddress.Any}:{configuration.WebSocketPort}."); | |||
return bootstrap.BindAsync(configuration.WebSocketPort) | |||
logger.LogInformation($"JT1078 Http Server start at {IPAddress.Any}:{configuration.HttpPort}."); | |||
return bootstrap.BindAsync(configuration.HttpPort) | |||
.ContinueWith(i => bootstrapChannel = i.Result); | |||
} | |||
@@ -0,0 +1,101 @@ | |||
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> | |||
/// | |||
/// -segment_time 5秒切片 | |||
/// ./ffmpeg -f dshow -i video="USB2.0 PC CAMERA" -start_number 0 -hls_list_size 0 -f hls "D:\v\sample.m3u8 -segment_time 5" | |||
/// </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 -start_number 0 -hls_list_size 0 -f hls {filePath}", | |||
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) | |||
{ | |||
webHost.WaitForShutdownAsync(); | |||
process.Kill(); | |||
return Task.CompletedTask; | |||
} | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
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 | |||
}); | |||
} | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
{ | |||
"Logging": { | |||
"LogLevel": { | |||
"Default": "Debug" //Warning | |||
} | |||
}, | |||
"AllowedHosts": "*", | |||
"AllowedOrigins": "*" | |||
} |
@@ -0,0 +1,25 @@ | |||
<!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> |
@@ -0,0 +1,175 @@ | |||
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; | |||
namespace JT1078.DotNetty.TestHosting | |||
{ | |||
class FFMPEGHTTPFLVHostedService : IHostedService,IDisposable | |||
{ | |||
private readonly Process process; | |||
private readonly NamedPipeServerStream pipeServerOut; | |||
private const string PipeNameOut = "demo1serverout"; | |||
private static readonly AsciiString ServerName = AsciiString.Cached("JT1078Netty"); | |||
private static readonly AsciiString DateEntity = HttpHeaderNames.Date; | |||
private static readonly AsciiString ServerEntity = HttpHeaderNames.Server; | |||
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 void Dispose() | |||
{ | |||
pipeServerOut.Dispose(); | |||
} | |||
public byte[] Chunk(byte[] data) | |||
{ | |||
byte[] buffer =new byte[4+2+2+ data.Length]; | |||
buffer[0] = (byte)(data.Length >> 24); | |||
buffer[1] = (byte)(data.Length >> 16); | |||
buffer[2] = (byte)(data.Length >> 8); | |||
buffer[3] = (byte)data.Length; | |||
buffer[4]=(byte)'\r'; | |||
buffer[5] = (byte)'\n'; | |||
Array.Copy(data,0, buffer, 7,data.Length); | |||
buffer[buffer.Length - 2] = (byte)'\r'; | |||
buffer[buffer.Length - 1] = (byte)'\n'; | |||
return buffer; | |||
} | |||
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())) | |||
{ | |||
IFullHttpResponse firstRes = new DefaultFullHttpResponse(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.AccessControlAllowOrigin, "*"); | |||
firstRes.Headers.Set(HttpHeaderNames.AccessControlAllowMethods, "GET,POST,HEAD,PUT,DELETE,OPTIONS"); | |||
firstRes.Headers.Set(HttpHeaderNames.AccessControlAllowCredentials, "*"); | |||
firstRes.Headers.Set(HttpHeaderNames.AccessControlAllowHeaders, "origin,range,accept-encoding,referer,Cache-Control,X-Proxy-Authorization,X-Requested-With,Content-Type"); | |||
firstRes.Headers.Set(HttpHeaderNames.AccessControlExposeHeaders, "Server,range,Content-Length,Content-Range"); | |||
firstRes.Headers.Set(HttpHeaderNames.AcceptRanges, "bytes"); | |||
firstRes.Headers.Set(HttpHeaderNames.ContentType, "video/x-flv"); | |||
firstRes.Headers.Set(HttpHeaderNames.Connection, "Keep-Alive"); | |||
//HttpUtil.SetContentLength(firstRes, long.MaxValue); | |||
firstRes.Content.WriteBytes(flvFirstPackage); | |||
session.Channel.WriteAndFlushAsync(firstRes); | |||
exists.TryAdd(session.Channel.Id.AsShortText(), 0); | |||
} | |||
IFullHttpResponse res2 = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK); | |||
res2.Headers.Set(ServerEntity, ServerName); | |||
res2.Headers.Set(DateEntity, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); | |||
res2.Headers.Set(HttpHeaderNames.AccessControlAllowOrigin, "*"); | |||
res2.Headers.Set(HttpHeaderNames.AccessControlAllowMethods, "GET,POST,HEAD,PUT,DELETE,OPTIONS"); | |||
res2.Headers.Set(HttpHeaderNames.AccessControlAllowCredentials, "*"); | |||
res2.Headers.Set(HttpHeaderNames.AccessControlAllowHeaders, "origin,range,accept-encoding,referer,Cache-Control,X-Proxy-Authorization,X-Requested-With,Content-Type"); | |||
res2.Headers.Set(HttpHeaderNames.AccessControlExposeHeaders, "Server,range,Content-Length,Content-Range"); | |||
res2.Headers.Set(HttpHeaderNames.AcceptRanges, "bytes"); | |||
res2.Headers.Set(HttpHeaderNames.ContentType, "video/x-flv"); | |||
res2.Headers.Set(HttpHeaderNames.Connection, "Keep-Alive"); | |||
//HttpUtil.SetContentLength(res2, long.MaxValue); | |||
res2.Content.WriteBytes(realValue); | |||
session.Channel.WriteAndFlushAsync(res2); | |||
} | |||
} | |||
//Console.WriteLine(JsonConvert.SerializeObject(realValue)+"-"+ length.ToString()); | |||
} | |||
} | |||
else | |||
{ | |||
if (!pipeServerOut.IsConnected) | |||
{ | |||
Console.WriteLine("WaitForConnection Star..."); | |||
pipeServerOut.WaitForConnectionAsync(); | |||
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,99 +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; | |||
namespace JT1078.DotNetty.TestHosting | |||
{ | |||
class FFMPEGHTTPFLVPHostedService : BackgroundService | |||
{ | |||
private readonly Process process; | |||
private readonly NamedPipeServerStream pipeServerOut; | |||
private const string PipeNameOut = "demo1serverout"; | |||
public FFMPEGHTTPFLVPHostedService() | |||
{ | |||
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, | |||
RedirectStandardError = true | |||
} | |||
}; | |||
} | |||
public override void Dispose() | |||
{ | |||
try | |||
{ | |||
process.Close(); | |||
pipeServerOut.Flush(); | |||
} | |||
catch | |||
{ | |||
} | |||
process.Dispose(); | |||
pipeServerOut.Dispose(); | |||
base.Dispose(); | |||
} | |||
protected override Task ExecuteAsync(CancellationToken stoppingToken) | |||
{ | |||
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; | |||
Console.WriteLine(JsonConvert.SerializeObject(realValue)+"-"+ length.ToString()); | |||
} | |||
} | |||
else | |||
{ | |||
if (!pipeServerOut.IsConnected) | |||
{ | |||
Console.WriteLine("WaitForConnection Star..."); | |||
pipeServerOut.WaitForConnectionAsync(); | |||
Console.WriteLine("WaitForConnection End..."); | |||
} | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.WriteLine(ex); | |||
} | |||
} | |||
}); | |||
return Task.CompletedTask; | |||
} | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
<!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:1819/demo.flv?token=" + Math.floor((Math.random() * 1000000) + 1) | |||
}); | |||
flvPlayer.attachMediaElement(player); | |||
flvPlayer.load(); | |||
flvPlayer.play(); | |||
} | |||
</script> | |||
</body> | |||
</html> |
@@ -6,6 +6,10 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<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="2.2.0" /> | |||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> | |||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" /> | |||
@@ -15,9 +19,9 @@ | |||
</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" /> | |||
<ProjectReference Include="..\JT1078.DotNetty.WebSocket\JT1078.DotNetty.WebSocket.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
@@ -36,6 +40,21 @@ | |||
<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> | |||
</ItemGroup> | |||
</Project> |
@@ -2,7 +2,7 @@ | |||
using JT1078.DotNetty.Tcp; | |||
using JT1078.DotNetty.TestHosting.Handlers; | |||
using JT1078.DotNetty.Udp; | |||
using JT1078.DotNetty.WebSocket; | |||
using JT1078.DotNetty.Http; | |||
using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||
@@ -64,14 +64,17 @@ namespace JT1078.DotNetty.TestHosting | |||
// .AddJT1078UdpHost() | |||
// .Replace<JT1078UdpMessageHandlers>() | |||
// .Builder() | |||
.AddJT1078WebSocketHost() | |||
.Builder(); | |||
//1.success 7-8s | |||
//.AddJT1078HttpHost() | |||
//.Builder(); | |||
; | |||
//1.success | |||
//services.AddHostedService<FFMPEGRTMPHostedService>(); | |||
//2.test | |||
//services.AddHostedService<FFMPEGHTTPFLVPHostedService>(); | |||
//3.success 6-7s | |||
//services.AddHostedService<FFMPEGHTTPFLVHostedService>(); | |||
//3.success | |||
//services.AddHostedService<FFMPEGWSFLVPHostedService>(); | |||
//4.success | |||
services.AddHostedService<FFMPEGHLSHostedService>(); | |||
}); | |||
await serverHostBuilder.RunConsoleAsync(); | |||
@@ -27,7 +27,7 @@ namespace JT1078.DotNetty.TestHosting | |||
/// ffmpeg pipe作为客户端 | |||
/// NamedPipeServerStream作为服务端 | |||
/// </summary> | |||
class FFMPEGRTMPHostedService : BackgroundService | |||
class FFMPEGRTMPHostedService : IHostedService | |||
{ | |||
private readonly Process process; | |||
public FFMPEGRTMPHostedService() | |||
@@ -44,15 +44,15 @@ namespace JT1078.DotNetty.TestHosting | |||
}; | |||
} | |||
public override void Dispose() | |||
public Task StartAsync(CancellationToken cancellationToken) | |||
{ | |||
process.Dispose(); | |||
base.Dispose(); | |||
process.Start(); | |||
return Task.CompletedTask; | |||
} | |||
protected override Task ExecuteAsync(CancellationToken stoppingToken) | |||
public Task StopAsync(CancellationToken cancellationToken) | |||
{ | |||
process.Start(); | |||
process.Kill(); | |||
return Task.CompletedTask; | |||
} | |||
} | |||
@@ -21,19 +21,19 @@ namespace JT1078.DotNetty.TestHosting | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
class FFMPEGWSFLVPHostedService :BackgroundService | |||
class FFMPEGWSFLVPHostedService : IHostedService,IDisposable | |||
{ | |||
private readonly Process process; | |||
private readonly NamedPipeServerStream pipeServerOut; | |||
private const string PipeNameOut = "demo2serverout"; | |||
private readonly JT1078WebSocketSessionManager jT1078WebSocketSessionManager; | |||
private readonly JT1078HttpSessionManager jT1078HttpSessionManager; | |||
/// <summary> | |||
/// 需要缓存flv的第一包数据,当新用户进来先推送第一包的数据 | |||
/// </summary> | |||
private byte[] flvFirstPackage; | |||
private ConcurrentDictionary<string,byte> exists = new ConcurrentDictionary<string, byte>(); | |||
public FFMPEGWSFLVPHostedService( | |||
JT1078WebSocketSessionManager jT1078WebSocketSessionManager) | |||
JT1078HttpSessionManager jT1078HttpSessionManager) | |||
{ | |||
pipeServerOut = new NamedPipeServerStream(PipeNameOut, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous,102400,102400); | |||
process = new Process | |||
@@ -46,26 +46,15 @@ namespace JT1078.DotNetty.TestHosting | |||
CreateNoWindow = true, | |||
} | |||
}; | |||
this.jT1078WebSocketSessionManager = jT1078WebSocketSessionManager; | |||
this.jT1078HttpSessionManager = jT1078HttpSessionManager; | |||
} | |||
public override void Dispose() | |||
public void Dispose() | |||
{ | |||
try | |||
{ | |||
process.Close(); | |||
pipeServerOut.Flush(); | |||
} | |||
catch | |||
{ | |||
} | |||
process.Dispose(); | |||
pipeServerOut.Dispose(); | |||
base.Dispose(); | |||
} | |||
protected override Task ExecuteAsync(CancellationToken stoppingToken) | |||
public Task StartAsync(CancellationToken cancellationToken) | |||
{ | |||
process.Start(); | |||
Task.Run(() => | |||
@@ -87,16 +76,16 @@ namespace JT1078.DotNetty.TestHosting | |||
{ | |||
flvFirstPackage = realValue; | |||
} | |||
if (jT1078WebSocketSessionManager.GetAll().Count() > 0) | |||
if (jT1078HttpSessionManager.GetAll().Count() > 0) | |||
{ | |||
foreach (var session in jT1078WebSocketSessionManager.GetAll()) | |||
foreach (var session in jT1078HttpSessionManager.GetAll()) | |||
{ | |||
if (!exists.ContainsKey(session.Channel.Id.AsShortText())) | |||
{ | |||
session.Channel.WriteAndFlushAsync(new BinaryWebSocketFrame(Unpooled.WrappedBuffer(flvFirstPackage))); | |||
exists.TryAdd(session.Channel.Id.AsShortText(), 0); | |||
} | |||
session.Channel.WriteAndFlushAsync(new BinaryWebSocketFrame(Unpooled.WrappedBuffer(realValue))); | |||
session.Channel.WriteAndFlushAsync(new BinaryWebSocketFrame(Unpooled.WrappedBuffer(realValue))); | |||
} | |||
} | |||
} | |||
@@ -119,5 +108,21 @@ namespace JT1078.DotNetty.TestHosting | |||
}); | |||
return Task.CompletedTask; | |||
} | |||
public Task StopAsync(CancellationToken cancellationToken) | |||
{ | |||
try | |||
{ | |||
process.Kill(); | |||
pipeServerOut.Flush(); | |||
pipeServerOut.Close(); | |||
} | |||
catch | |||
{ | |||
} | |||
return Task.CompletedTask; | |||
} | |||
} | |||
} |
@@ -15,7 +15,7 @@ | |||
var flvPlayer = flvjs.createPlayer({ | |||
type: 'flv', | |||
isLive: true, | |||
url: "ws://127.0.0.1:1818?token=" + Math.floor((Math.random() * 1000000) + 1) | |||
url: "ws://127.0.0.1:1818/jt1078live?token=" + Math.floor((Math.random() * 1000000) + 1) | |||
}); | |||
flvPlayer.attachMediaElement(player); | |||
flvPlayer.load(); | |||
@@ -16,6 +16,7 @@ | |||
"TcpPort": 1808, | |||
"UdpPort": 1808, | |||
"WebSocketPort": 1818, | |||
"HttpPort": 1819, | |||
"RemoteServerOptions": { | |||
} | |||
@@ -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.WebSocket | |||
{ | |||
class JT1078WebSocketBuilderDefault : IJT1078WebSocketBuilder | |||
{ | |||
public IJT1078Builder Instance { get; } | |||
public JT1078WebSocketBuilderDefault(IJT1078Builder builder) | |||
{ | |||
Instance = builder; | |||
} | |||
public IJT1078Builder Builder() | |||
{ | |||
return Instance; | |||
} | |||
public IJT1078WebSocketBuilder Replace<T>() where T : IJT1078Authorization | |||
{ | |||
Instance.Services.Replace(new ServiceDescriptor(typeof(IJT1078Authorization), 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.WebSocket.Authorization; | |||
using JT1078.DotNetty.WebSocket.Handlers; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||
using System.Runtime.CompilerServices; | |||
namespace JT1078.DotNetty.WebSocket | |||
{ | |||
public static class JT1078WebSocketDotnettyExtensions | |||
{ | |||
public static IJT1078WebSocketBuilder AddJT1078WebSocketHost(this IJT1078Builder builder) | |||
{ | |||
builder.Services.TryAddSingleton<JT1078WebSocketSessionManager>(); | |||
builder.Services.TryAddSingleton<IJT1078Authorization,JT1078AuthorizationDefault>(); | |||
builder.Services.AddScoped<JT1078WebSocketServerHandler>(); | |||
builder.Services.AddHostedService<JT1078WebSocketServerHost>(); | |||
return new JT1078WebSocketBuilderDefault(builder); | |||
} | |||
} | |||
} |
@@ -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> |
@@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.Udp", "JT10 | |||
EndProject | |||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1C26DF6A-2978-46B7-B921-BB7776CC6EE8}" | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.DotNetty.WebSocket", "JT1078.DotNetty.WebSocket\JT1078.DotNetty.WebSocket.csproj", "{55181194-5AED-4C4B-8501-C8A17A3587B1}" | |||
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 | |||
@@ -37,10 +37,10 @@ Global | |||
{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 | |||
{55181194-5AED-4C4B-8501-C8A17A3587B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{55181194-5AED-4C4B-8501-C8A17A3587B1}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{55181194-5AED-4C4B-8501-C8A17A3587B1}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{55181194-5AED-4C4B-8501-C8A17A3587B1}.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 | |||