public void SimpleSend(IByteBuffer source, bool bindClient, IByteBufferAllocator allocator, AddressFamily addressFamily, byte[] expectedData, int count)
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return;
            }
            SocketDatagramChannel serverChannel = null;
            IChannel clientChannel = null;
            var      serverGroup   = new MultithreadEventLoopGroup(1);
            var      clientGroup   = new MultithreadEventLoopGroup(1);

            try
            {
                var handler         = new TestHandler(expectedData);
                var serverBootstrap = new Bootstrap();
                serverBootstrap
                .Group(serverGroup)
                .ChannelFactory(() => new SocketDatagramChannel(addressFamily))
                .Option(ChannelOption.Allocator, allocator)
                .Option(ChannelOption.SoBroadcast, true)
                .Option(ChannelOption.IpMulticastLoopDisabled, false)
                .Handler(new ActionChannelInitializer <IChannel>(channel =>
                {
                    channel.Pipeline.AddLast(nameof(SocketDatagramChannelUnicastTest), handler);
                }));

                IPAddress address = NetUtil.GetLoopbackAddress(addressFamily);
                this.Output.WriteLine($"Unicast server binding to:({addressFamily}){address}");
                Task <IChannel> task = serverBootstrap.BindAsync(address, IPEndPoint.MinPort);

                Assert.True(task.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds * 5)),
                            $"Unicast server binding to:({addressFamily}){address} timed out!");

                serverChannel = (SocketDatagramChannel)task.Result;
                var endPoint = (IPEndPoint)serverChannel.LocalAddress;

                var clientBootstrap = new Bootstrap();
                clientBootstrap
                .Group(clientGroup)
                .ChannelFactory(() => new SocketDatagramChannel(addressFamily))
                .Option(ChannelOption.Allocator, allocator)
                .Option(ChannelOption.SoBroadcast, true)
                .Option(ChannelOption.IpMulticastLoopDisabled, false)
                .Handler(new ActionChannelInitializer <IChannel>(channel =>
                {
                    channel.Pipeline.AddLast("Dummy", new NetUtil.DummyHandler());
                }));

                var clientEndPoint = new IPEndPoint(
                    addressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any,
                    IPEndPoint.MinPort);

                clientBootstrap
                .LocalAddress(clientEndPoint)
                .RemoteAddress(new IPEndPoint(address, endPoint.Port));

                if (bindClient)
                {
                    this.Output.WriteLine($"Unicast client binding to:({addressFamily}){address}");
                    task = clientBootstrap.BindAsync(clientEndPoint);

                    Assert.True(task.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds * 5)),
                                $"Unicast client binding to:({clientEndPoint}) timed out!");
                    clientChannel = task.Result;
                }
                else
                {
                    this.Output.WriteLine($"Register client binding to:({addressFamily}){address}");
                    task = (Task <IChannel>)clientBootstrap.RegisterAsync();
                    Assert.True(task.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds)), "Unicast client register timed out!");
                    clientChannel = task.Result;
                }

                for (int i = 0; i < count; i++)
                {
                    var packet = new DatagramPacket((IByteBuffer)source.Retain(), new IPEndPoint(address, endPoint.Port));
                    clientChannel.WriteAndFlushAsync(packet).Wait();
                    Assert.True(handler.WaitForResult());

                    var duplicatedPacket = (DatagramPacket)packet.Duplicate();
                    duplicatedPacket.Retain();
                    clientChannel.WriteAndFlushAsync(duplicatedPacket).Wait();
                    Assert.True(handler.WaitForResult());
                }
            }
            finally
            {
                serverChannel?.CloseAsync().Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds));
                clientChannel?.CloseAsync().Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds));

                source.Release();
                Task.WaitAll(
                    serverGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(5)),
                    clientGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(5)));
            }
        }
        public void Multicast(AddressFamily addressFamily, IByteBufferAllocator allocator)
        {
            SocketDatagramChannel serverChannel = null;
            IChannel         clientChannel      = null;
            var              serverGroup        = new MultithreadEventLoopGroup(1);
            var              clientGroup        = new MultithreadEventLoopGroup(1);
            NetworkInterface loopback           = NetUtil.LoopbackInterface(addressFamily);

            try
            {
                var multicastHandler = new MulticastTestHandler();
                var serverBootstrap  = new Bootstrap();
                serverBootstrap
                .Group(serverGroup)
                .ChannelFactory(() => new SocketDatagramChannel(addressFamily))
                .Option(ChannelOption.Allocator, allocator)
                .Option(ChannelOption.SoReuseaddr, true)
                .Option(ChannelOption.IpMulticastLoopDisabled, false)
                .Handler(new ActionChannelInitializer <IChannel>(channel =>
                {
                    channel.Pipeline.AddLast(nameof(SocketDatagramChannelMulticastTest), multicastHandler);
                }));

                IPAddress address = addressFamily == AddressFamily.InterNetwork
                    ? IPAddress.Loopback : IPAddress.IPv6Loopback;

                this.Output.WriteLine($"Multicast server binding to:({addressFamily}){address}");
                Task <IChannel> task = serverBootstrap.BindAsync(address, IPEndPoint.MinPort);
                Assert.True(task.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds * 5)),
                            $"Multicast server binding to:({addressFamily}){address} timed out!");

                serverChannel = (SocketDatagramChannel)task.Result;
                var serverEndPoint = (IPEndPoint)serverChannel.LocalAddress;

                var clientBootstrap = new Bootstrap();
                clientBootstrap
                .Group(clientGroup)
                .ChannelFactory(() => new SocketDatagramChannel(addressFamily))
                .Option(ChannelOption.Allocator, allocator)
                .Option(ChannelOption.SoReuseaddr, true)
                .Option(ChannelOption.IpMulticastLoopDisabled, false)
                .Handler(new ActionChannelInitializer <IChannel>(channel =>
                {
                    channel.Pipeline.AddLast("Dummy", new NetUtil.DummyHandler());
                }));

                this.Output.WriteLine($"Multicast client binding to:({addressFamily}){address}");

                task = clientBootstrap.BindAsync(address, IPEndPoint.MinPort);
                Assert.True(task.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds * 5)),
                            $"Multicast client binding to:({addressFamily}){address} timed out!");

                clientChannel = (SocketDatagramChannel)task.Result;

                IPAddress multicastAddress = addressFamily == AddressFamily.InterNetwork
                    ? IPAddress.Parse("230.0.0.1") : IPAddress.Parse("ff12::1");
                var  groupAddress = new IPEndPoint(multicastAddress, serverEndPoint.Port);
                Task joinTask     = serverChannel.JoinGroup(groupAddress, loopback);
                Assert.True(joinTask.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds * 5)),
                            $"Multicast server join group {groupAddress} timed out!");

                clientChannel.WriteAndFlushAsync(new DatagramPacket(Unpooled.Buffer().WriteInt(1), groupAddress)).Wait();
                Assert.True(multicastHandler.WaitForResult(), "Multicast server should have receivied the message.");

                Task leaveTask = serverChannel.LeaveGroup(groupAddress, loopback);
                Assert.True(leaveTask.Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds * 5)),
                            $"Multicast server leave group {groupAddress} timed out!");

                // sleep half a second to make sure we left the group
                Task.Delay(DefaultTimeOutInMilliseconds).Wait();

                // we should not receive a message anymore as we left the group before
                clientChannel.WriteAndFlushAsync(new DatagramPacket(Unpooled.Buffer().WriteInt(1), groupAddress)).Wait();
                Assert.False(multicastHandler.WaitForResult(), "Multicast server should not receive the message.");
            }
            finally
            {
                serverChannel?.CloseAsync().Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds));
                clientChannel?.CloseAsync().Wait(TimeSpan.FromMilliseconds(DefaultTimeOutInMilliseconds));

                Task.WaitAll(
                    serverGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                    clientGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));
            }
        }