问题
How can setting a custom timeout for BeginConnect async call be accomplished in c#? It is very useful, while connecting to a host with a chance of not listening on given port. Each such call wastes around 15s of time before releasing the Thread.
I have following code, as advised in many stackoverflow answers:
public bool Test()
{
using (var tcp = new TcpClient())
{
var c = tcp.BeginConnect(IPAddress.Parse("8.8.8.8"), 8080, null, null);
var success = c.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!success)
{
Console.WriteLine("Before cleanup");
tcp.Close();
tcp.EndConnect(c);
Console.WriteLine("After cleanup");
throw new Exception("Failed to connect.");
}
}
return true;
}
However this does not work. Indeed after the call, the function enters the "if" switch but it blocks immediately on tcp.Close() call and waits for mentioned 15s. Can it be avoided somehow?
回答1:
I wrote a simple test program, using two different techniques for accomplishing your goal, as well as also testing the exact code you posted. I was unable to reproduce the problem you are describing. Whether I use TcpClient or Socket directly, calling Close() on the object results in the connection operation completing immediately (well, in less than 1/10th of a second, after all the async completion, exception handling, thread synchronization, etc.)
Do note that in the TcpClient case, the TcpClient class seems to have a bug in that it throws NullReferenceException instead of (as one would expect) ObjectDisposedException. This appears to be because TcpClient sets the Client property to null when Close() is called, but then tries to use that value when invoking the completion delegate. Oops.
That means that in your code, the caller would see NullReferenceException instead of the Exception you seem to want to throw. But that doesn't seem like it would cause an actual delay per se.
Here is my test program:
class Program
{
static void Main(string[] args)
{
_TestWithSocket();
_TestWithTcpClient();
try
{
_TestSOCode();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e);
}
}
private static void _TestSOCode()
{
using (var tcp = new TcpClient())
{
var c = tcp.BeginConnect(IPAddress.Parse("8.8.8.8"), 8080, null, null);
var success = c.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!success)
{
Console.WriteLine("Before cleanup");
tcp.Close();
tcp.EndConnect(c);
Console.WriteLine("After cleanup");
throw new Exception("Failed to connect.");
}
}
}
private static void _TestWithTcpClient()
{
TcpClient client = new TcpClient();
object o = new object();
Console.WriteLine("connecting TcpClient...");
client.BeginConnect("8.8.8.8", 8080, asyncResult =>
{
Console.WriteLine("connect completed");
try
{
client.EndConnect(asyncResult);
Console.WriteLine("client connected");
}
catch (NullReferenceException)
{
Console.WriteLine("client closed before connected: NullReferenceException");
}
catch (ObjectDisposedException)
{
Console.WriteLine("client closed before connected: ObjectDisposedException");
}
lock (o) Monitor.Pulse(o);
}, null);
Thread.Sleep(1000);
Stopwatch sw = Stopwatch.StartNew();
client.Close();
lock (o) Monitor.Wait(o);
Console.WriteLine("close took {0:0.00} seconds", sw.Elapsed.TotalSeconds);
Console.WriteLine();
}
private static void _TestWithSocket()
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
object o = new object();
Console.WriteLine("connecting Socket...");
socket.BeginConnect("8.8.8.8", 8080, asyncResult =>
{
Console.WriteLine("connect completed");
try
{
socket.EndConnect(asyncResult);
Console.WriteLine("socket connected");
}
catch (ObjectDisposedException)
{
Console.WriteLine("socket closed before connected");
}
lock (o) Monitor.Pulse(o);
}, null);
Thread.Sleep(1000);
Stopwatch sw = Stopwatch.StartNew();
socket.Close();
lock (o) Monitor.Wait(o);
Console.WriteLine("close took {0:0.00} seconds", sw.Elapsed.TotalSeconds);
Console.WriteLine();
}
}
Unfortunately, you have not provided an actual complete code example demonstrating the problem. If in your environment the above code demonstrates the problem you describe then, since it doesn't do so in my environment, that obviously means there's something about your environment causing the problem. Different OS version, different .NET version, etc.
In that case, you should be specific about the particular aspect of your environment that might be relevant.
If the above code example works as desired, and does not demonstrate the problem you describe, then you simply need to figure out what is in the code you have that is different and causing the problem. In that case, if you still can't actually figure out the problem, you should post a minimal, complete code example that demonstrates the problem.
来源:https://stackoverflow.com/questions/28158892/tcpclient-beginconnect-timeout