【问题标题】:Closing System.Net.Sockets.TcpClient kills the connection for other TCPClients at the same IP Address关闭 System.Net.Sockets.TcpClient 会终止同一 IP 地址上其他 TCPClients 的连接
【发布时间】:2012-02-08 01:33:37
【问题描述】:

为了清楚起见,我在这里提到的所有 TCPClients 都不是我自己的类的实例,它们都是 Mono 的 .NET 4.0 实现的 System.Net.Sockets.TcpClient 的实例。

我有一个服务器正在监听客户端连接,就像服务器一样。每当它获得一个新客户端时,它都会创建一个新的 TCPClient 来处理新线程上的连接。我正在使用字典跟踪所有连接和线程。如果客户端断开连接,它会向服务器发送断开连接消息,关闭 TCPClient,删除字典条目,线程自然死亡。没有大惊小怪,没有混乱。服务器可以毫无问题地处理多个客户端。

但是,我正在模拟如果客户端断开连接,没有机会发送断开连接消息,然后重新连接会发生什么。我正在检测客户端是否重新连接了用户名系统(完成测试后会更安全)。如果我只是创建一个新的 TCPClient 并让旧的 TCPClient 运行,系统工作得很好,但是我有一堆无用的线程在占用空间并且什么都不做。懒鬼。

所以我尝试关闭与旧连接关联的 TCPClient。当我这样做时,新的 TCPClient 也会死掉,并且客户端程序会抛出这个错误:

E/mono    (12944): Unhandled Exception: System.IO.IOException: Write failure ---> System.Net.Sockets.SocketException: The socket has been shut down

而服务器抛出这个错误:

Unable to write data to the transport connection: An established connection was aborted by the software in your host machine.

Cannot read from a closed TextReader.

所以用远程端点关闭旧的 TCPClient 说:192.168.1.10:50001

还使用远程端点 say:192.168.1.10:50002 打破新的 TCPClient

所以两个 TCPClient 对象具有相同的远程端点 IP 地址,但不同的远程端点端口。但是关闭一个似乎会阻止另一个工作。我希望能够关闭旧的 TCPClient 来进行清理,而无需关闭新的 TCPClient。

我怀疑这与 TCPClient 如何在低级别处理套接字有关,但对此没有任何真正的了解,我无法修复它。

【问题讨论】:

  • 字典的关键是什么?如果是用户名,这似乎来自关闭错误的 TCPClient
  • 好主意,但在冲突解决之前我不会将新连接添加到字典中。我使用字典关闭旧连接,删除字典条目,然后将新连接添加到字典。
  • 另外,字典的关键确实是用户名。
  • 不好意思这么说,我不认为是运行时,我认为是代码。请尝试以下操作:连接,记录netstat的相关部分,强制断开,重新连接(发生错误),检查netstat。如果“旧”连接仍然存在,则您关闭了错误的 tcpclient。
  • 好的,我现在要弄清楚如何使用 netstat。我会回来的。

标签: c# networking tcpclient robustness


【解决方案1】:

我的套接字服务器上遇到了类似的问题。我使用一个简单的列表而不是字典来保存我当前的所有连接。在侦听新流的连续 while 循环中,我有一个 try / catch,如果客户端断开连接,它会在 catch 块中杀死客户端。

sever.cs 上的类似内容:

public static void CloseClient(SocketClient whichClient)
        {
            ClientList.Remove(whichClient);
            whichClient.Client.Close();
            // dispose of the client object
            whichClient.Dispose();
            whichClient = null;
        }

然后在客户端一个简单的dispose方法:

public void Dispose()
        {
            System.GC.SuppressFinalize(this);
        }

编辑:这个粘贴是他或她在我的代码的帮助下自己找到的 OPs 分辨率。

所以澄清一下,情况是我有两个 TCPClient 对象 TCPClientA 和 TCPClientB,它们具有不同的远程端点端口,但 IP 相同:

TCPClientA.Client.RemoteEndPoint.ToString();

返回:192.168.1.10:50001

TCPClientB.Client.RemoteEndPoint.ToString();

返回:192.168.1.10:50002

TCPClientA 需要清理,因为它不再有用,所以我调用

TCPClientA.Close();

但是出于某种原因,这会关闭 TCPClientB 另一端的客户端的套接字。但是,写

TCPClientA.Client.Close();
TCPClientA.Close();

成功关闭 TCPClientA 而不会干扰 TCPClientB。所以我已经解决了这个问题,但我不明白为什么会这样。

【讨论】:

  • 感谢克里斯托弗,我已经用阅读您的代码后发现可以工作的代码编辑了我的问题。如果您想将我的代码添加到您的答案中,我很乐意将其标记为答案。不过,我现在不想这样做,因为您的代码与我的代码有些不同。首先,您使用的是 SocketClient,而 TCPClient 没有 Dispose() 方法。此外,在我的代码中不需要将 whichClient 设置为 null。
  • SocketClient 是我编写的自定义类,它或多或少只是 TCPClient 的扩展。但是你确定,我会把你的代码移到我的答案中。
  • 回答你为什么它使用 TCPClientA.Client.Close() 工作的问题......很难说,因为我们看不到你的类结构。我的假设是 TCPClientA 是一个具有称为 Client 的属性的类,它是您的实际 TcpClient。这正是我的设置方式,但我的类称为 SocketClient。我真的很惊讶 TCPClientA.Close() 为你做了任何事情(假设我对你的类结构的假设是正确的)。要真正关闭 TcpClient,您必须通过您的成员列表深入了解该对象,这就是我所做的并且似乎对您有用。
  • TCPClient 不是我的类结构,它是 .NET 的。我可能应该更清楚 - TCPClientA 是来自 Mono 的 .NET 4.0 实现的 System.Net.Sockets.TCPClient 的一个实例。如果这是我班级的问题,我不会问这个问题,我只是修复班级。我问是因为它是一个应该工作的类,但不知何故它没有。
【解决方案2】:

看起来您已经找到了解决方案,但是您知道在 .net 中编写客户端/服务器应用程序时存在许多类似的陷阱。有一个开源网络库(单声道完全支持),这些问题已经解决,networkComms.net。基本示例是here

免责声明:这是一个商业产品,我是创始人。

【讨论】:

    【解决方案3】:

    这显然是您的代码中的错误。仅仅关闭一个入站连接不可能关闭另一个。很明显,您的代码中的其他地方正在发生其他事情。

    【讨论】:

    • 它不会“关闭()”它,它只会导致它停止正常运行。如果是代码错误,你能解释一下为什么调用 TCPClientA.Client.Close();其次是 TCPClientA.Close();解决了这个问题,当只是调用 TCPClientA.Close();本身会导致问题吗?如果我关闭了错误的 TCPClient,那么这两个操作都应该终止错误的连接。前者没有,后者有。
    猜你喜欢
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 2021-07-08
    • 2017-10-03
    • 2012-09-21
    • 2016-10-27
    • 2021-08-23
    相关资源
    最近更新 更多