Почему консоли иногда зависают всегда, когда разрывается соединение SSH?

89

Я видел это на многих консолях (на Linux, Mac, ...) и на множестве разных машин в разных сетях. Я никогда не могу точно определить точную причину, почему это происходит: все, что вам нужно сделать, это войти в систему через SSH. Если по какой-то причине соединение разрывается (для простоты, скажем, был отключен сетевой кабель), то иногда консоль просто зависает навсегда - в других случаях она просто нормально выходит из родительской оболочки.

Это так раздражает, когда это происходит (например, вы теряете историю команд.) Может быть, есть секретное сочетание клавиш, которое может вызвать выход (Ctrl-C или Ctrl-D не работают)? И в чем причина этой случайной «ошибки» во всех реализациях?

Крис Лерчер
источник
В этой теме кажется уместным упомянуть Mosh (Mobile Shell), который хорошо справляется со сбоями соединения, включая роуминг (изменение IP) и другие вещи.
Ciprian Tomoiagă

Ответы:

138

Существует «секретная» комбинация клавиш для принудительного выхода: ~) В замороженном сеансе нажмите эти клавиши по порядку: Enter~.тильда (только после новой строки) распознается клиентом ssh как escape-последовательность, и точка сообщает клиент прекратить свой бизнес без лишних слов.

Длительное зависание в вопросах связи не является ошибкой, сеанс SSH зависает в надежде, что другая сторона вернется. Если сеть обрывается, иногда даже через несколько дней вы можете вернуть сеанс SSH обратно. Конечно, вы можете конкретно сказать ему, чтобы сдаться и умереть с помощью последовательности выше. Есть также различные вещи, которые вы можете сделать, например, установить тайм-ауты keep-alive в вашем клиенте, чтобы, если у него не было активной ссылки в течение определенного периода времени, он отключался сам по себе, но поведение по умолчанию оставалось как подключен как можно!

Редактировать: еще одно полезное применение этого ключа прерывания - привлечь внимание локального ssh-клиента и навести на него справку, чтобы на минутку вернуться в локальную оболочку - скажем, извлечь что-то из вашей истории, - а затем перенаправить на удаленную работу. Enter~ Ctrl+ Zотправить ssh-клиент в очередь фоновых заданий вашей локальной оболочки, fgа затем вернуть его как обычно.

Изменить: при работе с вложенными сессиями SSH вы можете добавить несколько символов тильды, чтобы разорвать только один из сессий SSH в цепочке, но сохранить другие. Например, если вы вложены в 3 уровня (то есть вы ssh из local-> Machine1-> Machine2-> Machine3), вернете Enter~.вас в локальный сеанс, Enter~~.оставите в Machine1 и Enter~~~.оставите в Machine2 , Это работает и для других escape-последовательностей, таких как временное перемещение сеанса ssh в фоновый режим. Вышеупомянутое работает для любого уровня вложенности, просто добавляя больше тильды.

Наконец, вы можете использовать Enter~?для печати меню справки доступных управляющих команд.

TL; DR - поддерживаемые управляющие команды. Поддерживаемые управляющие последовательности:

 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)
Калеб
источник
11
+1 за знание пароля для секретного пробного использования для стрельбы в голову sshd. Вы узнали об этом так же, как я (опечатка ~/.somethingorotherпосле нажатия Enter)?
voretaq7
2
@ voretaq7: Нет, я не был таким умным, но когда кто-то подсказал мне, я сказал: «Правда? Так вот, что происходило все те времена, когда моя оболочка просто ЧЕЛОВЕЛА! Без причины, когда я печатал?» , Это не обычная последовательность, за исключением опечаток, упомянутых вами, но это может произойти.
Калеб
14
При этом «секрет» означает «на странице руководства».
Жаворонки
4
Хотя многие люди не знают об этом, на самом деле это секрет. man sshохватывает это в ESCAPE CHARACTERSразделе. ~.(Disconnect) и ~^Z(background ssh) очень удобны.
Стефан Ласевский
9
Конечно, это на странице руководства :) Я использовал слово «секрет» только потому, что это сделал ОП, и я использовал язык в щеке. Проблема с этой функцией в том, что люди не знают, где ее искать, они ожидают, что управляющие символы и такие сигналы будут частью оболочки или чего-то подобного. Раз вы знаете, где искать или даже что спрашивать, конечно, это там.
Калеб
12

SSH предлагает средство поддержания жизни. Добавьте следующее к своему локальному ~/.ssh/config(создайте, если это не существует):

ServerAliveInterval 15
ServerAliveCount 3

Этот параметр устанавливает сигнал поддержания активности, отправляемый каждые 15 секунд через защищенный туннель. После трех последовательных сбоев клиент SSH завершит работу.

Обратите внимание, что в некоторых системах (включая macOS 10.14) вместо этого должно быть:

ServerAliveInterval 15
ServerAliveCountMax 3

Взято из этого ответа на ask.ubuntu: https://askubuntu.com/a/29967/30266

krlmlr
источник
9

Тот факт, что он зависает, является функцией TCP, а не SSH. Приложение не может знать, что сеанс / соединение TCP разорвано, если только TCP не сообщит приложению, используя соединение, что соединение больше не существует. С точки зрения каждого хоста, сеанс TCP все еще находится в состоянии «Установлено», и нечего сказать, что длительный бездействующий (без передачи данных) сеанс недопустим, кроме RST или отсутствия ответа на пакет поддержки активности TCP (который не универсально реализовано). Для меня это не ошибка в SSH, я бы ожидал такого поведения.

joeqwerty
источник
5

Если соединение правильно установлено, любое волшебное нажатие клавиши не будет выполнено, так как соединение уже установлено, прежде чем вы нажмете клавишу. Вы можете сказать клиенту прекратить, но это не повлияет на историю, сохраняемую (или нет) на стороне сервера.

Хотя это на самом деле не отвечает на вопрос, это может помочь уменьшить последствия зависания соединения: когда я работаю удаленно (и даже обычно, когда я не работаю), я запускаю screen(с или без byobuоболочки в зависимости от ее доступности) так что, если произойдет какое-либо прерывание соединения, мой сеанс со всей его историей будет сохранен и доступен в том состоянии, в котором я его оставил при повторном подключении.

Дэвид Спиллетт
источник
6
Ваше предложение по поводу экрана в порядке, но первый бит на самом деле не относится к проблеме. Вам не нужно передавать ключи на удаленную сторону, вам нужно только передать их локальному SSH-клиенту! ОП хочет вернуть его местную оболочку, включая историю. SSH берет это на себя и не отвечает на нормальные последовательности разрыва, такие как CTRL-C, потому что это передает их. Есть способ пройти и прекратить работу локального клиента, см. Мой ответ. История на локальном конце обычно сохраняется в любом случае, если вы входите снова, в зависимости от конфигурации оболочки.
Калеб
2
@Caleb: в этом вопросе конкретно упоминается потеря истории, а история команд сохраняется на стороне сервера. Чтобы сказать серверу, что нужно делать что-то другое с историей, вам нужно отправить сообщение серверу, чтобы он сказал что-то другое с историей. Конечно, есть способы сделать так, чтобы bash (и некоторые другие оболочки) записывали строки истории сразу, а не собирали их в ОЗУ, пока пользователь не создаст оболочку с exit/, logoutкоторая решит проблему истории без использования screen.
Дэвид Спиллетт