diff --git a/src/SuperSocket.Server/Host/MultipleServerHostBuilder.cs b/src/SuperSocket.Server/Host/MultipleServerHostBuilder.cs index ad269d8b3..b62cb9335 100644 --- a/src/SuperSocket.Server/Host/MultipleServerHostBuilder.cs +++ b/src/SuperSocket.Server/Host/MultipleServerHostBuilder.cs @@ -106,10 +106,10 @@ public static MultipleServerHostBuilder Create(string[] args) return new MultipleServerHostBuilder(args); } - private ServerHostBuilderAdapter CreateServerHostBuilder(Action> hostBuilderDelegate) + private ServerHostBuilderAdapter CreateServerHostBuilder(Action> hostBuilderDelegate, string serverName = null) where TReceivePackage : class { - var hostBuilder = new ServerHostBuilderAdapter(this); + var hostBuilder = new ServerHostBuilderAdapter(this, serverName); hostBuilderDelegate(hostBuilder); return hostBuilder; } @@ -119,11 +119,12 @@ private ServerHostBuilderAdapter CreateServerHostBuilder /// The type of the package received by the server. /// The action to configure the server host builder. + /// The optional server name for keyed service registration. Use this to register multiple instances of the same server type. /// The updated host builder. - public MultipleServerHostBuilder AddServer(Action> hostBuilderDelegate) + public MultipleServerHostBuilder AddServer(Action> hostBuilderDelegate, string serverName = null) where TReceivePackage : class { - var hostBuilder = CreateServerHostBuilder(hostBuilderDelegate); + var hostBuilder = CreateServerHostBuilder(hostBuilderDelegate, serverName); _hostBuilderAdapters.Add(hostBuilder); return this; } @@ -134,12 +135,13 @@ public MultipleServerHostBuilder AddServer(ActionThe type of the package received by the server. /// The type of the pipeline filter. /// The action to configure the server host builder. + /// The optional server name for keyed service registration. Use this to register multiple instances of the same server type. /// The updated host builder. - public MultipleServerHostBuilder AddServer(Action> hostBuilderDelegate) + public MultipleServerHostBuilder AddServer(Action> hostBuilderDelegate, string serverName = null) where TReceivePackage : class where TPipelineFilter : class, IPipelineFilter { - var hostBuilder = CreateServerHostBuilder(hostBuilderDelegate); + var hostBuilder = CreateServerHostBuilder(hostBuilderDelegate, serverName); _hostBuilderAdapters.Add(hostBuilder); hostBuilder.UsePipelineFilter(); return this; @@ -163,13 +165,14 @@ public MultipleServerHostBuilder AddServer(IServerHostBuilderAdapter hostBuilder /// The type of the package received by the server. /// The type of the pipeline filter. /// The action to configure the server host builder. + /// The optional server name for keyed service registration. Use this to register multiple instances of the same server type. /// The updated host builder. - public MultipleServerHostBuilder AddServer(Action> hostBuilderDelegate) + public MultipleServerHostBuilder AddServer(Action> hostBuilderDelegate, string serverName = null) where TReceivePackage : class where TPipelineFilter : class, IPipelineFilter where TSuperSocketService : SuperSocketService { - var hostBuilder = CreateServerHostBuilder(hostBuilderDelegate); + var hostBuilder = CreateServerHostBuilder(hostBuilderDelegate, serverName); _hostBuilderAdapters.Add(hostBuilder); diff --git a/src/SuperSocket.Server/Host/ServerHostBuilderAdapter.cs b/src/SuperSocket.Server/Host/ServerHostBuilderAdapter.cs index 9485d9fdf..5509b15e5 100644 --- a/src/SuperSocket.Server/Host/ServerHostBuilderAdapter.cs +++ b/src/SuperSocket.Server/Host/ServerHostBuilderAdapter.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using SuperSocket.Server.Abstractions; @@ -28,14 +29,18 @@ public class ServerHostBuilderAdapter : SuperSocketHostBuilder< private List _configureContainerActions = new List(); + private string _serverName; + /// /// Initializes a new instance of the class. /// /// The host builder to adapt. - public ServerHostBuilderAdapter(IHostBuilder hostBuilder) + /// The optional server name for keyed service registration. + public ServerHostBuilderAdapter(IHostBuilder hostBuilder, string serverName = null) : base(hostBuilder) { _hostBuilder = hostBuilder; + _serverName = serverName; } /// @@ -57,6 +62,20 @@ protected void ConfigureServer(HostBuilderContext context, IServiceCollection ho { var services = _currentServices; + // If a server name is provided, configure server options and register the server name + if (!string.IsNullOrEmpty(_serverName)) + { + this.ConfigureServerOptions((ctx, options) => + { + return options.GetSection(_serverName); + }); + + services.PostConfigure(options => + { + options.Name = _serverName; + }); + } + CopyGlobalServices(hostServices, services); RegisterBasicServices(context, hostServices, services); @@ -179,7 +198,20 @@ protected override void RegisterHostedService(IServiceCollection { _currentServices.AddSingleton(); _currentServices.AddSingleton(s => s.GetService() as IServerInfo); - servicesInHost.AddHostedService(s => GetHostedService()); + + if (_serverName != null) + { + // Register as keyed service + servicesInHost.AddKeyedSingleton(_serverName, (s, k) => GetHostedService()); + servicesInHost.AddKeyedSingleton(_serverName, (s, k) => GetHostedService()); + servicesInHost.AddKeyedSingleton(_serverName, (s, k) => GetHostedService() as IServerInfo); + servicesInHost.AddSingleton(_ => GetHostedService()); + servicesInHost.AddSingleton(_ => GetHostedService()); + } + else + { + servicesInHost.AddHostedService(s => GetHostedService()); + } } /// diff --git a/test/SuperSocket.Tests/MainTest.cs b/test/SuperSocket.Tests/MainTest.cs index 1ebd3ef49..3bcf60fb5 100644 --- a/test/SuperSocket.Tests/MainTest.cs +++ b/test/SuperSocket.Tests/MainTest.cs @@ -23,13 +23,12 @@ using SuperSocket.Server.Host; using Xunit; -/// -/// Run selected test case by command -/// dotnet test --filter 'FullyQualifiedName=SuperSocket.Tests.SessionTest.TestCloseReason' -/// - namespace SuperSocket.Tests { + /// + /// Run selected test case by command + /// dotnet test --filter 'FullyQualifiedName=SuperSocket.Tests.SessionTest.TestCloseReason' + /// [Trait("Category", "Basic")] public class MainTest : TestClassBase { @@ -50,17 +49,17 @@ public async Task TestSessionCount() { Assert.Equal("TestServer", server.Name); - Assert.True(await server.StartAsync()); + Assert.True(await server.StartAsync(CancellationToken)); OutputHelper.WriteLine("Started."); Assert.Equal(0, server.SessionCount); OutputHelper.WriteLine("SessionCount:" + server.SessionCount); var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetDefaultServerEndPoint()); + await client.ConnectAsync(GetDefaultServerEndPoint(), CancellationToken); OutputHelper.WriteLine("Connected."); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.Equal(1, server.SessionCount); OutputHelper.WriteLine("SessionCount:" + server.SessionCount); @@ -68,15 +67,15 @@ public async Task TestSessionCount() client.Shutdown(SocketShutdown.Both); client.Close(); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.Equal(0, server.SessionCount); OutputHelper.WriteLine("SessionCount:" + server.SessionCount); - await server.StopAsync(); - } + await server.StopAsync(CancellationToken); + } } - + [Fact] public void TestCustomConfigOptions() { @@ -277,14 +276,14 @@ public async Task TestUseHostedService() { Assert.Equal("TestServer", server.Name); - Assert.True(await server.StartAsync()); + Assert.True(await server.StartAsync(CancellationToken)); OutputHelper.WriteLine("Started."); var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetDefaultServerEndPoint()); + await client.ConnectAsync(GetDefaultServerEndPoint(), CancellationToken); OutputHelper.WriteLine("Connected."); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.True(connected); @@ -293,14 +292,94 @@ public async Task TestUseHostedService() client.Shutdown(SocketShutdown.Both); client.Close(); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.False(connected); - await server.StopAsync(); + await server.StopAsync(CancellationToken); } } + [Theory] + [InlineData(typeof(RegularHostConfigurator))] + [InlineData(typeof(SecureHostConfigurator))] + [InlineData(typeof(UdpHostConfigurator))] + [InlineData(typeof(KestralConnectionHostConfigurator))] + public async Task TestHostStartupMinimalApi(Type hostConfiguratorType) + { + var hostConfigurator = CreateObject(hostConfiguratorType); + + var hostBuilder = WebApplication.CreateBuilder() + .AsSuperSocketApplicationBuilder(serverHostBuilder => + serverHostBuilder + .ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); + }) + .AddServer(builder => + { + hostConfigurator.Configure(builder); + + builder + .ConfigureServerOptions((ctx, config) => + { + return config.GetSection("TestServer1"); + }) + .UsePackageHandler(async (IAppSession s, TextPackageInfo p) => + { + await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); + }); + }) + ); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(CancellationToken); + await host.StopAsync(CancellationToken); + } + } + + [Theory] + [InlineData(typeof(RegularHostConfigurator))] + [InlineData(typeof(SecureHostConfigurator))] + [InlineData(typeof(UdpHostConfigurator))] + [InlineData(typeof(KestralConnectionHostConfigurator))] + public async Task TestHostApplicationBuilderStartup(Type hostConfiguratorType) + { + var hostConfigurator = CreateObject(hostConfiguratorType); + + var hostBuilder = Host.CreateApplicationBuilder() + .AsSuperSocketApplicationBuilder(serverHostBuilder => + serverHostBuilder + .ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); + }) + .AddServer(builder => + { + hostConfigurator.Configure(builder); + + builder + .ConfigureServerOptions((ctx, config) => + { + return config.GetSection("TestServer1"); + }) + .UsePackageHandler(async (IAppSession s, TextPackageInfo p) => + { + await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); + }); + }) + ); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(CancellationToken); + await host.StopAsync(CancellationToken); + } + } + [Fact] public async Task TestConfigureSocketOptions() { @@ -328,14 +407,14 @@ public async Task TestConfigureSocketOptions() { Assert.Equal("TestServer", server.Name); - Assert.True(await server.StartAsync()); + Assert.True(await server.StartAsync(CancellationToken)); OutputHelper.WriteLine("Started."); var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetDefaultServerEndPoint()); + await client.ConnectAsync(GetDefaultServerEndPoint(), CancellationToken); OutputHelper.WriteLine("Connected."); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.True(connected); @@ -346,11 +425,11 @@ public async Task TestConfigureSocketOptions() client.Shutdown(SocketShutdown.Both); client.Close(); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.False(connected); - await server.StopAsync(); + await server.StopAsync(CancellationToken); } } @@ -373,7 +452,7 @@ public async Task TestConnectionType(Type hostConfiguratorType, Type connectionT return ValueTask.CompletedTask; }).BuildAsServer() as IServer) { - Assert.True(await server.StartAsync()); + Assert.True(await server.StartAsync(CancellationToken)); Assert.Equal(0, server.SessionCount); using (var socket = CreateClient(hostConfigurator)) @@ -382,7 +461,7 @@ public async Task TestConnectionType(Type hostConfiguratorType, Type connectionT Assert.True(resetEvent.WaitOne(5000)); } - await server.StopAsync(); + await server.StopAsync(CancellationToken); } Assert.IsType(connectionType, connection); @@ -401,22 +480,22 @@ public async Task TestConsoleProtocol(Type hostConfiguratorType) await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); }).BuildAsServer() as IServer) { - Assert.True(await server.StartAsync()); + Assert.True(await server.StartAsync(CancellationToken)); Assert.Equal(0, server.SessionCount); var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(hostConfigurator.GetServerEndPoint()); + await client.ConnectAsync(hostConfigurator.GetServerEndPoint(), CancellationToken); using (var stream = await hostConfigurator.GetClientStream(client)) using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) { await streamWriter.WriteAsync("Hello World\r\n"); - await streamWriter.FlushAsync(); - var line = await streamReader.ReadLineAsync(); + await streamWriter.FlushAsync(CancellationToken); + var line = await streamReader.ReadLineAsync(CancellationToken); Assert.Equal("Hello World", line); } - await server.StopAsync(); + await server.StopAsync(CancellationToken); } } @@ -453,122 +532,11 @@ public async Task TestCloseAfterSend(Type hostConfiguratorType) } } - [Theory] - [InlineData(typeof(RegularHostConfigurator))] - [InlineData(typeof(SecureHostConfigurator))] - [InlineData(typeof(UdpHostConfigurator))] - [InlineData(typeof(KestralConnectionHostConfigurator))] - public async Task TestMultipleHostStartup(Type hostConfiguratorType) - { - var hostConfigurator = CreateObject(hostConfiguratorType); - var hostBuilder = MultipleServerHostBuilder.Create() - .ConfigureAppConfiguration((hostingContext, config) => - { - config.Sources.Clear(); - config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); - }) - .AddServer(builder => - { - hostConfigurator.Configure(builder); - builder - .ConfigureServerOptions((ctx, config) => - { - return config.GetSection("TestServer1"); - }) - .UsePackageHandler(async (IAppSession s, TextPackageInfo p) => - { - await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); - }); - }); - using(var host = hostBuilder.Build()) - { - await host.StartAsync(); - await host.StopAsync(); - } - } - [Theory] - [InlineData(typeof(RegularHostConfigurator))] - [InlineData(typeof(SecureHostConfigurator))] - [InlineData(typeof(UdpHostConfigurator))] - [InlineData(typeof(KestralConnectionHostConfigurator))] - public async Task TestHostStartupMinimalApi(Type hostConfiguratorType) - { - var hostConfigurator = CreateObject(hostConfiguratorType); - var hostBuilder = WebApplication.CreateBuilder() - .AsSuperSocketApplicationBuilder(serverHostBuilder => - serverHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.Sources.Clear(); - config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); - }) - .AddServer(builder => - { - hostConfigurator.Configure(builder); - - builder - .ConfigureServerOptions((ctx, config) => - { - return config.GetSection("TestServer1"); - }) - .UsePackageHandler(async (IAppSession s, TextPackageInfo p) => - { - await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); - }); - }) - ); - - using (var host = hostBuilder.Build()) - { - await host.StartAsync(); - await host.StopAsync(); - } - } - - [Theory] - [InlineData(typeof(RegularHostConfigurator))] - [InlineData(typeof(SecureHostConfigurator))] - [InlineData(typeof(UdpHostConfigurator))] - [InlineData(typeof(KestralConnectionHostConfigurator))] - public async Task TestHostApplicationBuilderStartup(Type hostConfiguratorType) - { - var hostConfigurator = CreateObject(hostConfiguratorType); - - var hostBuilder = Host.CreateApplicationBuilder() - .AsSuperSocketApplicationBuilder(serverHostBuilder => - serverHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.Sources.Clear(); - config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); - }) - .AddServer(builder => - { - hostConfigurator.Configure(builder); - - builder - .ConfigureServerOptions((ctx, config) => - { - return config.GetSection("TestServer1"); - }) - .UsePackageHandler(async (IAppSession s, TextPackageInfo p) => - { - await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); - }); - }) - ); - - using (var host = hostBuilder.Build()) - { - await host.StartAsync(); - await host.StopAsync(); - } - } [Fact] [Trait("Category", "TestServiceProvider")] @@ -580,11 +548,11 @@ public async Task TestServiceProvider() services.AddSingleton(); }).BuildAsServer() as IServer) { - Assert.True(await server.StartAsync()); + Assert.True(await server.StartAsync(CancellationToken)); Assert.IsType(server.ServiceProvider.GetService()); - await server.StopAsync(); + await server.StopAsync(CancellationToken); } } @@ -601,13 +569,13 @@ public async Task TestStartWithDefaultConfig() }) .Build()) { - await host.StartAsync(); + await host.StartAsync(CancellationToken); var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetDefaultServerEndPoint()); + await client.ConnectAsync(GetDefaultServerEndPoint(), CancellationToken); OutputHelper.WriteLine("Connected."); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.Equal("TestServer", server.Name); @@ -617,294 +585,26 @@ public async Task TestStartWithDefaultConfig() client.Shutdown(SocketShutdown.Both); client.Close(); - await Task.Delay(1000); + await Task.Delay(1000, CancellationToken); Assert.Equal(0, server.SessionCount); OutputHelper.WriteLine("SessionCount:" + server.SessionCount); - await host.StopAsync(); - } - } - - class SuperSocketServiceA : SuperSocketService - { - public SuperSocketServiceA(IServiceProvider serviceProvider, IOptions serverOptions) - : base(serviceProvider, serverOptions) - { - + await host.StopAsync(CancellationToken); } } - class SuperSocketServiceB : SuperSocketService - { - public SuperSocketServiceB(IServiceProvider serviceProvider, IOptions serverOptions) - : base(serviceProvider, serverOptions) - { - - } - } - - class MyTestService - { - public string Name { get; set; } - - public int Version { get; set; } = 0; - - public MyTestService() - { - - } - } - - class MyLocalTestService - { - public IServerInfo Server { get; private set; } - - public MyLocalTestService(IServerInfo server) - { - Server = server; - } - } - - [Fact] - public async Task TestMultipleServerHost() - { - var serverName1 = "TestServer1"; - var serverName2 = "TestServer2"; - - var server1 = default(IServer); - var server2 = default(IServer); - - IHostEnvironment actualHostEvn = null; - - var hostBuilder = MultipleServerHostBuilder.Create() - .ConfigureAppConfiguration((hostingContext, config) => - { - actualHostEvn = hostingContext.HostingEnvironment; - config.Sources.Clear(); - config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); - }) - .ConfigureServices((hostingContext, services) => - { - services.AddSingleton(); - }) - .AddServer(builder => - { - builder - .ConfigureServerOptions((ctx, config) => - { - return config.GetSection(serverName1); - }).UseSessionHandler(async (s) => - { - server1 = s.Server as IServer; - await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); - }) - .UseInProcSessionContainer() - .ConfigureServices((ctx, services) => services.AddSingleton()); - }) - .AddServer(builder => - { - builder - .ConfigureServerOptions((ctx, config) => - { - return config.GetSection(serverName2); - }).UseSessionHandler(async (s) => - { - server2 = s.Server as IServer; - await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); - }) - .UseInProcSessionContainer() - .ConfigureServices((ctx, services) => services.AddSingleton()); - }) - .ConfigureLogging((hostCtx, loggingBuilder) => - { - loggingBuilder.AddConsole(); - loggingBuilder.AddDebug(); - }); - - using(var host = hostBuilder.Build()) - { - await host.StartAsync(this.CancellationToken); - - var serviceA = host.Services.GetServices().OfType().FirstOrDefault(); - Assert.NotNull(serviceA); - var serviceB = host.Services.GetServices().OfType().FirstOrDefault(); - Assert.NotNull(serviceB); - Assert.NotNull(serviceA.ServiceProvider.GetService()); - Assert.NotNull(serviceB.ServiceProvider.GetService()); - Assert.NotSame(serviceA.ServiceProvider.GetService(), serviceB.ServiceProvider.GetService()); - var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetDefaultServerEndPoint(), this.CancellationToken); - - using (var stream = new NetworkStream(client)) - using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) - using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) - { - var line = await streamReader.ReadLineAsync(this.CancellationToken); - Assert.Equal(serverName1, line); - } - - Assert.NotNull(server1); - Assert.Same(server1, serviceA); - - client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetAlternativeServerEndPoint(), this.CancellationToken); - - using (var stream = new NetworkStream(client)) - using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) - using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) - { - var line = await streamReader.ReadLineAsync(this.CancellationToken); - Assert.Equal(serverName2, line); - } - Assert.NotNull(server2); - Assert.Same(server2, serviceB); - var hostEnv = server1.ServiceProvider.GetService(); - Assert.NotNull(hostEnv); - Assert.Equal(actualHostEvn.ContentRootPath, hostEnv.ContentRootPath); - - var hostAppLifetime = server1.ServiceProvider.GetService(); - Assert.NotNull(hostAppLifetime); - - var hostLifetime = server1.ServiceProvider.GetService(); - Assert.NotNull(hostLifetime); - - var hostFromServices = server1.ServiceProvider.GetService(); - Assert.NotNull(hostFromServices); - - Assert.NotSame(server1.GetSessionContainer(), server2.GetSessionContainer()); - - var loggerFactory0 = host.Services.GetService(); - var loggerFactory1 = server1.ServiceProvider.GetService(); - var loggerFactory2 = server2.ServiceProvider.GetService(); - - Assert.Equal(loggerFactory0, loggerFactory1); - Assert.Equal(loggerFactory1, loggerFactory2); - - var testService0 = host.Services.GetService(); - testService0.Name = "SameInstance"; - testService0.Version = 1; - - var testService1 = server1.ServiceProvider.GetService(); - Assert.Equal(testService0.Name, testService1.Name); - Assert.Equal(1, testService1.Version); - testService1.Version = 2; - Assert.Same(server1, server1.ServiceProvider.GetService()); - Assert.Same(server1, server1.ServiceProvider.GetService().Server); - - var testService2 = server2.ServiceProvider.GetService(); - Assert.Equal(testService0.Name, testService2.Name); - Assert.Equal(2, testService2.Version); - Assert.Same(server2, server2.ServiceProvider.GetService()); - Assert.Same(server2, server2.ServiceProvider.GetService().Server); - - await host.StopAsync(); - } - } - - [Fact] - public async Task TestMultipleServerHostWithConfigureServerOptions() + class SuperSocketServiceA : SuperSocketService { - var serverName1 = "TestServer1"; - var serverName2 = "TestServer2"; - - var server1 = default(IServer); - var server2 = default(IServer); - - IHostEnvironment actualHostEvn = null; - - var hostBuilder = MultipleServerHostBuilder.Create() - .AddServer(builder => - { - builder - .UseSessionHandler(async (s) => - { - server1 = s.Server as IServer; - await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); - }) - .UseInProcSessionContainer() - .ConfigureServices((ctx, services) => - { - services.AddSingleton(); - services.Configure(options => - { - options.Name = serverName1; - options.Listeners = new List - { - new ListenOptions - { - Port = 4040, - Ip = "Any" - } - }; - - }); - }); - }) - .AddServer(builder => - { - builder - .UseSessionHandler(async (s) => - { - server2 = s.Server as IServer; - await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); - }) - .UseInProcSessionContainer() - .ConfigureServices((ctx, services) => - { - services.AddSingleton(); - services.Configure(options => - { - options.Name = serverName2; - options.Listeners = new List - { - new ListenOptions - { - Port = 4041, - Ip = "Any" - } - }; - }); - }); - }) - .ConfigureLogging((hostCtx, loggingBuilder) => - { - loggingBuilder.AddConsole(); - loggingBuilder.AddDebug(); - }); - - using (var host = hostBuilder.Build()) + public SuperSocketServiceA(IServiceProvider serviceProvider, IOptions serverOptions) + : base(serviceProvider, serverOptions) { - await host.StartAsync(); - - var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetDefaultServerEndPoint()); - - using (var stream = new NetworkStream(client)) - using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) - using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) - { - var line = await streamReader.ReadLineAsync(); - Assert.Equal(serverName1, line); - } - - client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.ConnectAsync(GetAlternativeServerEndPoint()); - - using (var stream = new NetworkStream(client)) - using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) - using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) - { - var line = await streamReader.ReadLineAsync(); - Assert.Equal(serverName2, line); - } - await host.StopAsync(); } } } diff --git a/test/SuperSocket.Tests/MultipleServerHostTest.cs b/test/SuperSocket.Tests/MultipleServerHostTest.cs new file mode 100644 index 000000000..ae24730d4 --- /dev/null +++ b/test/SuperSocket.Tests/MultipleServerHostTest.cs @@ -0,0 +1,805 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using SuperSocket.ProtoBase; +using SuperSocket.Server; +using SuperSocket.Server.Abstractions; +using SuperSocket.Server.Abstractions.Session; +using SuperSocket.Server.Host; +using Xunit; + +namespace SuperSocket.Tests +{ + [Trait("Category", "MultipleServerHost")] + public class MultipleServerHostTest : TestClassBase + { + public MultipleServerHostTest(ITestOutputHelper outputHelper) + : base(outputHelper) + { + + } + + [Theory] + [InlineData(typeof(RegularHostConfigurator))] + [InlineData(typeof(SecureHostConfigurator))] + [InlineData(typeof(UdpHostConfigurator))] + [InlineData(typeof(KestralConnectionHostConfigurator))] + public async Task TestMultipleHostStartup(Type hostConfiguratorType) + { + var hostConfigurator = CreateObject(hostConfiguratorType); + + var hostBuilder = MultipleServerHostBuilder.Create() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); + }) + .AddServer(builder => + { + hostConfigurator.Configure(builder); + + builder + .ConfigureServerOptions((ctx, config) => + { + return config.GetSection("TestServer1"); + }) + .UsePackageHandler(async (IAppSession s, TextPackageInfo p) => + { + await s.SendAsync(Utf8Encoding.GetBytes("Hello World\r\n")); + }); + }); + + using(var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + await host.StopAsync(this.CancellationToken); + } + } + + [Fact] + public async Task TestMultipleServerHost() + { + var serverName1 = "TestServer1"; + var serverName2 = "TestServer2"; + + var server1 = default(IServer); + var server2 = default(IServer); + + IHostEnvironment actualHostEvn = null; + + var hostBuilder = MultipleServerHostBuilder.Create() + .ConfigureAppConfiguration((hostingContext, config) => + { + actualHostEvn = hostingContext.HostingEnvironment; + config.Sources.Clear(); + config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); + }) + .ConfigureServices((hostingContext, services) => + { + services.AddSingleton(); + }) + .AddServer(builder => + { + builder + .ConfigureServerOptions((ctx, config) => + { + return config.GetSection(serverName1); + }).UseSessionHandler(async (s) => + { + server1 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }) + .UseInProcSessionContainer() + .ConfigureServices((ctx, services) => services.AddSingleton()); + }) + .AddServer(builder => + { + builder + .ConfigureServerOptions((ctx, config) => + { + return config.GetSection(serverName2); + }).UseSessionHandler(async (s) => + { + server2 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }) + .UseInProcSessionContainer() + .ConfigureServices((ctx, services) => services.AddSingleton()); + }) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using(var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + var serviceA = host.Services.GetServices().OfType().FirstOrDefault(); + Assert.NotNull(serviceA); + + var serviceB = host.Services.GetServices().OfType().FirstOrDefault(); + Assert.NotNull(serviceB); + + Assert.NotNull(serviceA.ServiceProvider.GetService()); + Assert.NotNull(serviceB.ServiceProvider.GetService()); + Assert.NotSame(serviceA.ServiceProvider.GetService(), serviceB.ServiceProvider.GetService()); + + var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client.ConnectAsync(GetDefaultServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName1, line); + } + + Assert.NotNull(server1); + Assert.Same(server1, serviceA); + + client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client.ConnectAsync(GetAlternativeServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName2, line); + } + + Assert.NotNull(server2); + Assert.Same(server2, serviceB); + + var hostEnv = server1.ServiceProvider.GetService(); + Assert.NotNull(hostEnv); + Assert.Equal(actualHostEvn.ContentRootPath, hostEnv.ContentRootPath); + + var hostAppLifetime = server1.ServiceProvider.GetService(); + Assert.NotNull(hostAppLifetime); + + var hostLifetime = server1.ServiceProvider.GetService(); + Assert.NotNull(hostLifetime); + + var hostFromServices = server1.ServiceProvider.GetService(); + Assert.NotNull(hostFromServices); + + Assert.NotSame(server1.GetSessionContainer(), server2.GetSessionContainer()); + + var loggerFactory0 = host.Services.GetService(); + var loggerFactory1 = server1.ServiceProvider.GetService(); + var loggerFactory2 = server2.ServiceProvider.GetService(); + + Assert.Equal(loggerFactory0, loggerFactory1); + Assert.Equal(loggerFactory1, loggerFactory2); + + var testService0 = host.Services.GetService(); + testService0.Name = "SameInstance"; + testService0.Version = 1; + + var testService1 = server1.ServiceProvider.GetService(); + Assert.Equal(testService0.Name, testService1.Name); + Assert.Equal(1, testService1.Version); + testService1.Version = 2; + Assert.Same(server1, server1.ServiceProvider.GetService()); + Assert.Same(server1, server1.ServiceProvider.GetService().Server); + + var testService2 = server2.ServiceProvider.GetService(); + Assert.Equal(testService0.Name, testService2.Name); + Assert.Equal(2, testService2.Version); + Assert.Same(server2, server2.ServiceProvider.GetService()); + Assert.Same(server2, server2.ServiceProvider.GetService().Server); + + await host.StopAsync(this.CancellationToken); + } + } + + [Fact] + public async Task TestMultipleServerHostWithConfigureServerOptions() + { + var serverName1 = "TestServer1"; + var serverName2 = "TestServer2"; + + var server1 = default(IServer); + var server2 = default(IServer); + + IHostEnvironment actualHostEvn = null; + + var hostBuilder = MultipleServerHostBuilder.Create() + .AddServer(builder => + { + builder + .UseSessionHandler(async (s) => + { + server1 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }) + .UseInProcSessionContainer() + .ConfigureServices((ctx, services) => + { + services.AddSingleton(); + services.Configure(options => + { + options.Name = serverName1; + options.Listeners = new List + { + new ListenOptions + { + Port = 4040, + Ip = "Any" + } + }; + + }); + }); + }) + .AddServer(builder => + { + builder + .UseSessionHandler(async (s) => + { + server2 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }) + .UseInProcSessionContainer() + .ConfigureServices((ctx, services) => + { + services.AddSingleton(); + services.Configure(options => + { + options.Name = serverName2; + options.Listeners = new List + { + new ListenOptions + { + Port = 4041, + Ip = "Any" + } + }; + }); + }); + }) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client.ConnectAsync(GetDefaultServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName1, line); + } + + client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client.ConnectAsync(GetAlternativeServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName2, line); + } + + await host.StopAsync(this.CancellationToken); + } + } + + class SuperSocketServiceA : SuperSocketService + { + public SuperSocketServiceA(IServiceProvider serviceProvider, IOptions serverOptions) + : base(serviceProvider, serverOptions) + { + + } + } + + class SuperSocketServiceB : SuperSocketService + { + public SuperSocketServiceB(IServiceProvider serviceProvider, IOptions serverOptions) + : base(serviceProvider, serverOptions) + { + + } + } + + class MyTestService + { + public string Name { get; set; } + + public int Version { get; set; } = 0; + + public MyTestService() + { + + } + } + + class MyLocalTestService + { + public IServerInfo Server { get; private set; } + + public MyLocalTestService(IServerInfo server) + { + Server = server; + } + } + + [Fact] + public async Task TestMultipleServerInstancesWithSameType() + { + var serverName1 = "Server1"; + var serverName2 = "Server2"; + + var server1 = default(IServer); + var server2 = default(IServer); + + var hostBuilder = MultipleServerHostBuilder.Create() + .AddServer(builder => + { + builder + .UseSessionHandler(async (s) => + { + server1 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }) + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Name = serverName1; + options.Listeners = new List + { + new ListenOptions + { + Port = 4040, + Ip = "Any" + } + }; + }); + }); + }, serverName: serverName1) + .AddServer(builder => + { + builder + .UseSessionHandler(async (s) => + { + server2 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }) + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Name = serverName2; + options.Listeners = new List + { + new ListenOptions + { + Port = 4041, + Ip = "Any" + } + }; + }); + }); + }, serverName: serverName2) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + var servers = host.Services.GetServices(); + var hostedServices = host.Services.GetServices(); + + Assert.Equal(2, servers.Count()); + Assert.Equal(2, hostedServices.Count()); + + foreach (var server in servers) + { + Assert.Equal(ServerState.Started, server.State); + } + + // Test first server instance + var client1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client1.ConnectAsync(GetDefaultServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client1)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName1, line); + } + + Assert.NotNull(server1); + Assert.Equal(serverName1, server1.Name); + + // Test second server instance + var client2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client2.ConnectAsync(GetAlternativeServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client2)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName2, line); + } + + Assert.NotNull(server2); + Assert.Equal(serverName2, server2.Name); + + // Verify both are same type but different instances + Assert.IsType(server1); + Assert.IsType(server2); + Assert.NotSame(server1, server2); + + await host.StopAsync(this.CancellationToken); + } + } + + [Fact] + public async Task TestMultipleServerInstancesWithKeyedServices() + { + var serverName1 = "KeyedServer1"; + var serverName2 = "KeyedServer2"; + + var hostBuilder = MultipleServerHostBuilder.Create() + .AddServer(builder => + { + builder + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Listeners = new List + { + new ListenOptions + { + Port = 4040, + Ip = "Any" + } + }; + }); + }); + }, serverName: serverName1) + .AddServer(builder => + { + builder + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Listeners = new List + { + new ListenOptions + { + Port = 4041, + Ip = "Any" + } + }; + }); + }); + }, serverName: serverName2) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + // Retrieve servers by their keyed names + var server1 = host.Services.GetKeyedService(serverName1); + Assert.NotNull(server1); + Assert.Equal(serverName1, server1.Name); + + var server2 = host.Services.GetKeyedService(serverName2); + Assert.NotNull(server2); + Assert.Equal(serverName2, server2.Name); + + // Verify they are different instances + Assert.NotSame(server1, server2); + + // Retrieve as IServerInfo + var serverInfo1 = host.Services.GetKeyedService(serverName1); + Assert.NotNull(serverInfo1); + Assert.Same(server1, serverInfo1); + + var serverInfo2 = host.Services.GetKeyedService(serverName2); + Assert.NotNull(serverInfo2); + Assert.Same(server2, serverInfo2); + + await host.StopAsync(this.CancellationToken); + } + } + + [Fact] + public async Task TestMultipleServerInstancesWithIndependentConfigurations() + { + var serverName1 = "ConfigServer1"; + var serverName2 = "ConfigServer2"; + + var receivedMessages1 = new List(); + var receivedMessages2 = new List(); + + var hostBuilder = MultipleServerHostBuilder.Create() + .AddServer(builder => + { + builder + .UsePackageHandler(async (s, p) => + { + receivedMessages1.Add(p.Text); + await s.SendAsync(Utf8Encoding.GetBytes($"Echo1: {p.Text}\r\n")); + }) + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Listeners = new List + { + new ListenOptions + { + Port = 4040, + Ip = "Any" + } + }; + }); + }); + }, serverName: serverName1) + .AddServer(builder => + { + builder + .UsePackageHandler(async (s, p) => + { + receivedMessages2.Add(p.Text); + await s.SendAsync(Utf8Encoding.GetBytes($"Echo2: {p.Text}\r\n")); + }) + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Listeners = new List + { + new ListenOptions + { + Port = 4041, + Ip = "Any" + } + }; + }); + }); + }, serverName: serverName2) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + // Test first server + var client1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client1.ConnectAsync(GetDefaultServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client1)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) + { + await streamWriter.WriteAsync("Hello1\r\n"); + await streamWriter.FlushAsync(this.CancellationToken); + var response = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal("Echo1: Hello1", response); + } + + // Test second server + var client2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client2.ConnectAsync(GetAlternativeServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client2)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + using (var streamWriter = new StreamWriter(stream, Utf8Encoding, 1024 * 1024 * 4)) + { + await streamWriter.WriteAsync("Hello2\r\n"); + await streamWriter.FlushAsync(this.CancellationToken); + var response = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal("Echo2: Hello2", response); + } + + // Verify each server received its own messages + Assert.Single(receivedMessages1); + Assert.Equal("Hello1", receivedMessages1[0]); + + Assert.Single(receivedMessages2); + Assert.Equal("Hello2", receivedMessages2[0]); + + await host.StopAsync(this.CancellationToken); + } + } + + [Fact] + public async Task TestMultipleServerInstancesMixedWithAndWithoutServerName() + { + var namedServerName = "NamedServer"; + + var hostBuilder = MultipleServerHostBuilder.Create() + .AddServer(builder => + { + builder + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Name = "UnnamedServer"; + options.Listeners = new List + { + new ListenOptions + { + Port = 4080, + Ip = "Any" + } + }; + }); + }); + }) + .AddServer(builder => + { + builder + .ConfigureServices((ctx, services) => + { + services.Configure(options => + { + options.Listeners = new List + { + new ListenOptions + { + Port = 4081, + Ip = "Any" + } + }; + }); + }); + }, serverName: namedServerName) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + // Unnamed server should be retrievable by type + var unnamedServer = host.Services.GetServices().OfType().FirstOrDefault(); + Assert.NotNull(unnamedServer); + + // Named server should be retrievable by keyed service + var namedServer = host.Services.GetKeyedService(namedServerName); + Assert.NotNull(namedServer); + Assert.Equal(namedServerName, namedServer.Name); + + // Named server should also be retrievable as IServerInfo + var namedServerInfo = host.Services.GetKeyedService(namedServerName); + Assert.NotNull(namedServerInfo); + Assert.Same(namedServer, namedServerInfo); + + await host.StopAsync(this.CancellationToken); + } + } + + [Fact] + public async Task TestAutomaticConfigurationLoadingByServerName() + { + var serverName1 = "TestServer1"; + var serverName2 = "TestServer2"; + + var server1 = default(IServer); + var server2 = default(IServer); + + var hostBuilder = MultipleServerHostBuilder.Create() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + config.AddJsonFile("Config/multiple_server.json", optional: false, reloadOnChange: true); + }) + .AddServer(builder => + { + builder + .UseSessionHandler(async (s) => + { + server1 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }); + }, serverName: serverName1) + .AddServer(builder => + { + builder + .UseSessionHandler(async (s) => + { + server2 = s.Server as IServer; + await s.SendAsync(Utf8Encoding.GetBytes($"{s.Server.Name}\r\n")); + }); + }, serverName: serverName2) + .ConfigureLogging((hostCtx, loggingBuilder) => + { + loggingBuilder.AddConsole(); + loggingBuilder.AddDebug(); + }); + + using (var host = hostBuilder.Build()) + { + await host.StartAsync(this.CancellationToken); + + // Verify server1 loaded configuration automatically and Name is set correctly + var client1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client1.ConnectAsync(GetDefaultServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client1)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName1, line); + } + + Assert.NotNull(server1); + Assert.Equal(serverName1, server1.Name); + + // Verify server2 loaded configuration automatically and Name is set correctly + var client2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await client2.ConnectAsync(GetAlternativeServerEndPoint(), this.CancellationToken); + + using (var stream = new NetworkStream(client2)) + using (var streamReader = new StreamReader(stream, Utf8Encoding, true)) + { + var line = await streamReader.ReadLineAsync(this.CancellationToken); + Assert.Equal(serverName2, line); + } + + Assert.NotNull(server2); + Assert.Equal(serverName2, server2.Name); + + // Verify servers are retrievable by their keyed names + var keyedServer1 = host.Services.GetKeyedService(serverName1); + Assert.NotNull(keyedServer1); + Assert.Same(server1, keyedServer1); + + var keyedServer2 = host.Services.GetKeyedService(serverName2); + Assert.NotNull(keyedServer2); + Assert.Same(server2, keyedServer2); + + await host.StopAsync(this.CancellationToken); + } + } + } +} \ No newline at end of file