【问题标题】:How can I check whether a (TCP) socket is (dis)connected in C#?如何检查(TCP)套接字是否在 C# 中(断开)连接?
【发布时间】:2010-10-05 15:40:44
【问题描述】:

我应该如何检查 (TCP) 套接字以确定它是否已连接?

我在MSDN 中阅读了有关Socket.Connected 属性的信息,但它说它只根据最后一次I/O 显示状态。这对我没有用,因为我想在尝试从套接字读取数据之前执行此操作。备注部分还指出:

如果需要确定当前 连接状态,做一个 非阻塞、零字节发送调用。如果 呼叫成功返回或 抛出 WAEWOULDBLOCK 错误代码 (10035),那么套接字仍然 连接的;否则,套接字为否 连接时间更长。

同一页面上的示例显示了如何做到这一点。(1) 但是a post by Ian Griffiths 说我应该从套接字读取,而不是发送 通过它。

Another post by Pete Duniho 说:

...在您致电Shutdown() 之后, 调用Receive(),直到它返回0 (假设远程端点不是 实际上会寄给你任何东西, 这将在遥控器发生后立即发生 端点已收到您的所有 数据)。除非你这样做,否则你有 不保证远程端点 实际上已经收到了所有的数据 你发送了,即使使用了挥之不去的 插座。

我不太明白他关于调用Receive() 以确保远程端点确实收到了我发送的所有数据的说法。 (在发送缓冲区为空之前,套接字会阻塞接收吗?)

我对提出的不同方法感到困惑。你能解释一下吗?


(1) 我想知道为什么Socket.Connected 属性的example 分配一个1 字节的数组,即使它以0 长度调用Send

【问题讨论】:

标签: .net sockets


【解决方案1】:

套接字的死亡会以多种方式改变其行为,因此这些方法都是有效的:)

使用这两种方法,您实际上检查了断开连接后套接字行为的那些部分。

我不太明白他关于调用 Receive() 以确保远程端点确实收到了我发送的所有数据的说法。 (在发送缓冲区为空之前,套接字会阻塞接收吗?)

TCP 是可靠协议,这意味着您发送的每个数据包都必须得到确认。确认意味着发送设置了ACK 位的数据包。这些数据包可能包含也可能不包含其他(有效负载)数据。

当套接字连接时,Receive() 将阻塞,直到套接字接收到具有非空有效负载的数据包。但是当套接字断开时,Receive() 会在最后一个ACK 数据包到达时立即返回。

调用Receive() 可确保您接收来自远程端点的最后一个ACK 数据包或发生断开连接超时,而您将能够接收更多信息在这个套接字上。

同一页面上的示例显示了如何执行此操作。 (我想知道为什么它分配一个 1 字节的数组,即使它以 0 长度调用 Send?)但是 Ian Griffiths 的一篇文章说我应该从套接字读取,而不是通过它发送。

send()ing 到一个套接字时,你实际上是在尝试将一些数据附加到套接字队列的末尾。缓冲区中是否还有一些地方,然后你的Send() 立即返回,如果没有,Send() 会阻塞直到有地方。

当套接字处于断开状态时,TCP/IP 堆栈会阻止对缓冲区的所有进一步操作,这就是 Send() 返回错误的原因。

Send() 实现了基本的指针检查,这意味着当 NULL 指针传递给它时它会失败。您可能会将任何非空常量作为指针传递,但您最好分配 1 个字节而不是创建常量——以防万一。


您可以使用任何您喜欢的方法,因为它们都不消耗资源。只要用于套接字连接检查,它们都是相同的。

至于我,我更喜欢Receive(),因为这是你通常在一个循环中运行并等待的。你从Receive() 得到一个非零值,你处理数据;你得到一个零,你处理断开连接。

【讨论】:

  • 非常感谢!我仍然需要一些澄清。如果我不通过套接字发送任何内容,那么在尝试读取套接字之前,哪种方法最好检查套接字是否处于活动状态?我是否应该尝试接收,如果我得到 0 则套接字已关闭?
  • 还有为什么示例分配一个1字节的数组,然后将其传递给长度为0的send()?这与分配新字节[0]有什么不同吗?
【解决方案2】:

通常会使用 Socket.Select 方法来确定一组套接字的状态(Socket.Poll 用于单个套接字)。

这两种方法都允许您查询套接字的状态。现在,假设您首先跟踪了套接字连接,那么您通常会在尝试读取之前调用套接字上的 Select/Poll。如果 Select/Poll 表明套接字是可读的,这会告诉您:

  • 任何一个套接字都有可用的最高读取数据,在这种情况下,Receive 将返回可读取的数据。
  • 套接字套接字已关闭,在这种情况下,当您调用 Receive 时,将立即返回 0 个字节(即,如果 Select/Poll 指示套接字是可读的并且您调用 Receive 但它立即返回 0 个字节,那么您知道连接已关闭、重置或终止。

就我个人而言,我从未使用过 Poll - 我一直使用 Select,但 MSDN 似乎暗示 Poll 与 Select 几乎相同,只是针对单个套接字。

我还要补充一点,在大多数情况下,使用 Select 是处理 Socket 连接的最有效和最好的方式。

【讨论】:

  • 谢谢。但是根据 MSDN,这种方法无法检测到某些类型的连接问题,例如网络电缆损坏,或者远程主机被不正常地关闭。您必须尝试发送或接收数据以检测此类错误。 ...
  • ... 请举例说明如何处理此类情况?
【解决方案3】:

"如果需要判断连接的当前状态,进行非阻塞、零字节的Send调用。如果调用成功返回或抛出WAEWOULDBLOCK错误码(10035),则socket仍然处于连接状态;否则,套接字不再连接。” -- 不幸的是,它甚至不起作用!

mySocket.Blocking = false;
byte[] buffer = new byte[1];
int iSent = mySocket.Send(buffer, 0, SocketFlags.None);
bConnected = mySocket.Connected;

bConnected 总是以 true 结束,并且调用总是成功返回,即使以太网电缆已被拔掉。

此外,不幸的是 == 发送任何实际数据也不会检测到断开的连接。

buffer[0] = 0xff ;
int iSent = mySocket.Send(buffer, 1, SocketFlags.None);

反复返回 1,就好像真的发送了什么一样。虽然有问题的设备甚至不再连接。

【讨论】:

  • 我从(不太出色的)MSDN 示例中假设您必须处理异常并在出现异常时手动返回 false。也就是 Socket.Connected 的值在异常之后可能有一个无效的值。如果您手动处理异常,则无法从您的代码 sn-p 中判断。
【解决方案4】:

我不太明白他关于调用 Receive() 以确保远程端点确实收到了我发送的所有数据的说法。

@PeteDuniho 的帖子不是关于建立连接的状态,而是关于以一种您知道对等方何时收到您的所有数据的方式终止连接。

(在发送缓冲区为空之前,套接字会阻塞接收吗?)

不,但是如果你关闭套接字然后读取直到 EOS,你正在等待对等方读取所有数据,直到 获得 EOS 然后关闭套接字。因此,您可以保证所有数据都已进入对等应用程序。

【讨论】:

    猜你喜欢
    • 2011-02-09
    • 1970-01-01
    • 2020-07-04
    • 2019-04-27
    • 2011-09-18
    • 2013-11-22
    • 2019-06-23
    相关资源
    最近更新 更多