这是由于实施TCP(和SSH使用TCP)。你的send()只是写入一个套接字,它只是一个文件描述符,返回意味着这个操作是成功的。这并不意味着数据已经发送。毕竟,文件描述符只是一些带有内核状态的指针。它在内核中实现以在失败会话之前保持TCP状态更长一些。事实上,内核被允许无限期地保持这个会话,直到你明确地调用close()或者终止你的进程。因此,您的数据实际上会缓存在内核空间以供网卡在稍后提供。
下面是一个简单的实验,你可以这样做: 编写不断建立连接
socket();
bind();
listen();
while (1) {
accept();
recv();
}
写客户端建立连接后,接收消息,需要CIN输入的服务器,并且将消息发送到服务器时你打回来了。
socket();
connect();
while (1) {
getline();
send();
}
请注意,您从不在任何一边的while循环中调用close()。现在,如果您在建立连接之后拔出电缆,发送消息,重新连接并发送另一条消息,则会在服务器端找到这两条消息。
你永远不会看到的是,你收到第一条消息之前的第二条消息。你要么全部丢失,要么按顺序接收它们。
现在让我解释为什么它的行为如此。这是TCP会话的状态图。
https://dl.dropbox.com/u/17011409/TCP_State.png
您可以清楚地看到,除非明确调用close(),否则连接将始终处于已建立状态。这是TCP的预期行为。建立TCP连接非常昂贵,并且保持会话活动对性能有好处。 (这部分是TCP DOS的工作原理,攻击者在服务器耗尽资源以保持TCP状态信息的同时保持连接。)
在这种状态下,send()将被委派给内核以进行实际发送。 TCP保证按顺序,可靠的传输,但网络随时可能丢失数据包。所以TCP有缓冲你的数据包,并继续尝试。有一些算法可以抑制这种重试,但是在声明失败之前它会被缓冲很长时间。在Linux中,假设丢包的默认超时时间为3秒。但是在损失之后,TCP会重试。然后在几秒钟后再试。拔掉电缆的事实与通往目的地的数据包丢失情况一样。再次插入电缆后,重试会成功,并且TCP将按顺序开始发送剩余的消息。
我知道我一定没有彻底解释它。你真的需要知道TCP的细节来推断这种行为。它是TCP给你的属性所必需的。将内部实现暴露给程序员是不可接受的。 (发送调用有时会在几毫秒内返回,有时在10秒后返回,我敢打赌,没有人会希望在他们的代码中使用这个性能炸弹。拥有一个TCP库的目的正是为了隐藏网络的这种丑陋本质。)实际上,您甚至需要了解多个RFC以及TCP如何通过有损网络实现可靠传输的算法。拥塞控制进入缓冲区将在那里存在多久。维基百科是一个很好的起点,但如果你真的想了解细节,这是一个完整的学期本科课程。
你真的拔掉了一根电缆吗?还是以更微妙的方式放下网络? 'len'多少钱? –
如果你从调用'send()'的主机(而不是**,例如,该主机上的虚拟机,或者你拔下的路由器的下游机器上)拔下了电缆,则打开套接字确实应该返回EIO缺失在另一个内核线程可以传播错误之前的一段时间。你试图测试的失败究竟是什么? –
其实我使用networkmanager的无线接口。但是对于有线接口也一样(我使用ifconfig down /拔掉了电缆)。 'len'在我的情况下是'52'。我试图找到一种在SSH会话期间检测连接丢失的方法。而且我没有找到任何可以帮助我使用该应用的'libssh'的东西。 – maverik