private async Task<UdpClient> PerformNATPunchthroughInternal(TempSessionWithSecrets userSession, UdpClient specificUdpClient, int timeout)
        {
            var natPunchthroughApi = new NATPunchthroughApi();
            natPunchthroughApi.Configuration.ApiKey["api_key"] = userSession.ApiKey;

            var start = DateTime.UtcNow;

            var udpClient = specificUdpClient ?? new UdpClient();

            while (true)
            {
                NATNegotation negotation;
                try
                {
                    negotation = await natPunchthroughApi.PunchthroughPutAsync(userSession.Id);
                }
                catch (HiveMP.NATPunchthrough.Client.ApiException ex)
                {
                    // There are no NAT punchthrough servers for us to use, send a UDP packet to 
                    // localhost so we get an outbound address and port.
                    var b = new byte[] { (byte)'a' };
                    await udpClient.SendAsync(
                        b,
                        1,
                        "localhost",
                        4242);
                    await Task.Delay(100);

                    return udpClient;
                }

                if (negotation.Port == null)
                {
                    throw new InvalidOperationException();
                }

                await udpClient.SendAsync(
                    negotation.Message,
                    negotation.Message.Length,
                    negotation.Host,
                    negotation.Port.Value);

                await Task.Delay(100);

                if (await natPunchthroughApi.PunchthroughGetAsync(userSession.Id) == true)
                {
                    // NAT punchthrough completed successfully.
                    return udpClient;
                }

                if (timeout > 0 && (DateTime.UtcNow - start).TotalMilliseconds > timeout)
                {
                    throw new TimeoutException("Unable to perform NAT punchthrough before the timeout occurred.");
                }

                await Task.Delay(100);
            }
        }
        private async Task <UdpClient> PerformNATPunchthroughInternal(TempSessionWithSecrets userSession, UdpClient specificUdpClient, int timeout)
        {
            var natPunchthroughApi = new NATPunchthroughApi();

            natPunchthroughApi.Configuration.ApiKey["api_key"] = userSession.ApiKey;

            var start = DateTime.UtcNow;

            var udpClient = specificUdpClient ?? new UdpClient();

            while (true)
            {
                var negotation = await natPunchthroughApi.PunchthroughPutAsync(userSession.Id);

                if (negotation.Port == null)
                {
                    throw new InvalidOperationException();
                }

                await udpClient.SendAsync(
                    negotation.Message,
                    negotation.Message.Length,
                    negotation.Host,
                    negotation.Port.Value);

                await Task.Delay(100);

                if (await natPunchthroughApi.PunchthroughGetAsync(userSession.Id) == true)
                {
                    // NAT punchthrough completed successfully.
                    return(udpClient);
                }

                if (timeout > 0 && (DateTime.UtcNow - start).TotalMilliseconds > timeout)
                {
                    throw new TimeoutException("Unable to perform NAT punchthrough before the timeout occurred.");
                }

                await Task.Delay(100);
            }
        }
        private async Task<UdpClient> PerformNATPunchthroughInternal(TempSessionWithSecrets userSession, UdpClient specificUdpClient, int timeout)
        {
            var natPunchthroughApi = new NATPunchthroughApi();
            natPunchthroughApi.Configuration.ApiKey["api_key"] = userSession.ApiKey;

            var start = DateTime.UtcNow;

            var udpClient = specificUdpClient ?? new UdpClient();

            while (true)
            {
                var negotation = await natPunchthroughApi.PunchthroughPutAsync(userSession.Id);
                if (negotation.Port == null)
                {
                    throw new InvalidOperationException();
                }

                await udpClient.SendAsync(
                    negotation.Message,
                    negotation.Message.Length,
                    negotation.Host,
                    negotation.Port.Value);

                await Task.Delay(100);

                if (await natPunchthroughApi.PunchthroughGetAsync(userSession.Id) == true)
                {
                    // NAT punchthrough completed successfully.
                    return udpClient;
                }

                if (timeout > 0 && (DateTime.UtcNow - start).TotalMilliseconds > timeout)
                {
                    throw new TimeoutException("Unable to perform NAT punchthrough before the timeout occurred.");
                }

                await Task.Delay(100);
            }
        }