【问题标题】:How to properly and completely close/reset a TcpClient connection?如何正确和完全关闭/重置 TcpClient 连接?
【发布时间】:2010-09-30 07:51:46
【问题描述】:

关闭或重置 TcpClient 连接的正确方法是什么? 我们有与硬件通信的软件,但有时有些东西 出错了,我们将不再与它通信,直到我们重新启动软件。

我尝试强制 TcpClient.Close() 甚至将其设置为 null 但这不起作用。 只有完全重新启动软件才能工作。

建议?


我不能使用 using 关键字,因为 TpcClient 只在一个位置定义,但在整个库中都使用。 (并且在任何给定时间只有一个连接)

这是一个处理通信的库。软件本身可以调用Controller类(代表硬件)的ResetConnection()方法。

目前看起来像

if (tcpClient != null)
{
    tcpClient.Close();
    tcpClient = null;
}

现在从我在这里阅读的内容来看,我应该使用 tcpClient.Dispose() 而不是“= null”

我会试一试,看看有没有什么不同。

【问题讨论】:

    标签: c# .net tcp


    【解决方案1】:

    您必须在关闭连接之前关闭流:

    tcpClient.GetStream().Close();
    tcpClient.Close();
    

    关闭客户端不会关闭流。

    【讨论】:

    • 如果关闭 TcpClient 并没有关闭连接,它到底关闭了什么?是否有任何理由关闭 TcpClient?
    • 我真的不知道,你应该检查反射器,但它可能会关闭其他使用的对象而不是流(也许流可以在不同的对象之间共享,这就是它不会自动关闭的原因,只是猜测)。
    • 请注意,导致必须关闭流的错误 (support.microsoft.com/kb/821625) 仅存在于 .NET 1.1 及更早版本中。我在 .NET 4.5 中测试了示例代码,它可以正常工作而无需在流上调用 close。
    • 如果我使用 tcpClient.Close() 而不是 tcpClient.Client.Close() 或 tcpClient.GetStream().Close(),我仍然会在 .NET 4.6.1 上遇到间歇性问题。前几天我几乎失去了理智,直到我找到了这个。
    • 使用 .NET Standard 的 System.Net.Sockets.TcpClient,调用 Dispose()。没有Close() 方法。
    【解决方案2】:

    鉴于已接受的答案已过时,并且我在其他答案中看不到任何关于此的内容,因此我正在创建一个新答案。在 .Net 2 及更早版本中,您必须在关闭连接之前手动关闭流。该错误已在 C# 中 TcpClient 的所有更高版本中修复,并且如 Close method 的文档中所述,对方法 Close 的调用会关闭连接和流

    根据 Microsoft Docs 进行编辑

    Close 方法将实例标记为已释放并请求 关联的 Socket 关闭 TCP 连接。基于 LingerState 属性,TCP 连接可能会保持打开一段时间后 当数据仍有待发送时调用 Close 方法。没有 基础连接完成时提供的通知 关闭。

    调用此方法最终会导致关联的 Socket 关闭,并且还会关闭关联的用于发送和接收数据的 NetworkStream(如果已创建)。

    【讨论】:

    • @Twometer 您的目标是哪个版本的 .Net?
    • 它是 .NET 4.6.1
    • 我正在调用 TcpClient.Close()、tcpClient.Client.Close()、.Disconnect() 等等,但它并没有关闭。只有当我终止进程时,服务器才会说“连接已关闭”
    • 您链接的文章中的示例在调用tcpClient.Close();之前仍然调用networkStream.Close();,因此该示例已过期或您仍然必须手动关闭底层流。
    • @Deantwo 更新了我的答案。它标志着它已准备好被处置。但无法保证何时处置。
    【解决方案3】:

    使用单词:using。编程的好习惯。

    using (TcpClient tcpClient = new TcpClient())
    {
         //operations
         tcpClient.Close();
    }
    

    【讨论】:

    • 不适用于所有型号。如果您在一个代码块中临时使用连接,这很好,但如果您有一个客户端实现想要将连接作为成员来维护,则此方法将失败。您还会发现,根据类文档,在 TcpClient 上调用 Dispose() 不会关闭底层套接字。使用只会丢弃。
    • 这在异步和非阻塞套接字中是行不通的,如果你想将连接存储在某处并管理它,则实际上不能使用此代码。
    • @Gusdor 查看反射源,似乎 Dispose(true) 将在客户端调用 Close()。 (.NET 4.0)
    • 必须是新增的。我发布参考.net 3.5。很好看!
    • @Dermot,@SteveJobs 好吧,现在有了async 关键字,未来就在这里,它确实如此;-)。它甚至可以与BeginConnect()EndConnect() 兼容,前提是您在using 块内调用End………
    【解决方案4】:

    尽管有所有适当的using 语句、调用Close、有一些指数回退逻辑并重新创建TcpClient,但我仍然看到应用程序在没有应用程序重新启动的情况下无法恢复TCP 连接的问题。它一直以失败告终 System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

    但是TcpClient 上有一个选项LingerState 似乎已经解决了这个问题(可能几个月后才知道,因为我自己的硬件设置经常失败!)。见MSDN

    // This discards any pending data and Winsock resets the connection.
    LingerOption lingerOption = new LingerOption(true, 0);
    
    using (var tcpClient = new TcpClient 
           {SendTimeout = 2000, ReceiveTimeout = 2000, LingerState = lingerOption })
       ...
    

    【讨论】:

    • 嗨!我有完全相同的问题,尽管所有适当的代码有时会随机出现此异常,但 LingerOption 解决了您的问题吗?我正在认真考虑将我的所有代码迁移到 UDP 只是为了摆脱这个间歇性问题
    • @rafael 我认为它解决了我的问题:自从发布此问题后,我根本没有看到这个问题。
    【解决方案5】:

    除了一些内部日志,关闭 == 处置。

    Dispose 调用 tcpClient.Client.Shutdown( SocketShutdown.Both ),但它会吃掉任何错误。 或许直接调用,可以得到一些有用的异常信息。

    【讨论】:

      【解决方案6】:

      关闭套接字连接并允许重新使用套接字:

      tcpClient.Client.Disconnect(false);
      

      【讨论】:

      • 如果我想稍后重新打开连接,我很困惑为什么我需要这样做。只需关闭流并关闭TcpClient,然后在以后的.Connect 上引发异常。
      • 你不是说 Disconnect(true) 吗?
      【解决方案7】:

      关闭套接字以便重新打开的正确方法是:

      tcpClient.Client.Disconnect(true);
      

      布尔参数表示是否要重用套接字:

      【讨论】:

        【解决方案8】:

        您是否尝试过显式调用TcpClient.Dispose()

        你确定你有 TcpClient.Close() 和 TcpClient.Dispose()-ed ALL 连接吗?

        【讨论】:

        • 只有一个连接,我关闭了它但我没有处理它......这有什么不同吗? (我想是的,只是问:-)
        • TcpClient.Dispose 受保护,无法直接调用。
        • @theycallmemorty 文档另有说明。 msdn.microsoft.com/en-us/library/vstudio/…
        • 这是一个显式的接口实现。因此,在将其强制转换为 IDisposable 之前,他键入 TcpClient 是不可见的,即((IDisposable)client).Dispose()
        猜你喜欢
        • 2017-01-17
        • 1970-01-01
        • 2011-04-04
        • 2014-03-14
        • 2015-03-12
        • 1970-01-01
        • 1970-01-01
        • 2018-12-15
        • 2010-11-26
        相关资源
        最近更新 更多