这是由于 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 库的目的正是为了隐藏这种丑陋的网络性质。)事实上,您甚至需要了解 TCP 如何在有损网络上实现有序可靠交付的多个 RFC 和算法。拥塞控制也会影响缓冲区的存在时间。维基百科是一个很好的起点,但这是一个完整的学期”