public static void NatConicityTest()
        {
            var info    = new AddressInfoMessage[2];
            var clients = new Client[2];

            clients[0] = new Client(server_endpoints[0]);
            clients[0].ConnectToServer();

            clients[1] = new Client(server_endpoints[1]);
            clients[1].ReuseEndPoint(clients[0].client.LocalEndPoint);
            clients[1].ConnectToServer();

            info[0] = clients[0].GetMyAddressInfo();
            info[1] = clients[1].GetMyAddressInfo();

            System.Console.WriteLine($"AddressInfo 0: {info[0].ToPrettyString()}");
            System.Console.WriteLine($"AddressInfo 1: {info[1].ToPrettyString()}");

            bool isConic = info[0].PublicEndpoint.Equals(info[1].PublicEndpoint);

            System.Console.WriteLine($"Your nat is {(isConic ? "CONE" : "SIMMETRIC")}");
            System.Console.WriteLine("If public ports and addresses are the same, then your NAT is cone.");
            System.Console.WriteLine("Clients will connect IF ONLY your NAT is CONE.");

            clients[0].client.Close();
            clients[1].client.Close();
        }
        public Socket EstablishOutboundTcp(AddressInfoMessage info)
        {
            Socket private_client = CreateSocket();
            Socket public_client  = CreateSocket();
            Socket listener       = CreateSocket();

            private_client.Bind(client.LocalEndPoint);
            public_client.Bind(client.LocalEndPoint);
            listener.Bind(client.LocalEndPoint);
            listener.Listen(1);

            Func <Task <Socket> >[] funcs = new Func <Task <Socket> >[]
            {
                () => Task.Run(() =>
                {
                    System.Console.WriteLine($"Sending connection request to {info.PrivateEndpoint.Convert()} from {private_client.LocalEndPoint}");
                    private_client.Connect(info.PrivateEndpoint.Convert());
                    return(private_client);
                }),
                () => Task.Run(() =>
                {
                    System.Console.WriteLine($"Sending connection request to {info.PublicEndpoint.Convert()} from {public_client.LocalEndPoint}");
                    public_client.Connect(info.PublicEndpoint.Convert());
                    return(public_client);
                }),
                () => Task.Run(() => listener.Accept())
            };

            Task <Socket>[] tasks = new Task <Socket>[]
            {
                funcs[0](),
                funcs[1](),
                funcs[2]()
            };

            int       num_errors = 0;
            const int max_errors = 10;

            while (true)
            {
                int index = Task.WaitAny(tasks);

                if (tasks[index].IsFaulted)
                {
                    if (++num_errors >= max_errors)
                    {
                        return(null);
                    }
                    else
                    {
                        System.Console.WriteLine($"Task number {index} threw an error. Current number of errors: {num_errors}");
                        tasks[index] = funcs[index]();
                    }
                }
                else
                {
                    System.Console.WriteLine($"Task number {index} succeeded.");
                    return(tasks[index].Result);
                }
            }
        }
 public static string ToPrettyString(this AddressInfoMessage info)
 {
     return($"[{info.Id}] {info.PublicEndpoint.GetAddress()}:{info.PublicEndpoint.Port} | {info.PrivateEndpoint.GetAddress()}:{info.PrivateEndpoint.Port}");
 }