【问题标题】:Stack Overflow on TcpClient (Recursion)TcpClient 上的堆栈溢出(递归)
【发布时间】:2020-08-15 03:17:00
【问题描述】:

由于对new的重复调用,如果保持原样,以下代码可能会产生堆栈溢出异常:

async void Connect()
{
    try 
    {
        client = new TcpClient(ip, port);
    }
    catch (Exception e)
    {
        HandleException(e); // Most likely will be a SocketException e.g. connection refused
        Connect(); // Try to connect again
    }
}

虽然我可以通过例如减少这种情况发生的机会在放弃和退出递归之前添加最大重试次数,我宁愿写它以便释放内存并且不会发生堆栈溢出异常。

我想过把它放在一个while循环中,但我认为会有同样的问题,例如

while (!connected)
{
    try
    {
        client = new TcpClient(ip, port);
        connected = true;
    }
    catch (Exception e)
    {
        HandleException(e);
    }
}

除了有一个任意定义的最大重试次数之外,还有更好的方法来避免堆栈溢出异常吗?

【问题讨论】:

  • 不,循环不会有同样的问题,但你为什么要这样的无限循环呢?如果您指定了错误的 IP 地址或端口怎么办?它永远不会完成,没有那个超时听起来像是一个更好的交易吗?我会使用一个循环,连接超时以及放弃前的最大尝试次数。
  • 无限只是为了说明我的观点;实际上我有最大重试次数,但我知道理论上可以使用这种方法获得堆栈溢出异常,我想知道是否有更好的方法。
  • 理论上总是有可能发生堆栈溢出异常,但递归方法更容易出现这种情况。循环是这里的最佳选择。
  • 你是对的,循环工作没有产生异常(到目前为止)。我想我误解了循环内部的范围。我猜循环变量只存在一次迭代,然后超出范围,所以它们不会一直咀嚼内存。我将使用一个循环并保持我的最大重试次数,并可能添加一个整体超时。谢谢!如果您想将其发布为答案,我会接受,否则我会自己做。

标签: .net recursion stack-overflow tcpclient


【解决方案1】:

我认为如果我们优雅地关闭套接字(通过添加 SocketException),那么无论递归方法调用 Connect() 多少次,我们都可以避免 Stackoverflow。此外,在我看来,如果由于任何给定原因连接失败,立即调用 Connect() 方法是没有意义的。我认为在再次调用之前稍作休息可以避免不断面临类似的错误。

你可以试试这个:

async void Connect()
    {            
        Socket socket = null; //namespace System.Net.Sockets
        try
        {
            client = new TcpClient(ip, port);
            socket = client.Client; 
        }
        catch(SocketException se) //All socket related exceptions will be caught here
        {
            if (socket != null)
                socket.Close();
            //better to write exception in the log to fix the problem
            System.Threading.Thread.Sleep(1000 * 10); //Wait for few seconds before retrying
            Connect();
        }
        catch (Exception e)
        {
            HandleException(e); 
            //Connect(); // Try to connect again
        }
    }

【讨论】:

    【解决方案2】:

    正如 Lasse 在他的 cmets 中所说:

    一个。在内存和范围方面,循环的行为与递归不同,因此在这里使用它更安全。

    b.技术上总是可能遇到堆栈溢出异常,但递归等一些方法通常比常规循环更容易出现这种情况。

    我最终实现了一个带有一些额外条件的循环,例如最大重试次数。

    【讨论】:

      猜你喜欢
      • 2015-04-04
      • 2017-01-20
      • 2018-12-02
      • 2017-09-06
      • 2019-07-08
      • 2015-08-05
      • 2017-09-29
      • 1970-01-01
      • 2018-03-10
      相关资源
      最近更新 更多