
我經常需要在不可靠的 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
設定超時間隔(以秒為單位),在此之後如果沒有從伺服器接收到數據,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
在進入會話掛起狀態後,您可能沒有給它足夠的時間自行「取消掛起」 。