Timeouts when connecting to TCPListener exposed via ngrok

Viewed 63

I have two worker services acting as TCP client and server. I am trying to send a single byte from the client to server and have the server response with an acknowledgement byte.

Everything works when both are running locally, but when I expose the server via ngrok, the client gets timeouts and can't connect. ngrok diagnose doesn't report any issues.

Using regular sockets instead of TCP wrappers resulted in the same exceptions.

Minimal repro, steps and exceptions follow.

Server

using Serilog;
using System.Net;
using System.Net.Sockets;

namespace TcpServer
{
    public class ServerService : BackgroundService
    {
        private readonly ILogger<ServerService> _logger;

        public ServerService(ILogger<ServerService> logger)
        {
            _logger = logger;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var acknowledgement = new byte[] { 2 };
            var receiveBuffer = new byte[1];
            TcpClient? client = null;
            NetworkStream? stream = null;

            return Task.Run(() =>
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        var server = new TcpListener(new IPAddress(new byte[] { 127, 0, 0, 1 }), 2222);
                        server.Start();
                        Log.Debug("Listening on endpoint {Endpoint}", server.LocalEndpoint);

                        client = server.AcceptTcpClient();
                        Log.Debug("Connection request accepted");

                        var stream = client.GetStream();
                        var received = stream.ReadByte();
                        Log.Information("Message {Message} received", received);

                        stream.Write(acknowledgement, 0, 1);
                        Log.Debug("Acknowledgement sent");

                        stream.Close();
                        client.Close();
                    }
                    catch (Exception ex)
                    {
                        stream?.Close();
                        client?.Close();
                        Log.Error("Exception {Exception} occurred", ex);
                    }
                   
                    stream?.Close();
                    client?.Close();
                    Log.CloseAndFlush();
                    stoppingToken = new CancellationToken(true);
                }
            }, stoppingToken);
        }
    }
}

Client

using Serilog;
using Serilog.Core;
using System.Net;
using System.Net.Sockets;

namespace TcpClient
{
    public class ClientService : BackgroundService
    {
        private readonly ILogger<ClientService> _logger;
        private readonly IConfiguration _configuration;

        public ClientService(ILogger<ClientService> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var messageToSend = new byte[] { 1 };
            var receiveBuffer = new byte[1];
            System.Net.Sockets.TcpClient? client = null;
            NetworkStream? stream = null;

            var noTries = 5;

            return Task.Run(() =>
            {
                while (!stoppingToken.IsCancellationRequested &&
                       noTries-- > 0)
                {
                    try
                    {
                        client = new System.Net.Sockets.TcpClient("localhost", 2222);
                        Log.Debug("Connected to the remote server");

                        stream = client.GetStream();

                        stream.Write(messageToSend);
                        Log.Information("Message sent");

                        stream.Read(receiveBuffer);
                        Log.Debug("Acknowledgement received");
                        noTries = 0;
                    }
                    catch (Exception ex)
                    {
                        stream?.Close();
                        client?.Close();
                        Log.Error("Exception {Exception} occurred", ex);
                    }
                }

                stream?.Close();
                client?.Close();
                Log.CloseAndFlush();
            }, stoppingToken);
        }
    }
}

Steps

  1. Run the server service.
  2. Start ngrok and execute command ngrok tcp 2222.
  3. Hardcode the given hostname and port as client params.
  4. Run the client.

Exception (repeated)

[13:19:42 ERR] Exception System.Net.Internals.SocketExceptionFactory+ExtendedSocketException (10060): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
0 Answers
Related