gpt4 book ai didi

linux - 发送 (2) 在无法访问的网络上成功建立连接

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:53:21 24 4
gpt4 key购买 nike

我在理解我的 linux x86 机器上的 send (2) 系统调用时遇到了一些麻烦。假设我在我的应用程序中与 LAN 中的其他主机建立了 SSH 连接。然后我放下网络(例如拔下电缆)并调用通过连接发送一些 SSH 数据包的函数(从我的应用程序)。此函数内部调用 send like

w = send(s->fd_out,buffer, len, 0);

在调试器中,我发现 send 返回 len(即调用后 w == len)。如果网络无法访问,这怎么可能?当我调用 netstat 时,它说我的 SSH 连接处于 ESTABLISHED 状态,即使网络已关闭也是如此。

无法理解为什么send 执行正常并且不返回任何错误(如EPIPEECONNRESET)。 SSH 连接可能会在网络中断后的某个时间存在吗?

感谢大家。

最佳答案

这是由于TCP的实现(而ssh使用的是TCP)。您的 send() 只是写入一个套接字,它只是一个文件描述符,返回表示此操作成功。这并不意味着数据已发送。毕竟,文件描述符只是一些带有内核状态的指针。它在内核中实现,以在 session 失败之前将 TCP 状态保持更长的时间。事实上,内核被允许无限期地保持这个 session ,直到你明确地调用 close() 或终止你的进程。所以你的数据实际上是缓存在内核空间中,供网卡稍后传送。

这是一个您可以做的快速实验:写一个建立连接后一直接收消息的服务器

socket();
bind();
listen();
while (1) {
accept();
recv();
}

编写一个客户端建立连接,获取 cin 输入,并在您按下回车键时向服务器发送消息。

socket();
connect();
while (1) {
getline();
send();
}

请注意,您永远不要在任何一侧的 while 循环中调用 close()。现在,如果您在建立连接后拔下电缆,发送一条消息,再次重新连接,然后发送另一条消息,您会在服务器端找到这两条消息。
您永远不会观察到的是您在收到第一条消息之前收到了第二条消息。您要么将它们全部丢失,要么按顺序接收它们。

现在让我解释一下为什么它会这样。这是一个TCP session 的状态图。
https://dl.dropbox.com/u/17011409/TCP_State.png

您可以清楚地看到,除非您显式调用 close(),否则连接将始终处于已建立状态。这是 TCP 的预期行为。建立 TCP 连接是昂贵的,保持 session 事件对性能有好处。 (这就是那些 TCP DOS 的部分工作方式。攻击者不断建立连接,直到服务器耗尽资源以保留 TCP 状态信息。)

在此状态下,您的 send() 将被委托(delegate)给内核进行实际发送。 TCP 保证按顺序、可靠地传送,但网络随时可能丢失数据包。所以 TCP 必须缓冲您的数据包,并继续尝试。有一些算法可以限制这种重试,但它在宣布失败之前缓冲了相当长的时间。在 Linux 中,假设数据包丢失的默认超时是 3 秒。但是在丢失之后,TCP 将重试。然后在几秒钟后重试。拔下电缆的事实与在到达目的地的途中丢失数据包的情况相同。再次插入电缆后,重试成功,TCP 将开始按顺序发送剩余的消息。

我知道我肯定没有解释清楚。您确实需要了解 TCP 的详细信息才能推断出这种行为。它是 TCP 为您提供的属性所必需的。将内部实现暴露给程序员是 Not Acceptable 。 (有时在几毫秒内返回,有时在 10 秒后返回的发送调用怎么样?我打赌没有人会在他们的代码中想要这个性能炸弹。拥有 TCP 库的目的正是为了隐藏网络的这种丑陋本质。)事实上,您甚至需要了解 TCP 如何在有损网络上实现有序可靠交付的多个 RFC 和算法。拥塞控制也会影响缓冲区的存在时间。维基百科是一个很好的起点,但如果你真的想了解细节,它是一个完整学期的本科类(class)。

关于linux - 发送 (2) 在无法访问的网络上成功建立连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12645119/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com