Search code examples
c#.nettasktask-parallel-librarytcpclient

How send many requests over a single TCP connection without wait responses? C#


Currently, I'm trying to send many raw requests over a single connection in the faster way. I achieve that using HTTPClient lib but due the need of send raw bytes, I'm using TCPClient.

Some of requisites of this application:

  • 1 connection
  • Submit all of requets in a pipeline, , continuing to send without waiting for each read
  • Send raw requests

What I have of code:

Method to send all requests

private static async Task SendAllRequests(Dictionary<int, string> requestsDictionary)
        {
            var client = new TcpClient();
            var ipAddress = Dns.GetHostEntry("localhost").AddressList[0];

            await client.ConnectAsync("localhost", 44392);
            SslStream sslStream = new SslStream(
                client.GetStream(),
                false,
                null
            );
            await sslStream.AuthenticateAsClientAsync("localhost");

            var tasks = new List<Task<KeyValuePair<int, SslStream>>>();

            foreach (var k in requestsDictionary.Keys)
            {

                tasks.Add(SendStreamAsync(requestString, sslStream));
            }

            var requestsInfo = await Task.WhenAll(tasks);
       }

Method to send and read bytes of a single request

            byte[] buffer = new byte[2048];
            int bytes;
 
            byte[] request = Encoding.UTF8.GetBytes(requestString);
            await sslStream.WriteAsync(request, 0, request.Length);
            await sslStream.FlushAsync();

            do
            {
                bytes = await sslStream.ReadAsync(buffer, 0, buffer.Length);
            } while (bytes == 2048);


Currently behaviour:

scriptbehaviour


Solution

  • If you want to send multiple requests, you literally just: send multiple requests. It isn't clear what the question is here, or what unexpected behaviour you're seeing. However! A few considerations:

    • while you don't need to await each response, you do need to await each write - you can't write concurrently to the same socket from two different contexts (which you do currently)
    • TCP is a stream; there is no inherent framing - all that is guaranteed is that the same bytes will arrive in the right order (or failure, eventually); so: when sending multiple messages, you need to add the framing (so you know where each message starts and ends); this could be, for example, by CR/LF terminators in a text-based protocol, or by a length-prefix in a binary protocol
    • when dealing with this kind of pipeline, you can't read as part of the write (as that would defy the point), so: you'll need a separate read loop that just reads replies, and correlates them back to requests if needed; this might mean a queue if replies are always ordered with regards to requests, or a dictionary if replies can be out of order, keyed by some correlation identifier
    • honestly, this kind of network code is non-trivial