异步tcp套接字的奇怪行为

时间:2014-06-02 12:56:20

标签: c# sockets asyncsocket

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

    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"); }));
    }

4 个答案:

答案 0 :(得分:2)

发件人怎么可能告诉收不到数据包?它将它发送到黑洞并等待回复。只要没有回复,他就无法知道数据包是收到还是永远都不会。

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

发送不保证交付。

请对方发送确认信。您的确认阅读将最终超时。

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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

查看TcpClientConnected属性:

  

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

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

答案 3 :(得分:0)

是的,这是TCP套接字的工作方式。不,这并不意味着数据包必然丢失。

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

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

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

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

有关TCP重传超时的更多信息,请参见RFC 1122:

http://tools.ietf.org/html/rfc1122#page-95