0

I am writing a ConnectionHandler as a part of Kestrel. The idea is that when a client connects, the ConnectionHandler opens a socket with another server in the network, gets a continuous stream of data and forwards them back to the client. In the meantime, the client can also send data to the ConnectionHandler that the latter is constantly forwarding to the other server in the network (opened socket).

public override async Task OnConnectedAsync(ConnectionContext connection)
{
  TcpClient serverSocket = TcpClient(address, port);
  serverSocket.ReceiveTimeout = 10000;
  serverSocket.SendTimeout = 10000;

  NetworkStream dataStream = serverSocket.GetStream();
  dataStream.ReadTimeout = 10000;
  dataStream.WriteTimeout = 10000;

  Stream clientStreamOut = connection.Transport.Output.AsStream();
  Stream clientStreamIn = connection.Transport.Input.AsStream();

  Task dataTask = Task.Run(async () =>
  {
      try
      {
          await dataStream.CopyToAsync(clientStreamOut);
      }
      catch
      {
          await LogsHelper.Log(logStream, LogsHelper.BROKEN_CLIENT_STREAM);
          return;
      }
  }, connection.ConnectionClosed);

  Task clientTask = Task.Run(async () =>
  {
      try
      {
          await clientStreamIn.CopyToAsync(dataStream);
      }
      catch
      {
          await LogsHelper.Log(logStream, LogsHelper.BROKEN_DATA_STREAM);
          return;
      }
  }, connection.ConnectionClosed);
  await Task.WhenAny(dataTask, clientTask);
}

I am encountering 3 issues:

  1. For the socket with the other server, I am using a TcpClient and I use a NetworkStream. Even though I am setting both ReadTimeout and WriteTimeout to 10 seconds, for both TcpClient and NetworkStream, the opened socket is waiting forever, even if the other server in the network does not send any data for 5 minutes.
  2. Setting timeout for clientStreamOut and clientStreamIn (e.g: clientStreamIn.ReadTimeout = 10000;) is also failing with an exception that it's not supported for that particular stream. I was wondering, is it possible somehow to provide a timeout?
  3. When a client connects to the ConnectionHandler, OnConnectedAsync is triggered. The issue with the code comes when a client disconnects (either due to network drop or for whatever reason). Sometimes disconnection of the client is being detected and the session terminates, while other times it hangs forever, even if the client has actually been disconnected. I was expecting that CopyToAsync will throw an exception in case of a disconnection since I assume that CopyToAsync is trying to write, but that's not always the case.

connection.ConnectionClosed is a CancellationToken that comes from OnConnectedAsync, I read here https://github.com/dotnet/runtime/issues/23207 that it can be used in CopyToAsync. However, I am not sure how I can use it. Also, it is worth to mention that I have zero control over the client code.

I am running the app using Docker

FROM mcr.microsoft.com/dotnet/core/sdk:3.1
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
thzois
  • 41
  • 5
  • https://stackoverflow.com/q/12421989/25182 has answers for `ReadAsync` timeouts, but they could be adapted to a `CopyToAsync` scenario. – Nathan Mar 15 '21 at 04:22

1 Answers1

0

The ReadTimeout and WriteTimeout properties only apply to synchronous reads/writes, not asynchronous ones.

For asynchronous code, you'll need to implement your own read timeouts (write timeouts are generally unnecessary). E.g., use Task.Delay and kill the connection if data isn't received in that time.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810