【问题标题】:TcpClient BeginConnect timeoutTcpClient BeginConnect 超时
【发布时间】:2015-03-25 09:12:37
【问题描述】:

如何在 c# 中为 BeginConnect 异步调用设置自定义超时? 它非常有用,在连接到有可能不在给定端口上侦听的主机时。每个这样的调用在释放线程之前都会浪费大约 15 秒的时间。

我有以下代码,正如许多 stackoverflow 答案中所建议的那样:

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;
}

但是这不起作用。实际上,在调用之后,该函数进入“if”开关,但它在 tcp.Close() 调用时立即阻塞并等待提到的 15 秒。可以避免吗?

【问题讨论】:

  • How to set the timeout for a TcpClient? 的可能重复项另外,手动处理 using 语句是什么?
  • 感谢您的评论,但是“使用”是否存在并不重要。您的链接解决方案不起作用,并且存在于我的代码中。这是与此处类似的问题:stackoverflow.com/questions/27417990/…
  • 你找到解决办法了吗?
  • 这是卡巴斯基。它会干扰常用端口(例如 8080 和 DNS (53))上的网络流量。尝试卸载并测试。

标签: c# .net sockets timeout


【解决方案1】:

我编写了一个简单的测试程序,使用两种不同的技术来实现您的目标,并测试您发布的确切代码。我无法重现您描述的问题。无论我是直接使用TcpClient还是Socket,在对象上调用Close()都会导致连接操作立即完成(嗯,在不到1/10秒的时间内,在所有异步完成、异常处理、线程同步之后等)

请注意,在 TcpClient 的情况下,TcpClient 类似乎有一个错误,即它抛出 NullReferenceException 而不是(正如人们所期望的)ObjectDisposedException。这似乎是因为TcpClient 在调用Close() 时将Client 属性设置为null,但随后在调用完成委托时尝试使用该值。哎呀。

这意味着在您的代码中,调用者会看到 NullReferenceException 而不是您似乎想要抛出的 Exception。但这似乎不会导致实际的延迟。

这是我的测试程序:

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();
    }
}

很遗憾,您没有提供一个实际完整的代码示例来演示该问题。如果在您的环境中,上面的代码演示了您描述的问题,因为它在我的环境中没有这样做,这显然意味着您的环境有一些问题导致了问题。不同的 OS 版本,不同的 .NET 版本等。

在这种情况下,您应该具体说明您的环境中可能相关的特定方面。

如果上面的代码示例按预期工作,并且没有演示您描述的问题,那么您只需要弄清楚您的代码中有什么不同并导致问题。在这种情况下,如果您仍然无法真正找出问题所在,您应该发帖 a minimal, complete code example 来说明问题。

【讨论】:

  • 仍然无法正常工作。我在项目属性中使用 VS 2013, NET 4.5.1。 Win 8.1 已完全修补。我刚刚运行了您作为控制台项目(发布)提到的代码,我得到了 Socket -> 20s,TcpClient -> 20s,myCode -> 20s。 SS:gyazo.com/fb0d640045c18517d2d7524fa9619efe我也让我朋友运行你的代码,结果一样。
  • 抱歉...听起来您(和您的朋友,现在)正在寻找计算机上的某些特定配置问题。我自己在各种配置上测试了代码,包括与您的配置相匹配的配置以及在一些 Windows 7 机器上,它们都可以正常工作。代码本身很好,只会给您的特定计算机配置留下一些问题(.NET 中的某些问题,或者可能是特定网络驱动程序和/或硬件配置的问题)。
  • 你会这么好心,用上面的代码为我编译发布二进制文件吗?我还将尝试自己编译并在“新鲜”的 Windows 7 虚拟机上进行测试。如果您的二进制文件有效.. 那么肯定是 VS/.NET 问题。
  • @Josh:我不确定您希望我如何为您提供已编译的二进制文件。此外,至少鉴于我对 Windows 网络实施的更深层次的知识有限,因为我们不知道是什么环境差异导致了问题,所以认为即使在“新的 Windows 7 VM”上,你也不会体验到还为时过早同样的问题。 IE。您的本地网络环境可能会触发 Windows 或 .NET 中的某些行为,从而导致此延迟。如果你有一个地方想让我放一个二进制文件,我可以,但不要抱太大希望。
  • 我已经设法在全新的 Win7 和 Win8 安装上测试了二进制文件。它工作正常,在不到 0.02 秒内关闭连接。这意味着某些软件在我的 PC 上搞砸了。我和我的朋友最常见的一件事情是卡巴斯基杀毒软件。稍后我将深入研究问题并发布我的研究结果。
猜你喜欢
  • 1970-01-01
  • 2015-10-04
  • 2012-12-01
  • 1970-01-01
  • 2012-12-04
  • 1970-01-01
  • 1970-01-01
  • 2013-06-11
相关资源
最近更新 更多