Назад | Перейти на главную страницу

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

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

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

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

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

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

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

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

TL; DR - поддерживаемые escape-команды: Поддерживаемые escape-последовательности:

 ~.   - 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.)

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

ServerAliveInterval 15
ServerAliveCount 3

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

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

ServerAliveInterval 15
ServerAliveCountMax 3

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

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

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

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

В моем случае проблема заключалась в большом размере MTU. Вы можете изменить MTU на маршрутизаторе, если используете NAT, но я меняю MTU на сервере:

sudo /sbin/ifconfig eth0 mtu 1036
sudo /etc/init.d/networking restart

В Windows вы также можете увеличить этот ключ:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"TcpMaxDataRetransmissions"=dword:00000010