【问题标题】:Weird behavior of async tcp socket异步 tcp 套接字的奇怪行为
【发布时间】:2014-06-02 12:56:20
【问题描述】:

我有一个异步服务器套接字正在监听某个端口。然后从另一台电脑连接到服务器并发送 1 个字节。一切正常,但有一个奇怪的行为。当我拉出网络电缆并尝试发送 1 个字节(在操作系统意识到电缆被拉出之前)时,我没有收到任何异常/错误,并且正如预期的那样,服务器没有收到该数据包。这是套接字应该如何工作的吗?这是否意味着在连接丢失的情况下,一些数据包可能会丢失(因为我没有收到异常并且不知道没有发送请求)? 代码如下:

    private void button3_Click(object sender, EventArgs e)
    {
        var b = new byte[1] {1};
        client.BeginSend(b, 0, b.Length, 0, new AsyncCallback(SendCallback), client);
    }

    private void SendCallback(IAsyncResult ar)
    {
        Socket client = (Socket)ar.AsyncState;

        int bytesSent = client.EndSend(ar);

        this.Invoke(new MethodInvoker(() => { MessageBox.Show(bytesSent.ToString() + " bytes sent"); }));
    }

【问题讨论】:

  • 你有回电吗?如果您重新插入网络电缆,它最终会传送数据吗?你等了多久?
  • 是的,调用了 SendCallback。插入数据线后数据未发送
  • 那么你的回调中bytesSent的值是多少?

标签: c# sockets asyncsocket


【解决方案1】:

发送者怎么可能告诉数据包没有收到呢?它把它送进了一个黑洞,等待回复。只要没有回复,他就无法知道数据包是否已收到或永远不会收到。

这通常通过超时来解决。最终,TCP 堆栈将宣布连接失效,或者您的后续读取超时。

发送根本不保证送达。

让对方向您发送确认信息。您的确认读取最终会超时。

或者,Shutdown(Send) 套接字。这确保了交付并会抛出异常(超时后)。无论如何,您都应该在关闭它之前Shutdown(Both) 一个套接字,以确保您收到所有错误的通知。

【讨论】:

  • 听说 TCP 保证投递……这就是我困惑的原因
【解决方案2】:

套接字层的 Windows 在内核中维护一个套接字缓冲区。在应用层成功发送仅仅意味着数据被复制到内核缓冲区中。此缓冲区中的数据由 TCP 堆栈推送到远程应用程序。在 Windows 中,如果一个数据包被丢弃,TCP 会重新发送数据 3 次,之后 TCP 堆栈会通知应用程序的连接关闭。重试间隔由 RTT 决定。第一次重试是在 1*RTT 之后,第二次是在 2*RTT 之后,第三次是在 3*RTT 之后。在您的情况下,您发送的 1 字节只是复制到内核缓冲区并表示成功。需要 3*RTT 来指示套接字已关闭。仅当您调用任何套接字 API 或监视套接字的关闭事件时,才会通知此连接关闭。拉动电缆后,如果您恰好在 3*RTT 之后排队第二次发送,则发送应该通过异常。立即获取发送失败指示的另一种方法是将发送套接字缓冲区大小设置为零 (SetSocketOption(..,SendBuffer,..)),以便 TCP 堆栈直接使用您的缓冲区并立即指示失败。

【讨论】:

    【解决方案3】:

    我假设您创建了一个 TCP 套接字。

    在这种情况下,您的客户端将不会在断开连接时收到通知(除非您的应用程序发送了某种注销消息)。

    查看TcpClientConnected 属性:

    Connected 属性获取客户端套接字的连接状态 截至最后一次 I/O 操作。当它返回 false 时,客户端套接字 要么从未连接,要么不再连接。

    “截至上一次 I/O”操作:只有从客户端读取(不成功)才能帮助您检测断开连接。许多应用程序实现“ping”来检测某些断开连接。

    【讨论】:

    • 你说的有点像,但我想澄清一点: Connected 属性仅在某些情况下可靠地为假。即使网络中断,仅发送某些内容也不一定会使其错误。它大多一文不值。这是一种启发式。要获得可靠的错误发现,您还需要其他东西。
    • 是的,对于可靠的东西,每 N 秒发送一次 ping。
    【解决方案4】:

    是的,这就是 TCP 套接字应该工作的方式。不,这并不意味着数据包一定会丢失。

    幕后发生的事情就是这样。当您调用 BeginSend 时,您发送的字节被传递到操作系统的 TCP 堆栈。然后 TCP 堆栈以数据包的形式发送数据。

    当数据包在合理的时间内没有被确认时,TCP 堆栈会自动重新发送数据包。只要没有收到确认包,这种情况就会反复发生。

    如果您重新插入电缆,其中一个重新发送将通过,服务器将延迟看到数据。这是期望的行为。请记住,TCP 旨在在冷战期间传输军事数据;这个想法是,即使网络的一部分被核爆,路由系统最终也会调整以找到另一条通往接收者的路径,并且数据最终会通过。

    如果您不重新插入电缆,大多数 TCP 堆栈最终将放弃,终止连接。不过,这需要几分钟时间。

    有关 TCP 重传超时的更多信息可以在此处的 RFC 1122 中找到:

    https://www.rfc-editor.org/rfc/rfc1122#page-95

    【讨论】:

    • 但在我的情况下它不会重新发送。重新连接电缆后数据包未送达。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多