Rageline
Rageline

Reputation: 79

C# await TcpClient ConnectAsync/ReadToEndAsync with timeout

I'm building a SOCKS proxy checker using .NET 4.5 and everything works fine except when one of SOCKS proxies is really slow and it takes over 100 seconds to respond. I'd like to timeout those proxies at few stages (ConnectAsync, ReadToEndAsync) especially at ReadToEndAsync because if proxy is slow it hangs.

I've tried everything I was able to find about this, using Cancellation tokens, Task.Wait, NetworkStream.ReadTimeout ( doesn't work.. strange ).. and if I use Task.Wait then I can't use await keyword which makes it synchronous and not async and that beats the whole idea of my tool..

 var socksClient = new Socks5ProxyClient(IP,Port);
                var googleAddress = await Dns.GetHostAddressesAsync("google.com");
                var speedStopwatch = Stopwatch.StartNew();
                using(var socksTcpClient = await socksClient.CreateConnection(googleAddress[0].ToString(),80))
                {

                    if(socksTcpClient.Connected)
                    {
                        using(var socksTcpStream = socksTcpClient.GetStream())
                        {
                            socksTcpStream.ReadTimeout = 5000;
                            socksTcpStream.WriteTimeout = 5000; //these don't work..
                            using (var writer = new StreamWriter(socksTcpStream))
                            {

                                await writer.WriteAsync("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n");
                                await writer.FlushAsync();
                                using (var reader = new StreamReader(socksTcpStream))
                                {
                                    var result = await reader.ReadToEndAsync(); // up to 250 seconds hang on thread that is checking current proxy..



                                    reader.Close();
                                    writer.Close();
                                    socksTcpStream.Close();
                                }
                            }
                        }
                    }
                }

Upvotes: 2

Views: 2049

Answers (2)

Renan Fernandes
Renan Fernandes

Reputation: 157

I had the same issue, using a cancelation token worked for me (.NET 7)

Here's an example for 10 seconds.

reader.ReadToEndAsync(new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token)

Upvotes: 0

usr
usr

Reputation: 171246

Shamefully, async socket IO does not support timeouts. You need to build that yourself. Here is the best approach I know:

Make your entire function not care about timeouts. Disable all of them. Then, start a delay task and when it completes dispose of the socket. This kills all IO that is in flight and effects immediate cancellation.

So you could do:

Task.Delay(TimeSpan.FromSeconds(100)).ContinueWith(_ => socksTcpClient.Dispose());

This leads to an ugly ObjectDisposedException. This is unavoidable.

Probably, you need to cancel the delay in case of success. Otherwise you keep a ton of delay tasks for 100 seconds and they might amount to millions depending on load.

Upvotes: 2

Related Questions