Search code examples
c#.net-coretcpclient

Terribly slow performance of TCPClient


For learning purposes (I want to get firm grasp on HTTP internals) I'm writing simple HTTP server, which would parse request according to rfc7230-7235 and proxy it to my simple backend server.

I'm not aspiring to beat any existing solution, but apparently TCPClient is working very slow for some reason. Direct call to my backend would take in worse scenario just 20ms, when calling it using my simple server would take at least 200ms, which is awful.

Parsing aside, which takes mostly insignificant toll on response time, this is minimal code which would take response and send it as is to backend:

public static async Task Main()
{
    Logger.Info("Start listener.");
    var listener = new TcpListener(IPEndPoint.Parse("0.0.0.0:5000"));
    listener.Start();

    while (true)
    {
        var client = await listener.AcceptTcpClientAsync();

        using var c_stream = client.GetStream();

        // read request
        using var ms = new MemoryStream();
        int i;
        var buffer = new byte[1024];
        while ((i = await c_stream.ReadAsync(buffer, 0, 1024)) != 0)
        {
            await ms.WriteAsync(buffer, 0, i);
            if (i < 1024) break;
        }

        // write whole request as-is to backend
        using var backend = new TcpClient();
        await backend.ConnectAsync(IPEndPoint.Parse("172.21.215.119:3000"));
        var b_stream = backend.GetStream();

        ms.Position = 0;
        ms.WriteTo(b_stream);
        await b_stream.FlushAsync();

        // read output from backend to memory
        ms.Position = 0;
        while ((i = await b_stream.ReadAsync(buffer, 0, 1024)) != 0)
        {
            await ms.WriteAsync(buffer, 0, i);
            if (i < 1024) break;
        }

        // send back to fuckin client
        ms.Position = 0;
        ms.WriteTo(c_stream);
        await c_stream.FlushAsync();
    }
}

I don't know if this is important, but my environment looks like that:

  1. I'm working on windows 10 machine with WSL 2.
  2. Backend is setup on WSL (ubuntu). It's just pet shop on nestjs.
  3. Backend calls mongodb, which is setup in docker environment (also WSL).

In my minimal code example, each request takes 200-250ms, at least on my machine. It isn't that much different from how my actual code works. The biggest difference might be, that I'm spamming tasks for each request and I have a lot of validation related to RFC requirements.

If there are some good resources on how to use TCPClient (or Sockets if necessary) correctly, then I would gladly take it.


Solution

  • Okay, apparently answer is quite... Disturbing...

    I solved this issue by changing call to http://localhost:port to http://127.0.0.1:port.

    Now even my dumb proxy implementation in this example, minimal code takes few milliseconds to run.

    Seems like there is some kind of issue with localhost resolution on windows which honestly is beyond me. Apparently it's not only my problem, some user on reddit (post) that helped me, had the same issue when tried to run code similar to mine.