我经常需要在不可靠的 wifi 环境中通过 ssh 连接到服务器。在服务器上,我运行 screen,因此如果断开连接,我可以重新连接并恢复 screen 会话,并从中断处继续,但连接丢失仍然是一个主要的时间消耗:如果在我连接时连接断开在服务器上,终端窗口往往会冻结。我必须终止该选项卡,打开一个新选项卡,再次 ssh 到服务器并恢复屏幕会话。我已经尝试过在服务器上运行屏幕和本地屏幕。无论哪种方式,当连接断开时它都会冻结。
有什么方法可以让我拥有类似于 screen 的东西,或者可能是 screen 本身,它会自动尝试重新连接并保持会话运行,这样我就不必手动重新连接?通常,当我失去连接时,我认为这只是很短的一段时间——也许不到一秒钟。
我使用的是 Ubuntu 14.04 LTS,MATE 版本。谢谢
答案1
你可以看看使用mosh
:https://mosh.org/
您可以设置一个具有可靠互联网连接的“跳转”服务器,用于mosh
连接,然后ssh
与您管理的每台服务器建立会话。我建议使用跳转服务器的原因是您可能不希望安装mosh
在您正在管理的服务器上。
另一个优点mosh
是它基于 UDP 而不是 TCP,并且您的会话可以在 IP 地址发生变化时继续存在,例如从 WiFi 连接到移动互联网连接。
只是要明确一点,mosh
它不是替代screen
,而是ssh
。使用它仍然是一个好主意screen
,因为mosh
如果客户端由于某种原因死亡,它本身不提供重新连接到会话的方法。
答案2
使用 ssh 的ServerAlive
选项来检测连接何时失败。
ServerAliveCountMax
设置服务器活动消息的数量(见下文),可以在 ssh(1) 接收不到服务器返回的任何消息的情况下发送该消息。如果在发送服务器活动消息时达到此阈值,ssh 将与服务器断开连接,从而终止会话。需要注意的是,服务器活动消息的使用与 TCPKeepAlive(如下)有很大不同。服务器活动消息通过加密通道发送,因此不会被欺骗。 TCPKeepAlive 启用的 TCP keepalive 选项是可欺骗的。当客户端或服务器依赖于了解连接何时变为非活动状态时,服务器活动机制非常有价值。默认值为 3。例如,如果将 ServerAliveInterval(见下文)设置为 15 并且 ServerAliveCountMax 保留为默认值,则如果服务器变得无响应,ssh 将在大约 45 秒后断开连接。
ServerAliveInterval
设置超时间隔(以秒为单位),在此之后如果没有从服务器接收到数据,ssh(1) 将通过加密通道发送消息以请求服务器响应。默认为0,表示这些消息不会发送到服务器。
因此,如果您设置ServerAliveInterval
为 5,ssh
如果网络中断 15 秒,将自动断开连接。
答案3
我一直在使用tmux
几年来,根据我的经验,它会自动重新连接。至少当连接仅在相对较短的时间内失败时。请注意,我实际上使用byobu
以 tmux 作为后端。我不知道这种稳健性是否是两者的一个特征,tmux
或者byobu
甚至是两者的结合,但我建议您都尝试一下。
我通过 VPN 从本地 Arch 安装连接到各种远程 Ubuntu 服务器。我刚才在连接到遥控器时拔掉网线进行了测试。会话挂起,但一旦我的电缆再次插入,它就无缝恢复。
但是,当我重新启动路由器进行测试时,连接没有恢复。我认为这与网络中断的时间有关,但如果仅中断几秒钟,它似乎就会重新连接。
如果相关的话,我会使用terminator
作为我的终端模拟器。
所有这三个都可以在 Ubuntu 存储库中找到:
sudo apt-get install tmux terminator byobu
但是,我完全不确定tmux
或是否byobu
更擅长处理 ssh 断开连接。我只知道根据我的经验,他们经常会从短暂的连接丢失中恢复过来。不过,这可能取决于我的配置的其他方面。
答案4
免责声明
如果您的 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
太热心地捣碎。
最简单的附加软件解决方案
您可以尝试该程序自动SSH,它应该在您的 Ubuntu 软件包存储库中可用。
如果您需要从源代码构建或审核它,它是一个单独的 C 程序,无需任何附加库作为依赖项即可编译,似乎比我上面的 hack 更智能地检查连接活跃度,和它还附带了一个方便的rscreen
脚本命令,可以自动附加到screen
.
细节
一般如何ssh
恢复
只是为了验证一下,因为我不喜欢在没有检查自己的情况下就说出事情,所以我在回答之前进行了一些测试:
我使用 Linux 设备连接到 WiFi,与 LAN 上的另一台设备建立了 SSH 连接,验证了ssh
与另一端的工作连接(可以运行命令等),然后在客户端上断开 WiFi(导致接口被取消配置:不再有 IP 地址),在 ssh 会话中输入更多字符(当然没有响应),然后重新连接到我的 WiFi - 由于错误,重新连接实际上至少失败了一次信号和其他因素,然后终于重新连接:我等待了大约五秒钟让会话ssh
恢复,什么也没发生,所以我又按了一个键,会话ssh
立即再次活跃起来,我在断开连接期间输入的所有键都出现在命令行。
看,ssh
只需写入/读取 TCP 网络套接字,直到操作系统告诉它出了问题,而 TCP 实际上是非常能够容忍长时间的连接中断。
如果留给自己的设备使用默认内核设置,Linux 中的 TCP 堆栈会很高兴地容忍连接完全静默几分钟,然后宣布连接死亡并向其报告错误ssh
- 当它最终放弃时,我们正在谈论大致情况大约 30 分钟,或者至少足够长,足以克服持续一秒或一分钟的连接中断。
然而,在幕后,Linux TCP 堆栈会逐渐以越来越长的延迟重试消息,这意味着当您的连接确实恢复时,您可能会在会话ssh
似乎再次“活跃”之前看到额外的延迟。
为什么有时会出现这种情况
通常,某些事情会主动导致连接在一段时间后关闭显着缩短不活动的时间超过 TCP 堆栈可以容忍的时间,然后无法向客户端报告该连接状态ssh
。
可能的候选人包括:
防火墙或 NAT'ing 路由器必须使用内存来记住每个实时 TCP 连接 - 作为针对 DOS 攻击的优化和一些缓解措施,它们有时只是忘记您的连接,然后默默无视它的后续数据包,因为当您不记得现有连接时,连接中间的数据包看起来无效。
表现更好的防火墙/路由器会注入 TCP RST 数据包,这通常表现为
connection reset by peer
错误消息,但重置数据包是一劳永逸的,因此如果与客户端的连接当时仍然存在问题并丢弃也重置数据包,您的客户端会认为连接仍然存在。服务器本身可能有一个防火墙策略来默默地丢弃意外的数据包,每当服务器认为连接已关闭但客户端没有关闭时,这就会中断客户端的连接恢复尝试:您的客户端不断尝试继续连接,但服务器只是忽略它,因为在服务器的防火墙状态下,这些数据包所属的实时连接不存在。
由于您运行的是 Linux,请仔细检查服务器的
iptables
/ip6tables
(或nft
如果您正在使用新的东西),看看您到底允许什么,还是放弃什么。允许是很常见的新的/已确立的/有关的TCP SSH 端口上的数据包,但是不是“无效”的 - 如果您默默地删除所有不允许的内容,这种常见的设置可能会在短暂的连接问题后导致此类冻结。您的 SSH 服务器本身可能配置为在一段时间不活动后关闭连接,使用 TCP 或 SSH 客户端保持活动数据包的 OpenSSH 选项之一。就其本身而言,这不会导致无限期挂起,但它可能会使您处于上述状态之一。
ssh
在进入会话挂起状态后,您可能没有给它足够的时间自行“取消挂起” 。