免责声明
如果您的SSH连接无法在短暂的网络中断中幸存下来,则说明还有其他事情正在发生,这让ssh
TCP不能正常工作。
有关详情,请参见下文。无论如何:
最快最脏的无依赖性解决方案
创建如下的shell脚本:
#!/bin/sh -
# Tune these numbers depending on how aggressively
# you want your SSH session to get reconnected.
timeout_options='-o ServerAliveInterval=4 -o ServerAliveCountMax=2'
# 255 is the status OpenSSH uses to signal SSH errors, which
# means we want to connect. All other exit statuses suggest
# an intentional exit.
status=255
# Keep opening the SSH connection and immediately dropping into
# `screen` until an intentional exit happens.
while [ "$status" = 255 ]
do
ssh $timeout_options -t "$@" screen -dR
status=$?
# You can add a `sleep` command here or a counter or whatever
# you might need as far as rate/retry limiting.
done
exit "$status"
这只会运行一个愚蠢的简单循环,并不断尝试连接ssh
和附加到screen
。将主机或通常传递给ssh
调用的其他任何内容作为命令行参数传递。
重新连接仅基于SSH是否报告连接错误,这意味着它不具有检测非SSH错误(例如“您确实没有打开WiFI”之类的信息)的智能,但对于您。
我假设您拥有ssh-agent
一个或无密码的SSH密钥,该密钥将允许重新连接在您没有其他输入的情况下正常工作。
这将是一个很小的竞争条件,如果您^C
在重新连接期间恰好在人类无法察觉的瞬间击中,您最终可能会杀死脚本,而不是将脚本传递^C
给客户端,因此如果您怀疑连接挂起不要^C
太热心捣碎。
最简单的附加软件解决方案
您可以尝试程序autossh,该程序应该在Ubuntu软件包存储库中可用。
如果您需要从源代码构建或对其进行审核,则它是一个C程序,无需任何其他库就可以进行编译作为依赖项,与上面的hack相比,它似乎在检查连接活动性方面更具智能,并且还附带了一个方便的rscreen
脚本命令,该命令可以自动-附加到screen
。
细节
如何ssh
恢复正常后
只是为了验证,因为我不喜欢自己检查自己说的话,所以我在回答之前做了一点测试:
我使用Linux设备进入WiFi,与LAN上的另一设备建立SSH连接,验证我ssh
与另一端的连接正常(可以运行命令等),然后客户端断开WiFi(导致接口断开)要取消配置:没有更多的IP地址),在ssh会话中键入了一堆更多的字符(当然没有响应),然后重新连接到我的WiFi-由于信号不良和其他因素,重新连接实际上至少失败了一次,然后终于重新连接:我等待了大约五秒钟,ssh
会话恢复了,什么也没发生,所以我又按下了一个键,ssh
会话立即又恢复了活动,断开时我键入的所有键都出现在命令行中。
见,ssh
只是写/读入TCP网络套接字,直到OS告诉它出事了,而实际上TCP是非常宽容的延长连接滴。
在默认内核设置下,将其放置在自己的设备上,Linux中的TCP堆栈将很高兴地允许连接完全静默很多分钟,然后再声明连接已死并向其报告错误ssh
-直到它最终放弃时,我们正在讨论大约30分钟,或者至少肯定长到足以使连接打last持续一秒或一分钟。
然而,在幕后,Linux TCP堆栈会逐渐重试消息,并且延迟越来越长,这意味着到连接恢复时,您可能正在等待其他延迟,然后ssh
会话才能再次“活跃”。
为什么有时会中断
通常,在闲置时间比TCP堆栈所允许的时间短得多的情况下,主动导致连接关闭的原因是某些原因,然后无法向ssh
客户端报告该连接状态。
可能的候选人包括:
防火墙或NAT'ing路由器必须使用内存来记住每个实时TCP连接-作为对DOS攻击的一种优化和缓解措施,它们有时会忘记您的连接,然后静默忽略随之而来的数据包,因为当您不记得现有连接时,在连接的中间看起来无效。
行为良好的防火墙/路由器将注入TCP RST数据包,该数据包通常显示为connection reset by peer
错误消息,但重置数据包是一劳永逸的,因此,如果此时与客户端的连接仍然有问题,则丢弃也重置数据包,您的客户端会认为连接仍然有效。
服务器本身可能具有防火墙策略,以静默方式丢弃意外的数据包,每当服务器认为连接已关闭但客户端没有关闭时,这都会破坏客户端的连接恢复尝试:您的客户端一直在尝试继续连接,但服务器只是忽略它是因为在服务器的防火墙状态下没有这些数据包所属的实时连接。
由于您运行的是Linux,因此请仔细检查服务器的iptables
/ ip6tables
(或者nft
您是否在使用新的东西),以准确了解允许或删除的内容。在TCP SSH端口上允许新的 /已建立的 / 相关的数据包是很常见的,但不允许 “无效”的数据包-如果您无声地丢弃所有不允许的数据包,则这种常见的设置可能会在短暂的连接问题后导致这些冻结。
您的SSH服务器本身可能被配置为在一段时间不活动之后使用TCP或SSH客户端keepalive数据包的OpenSSH选项之一关闭连接。就其本身而言,这不会导致不确定的挂起,但可以使您进入上述状态之一。
进入ssh
会话挂起的状态后,您可能没有给它足够的时间自行“取消挂起”。
<Enter>
并输入~.
以告知您的一方断开连接,您只需重复上一个ssh命令即可重新连接(例如,使用向上箭头或!!
)。