Як насильно закрити розетку в TIME_WAIT?


113

Я запускаю певну програму на Linux, яка іноді виходить з ладу. Якщо ви відкриєте його швидко після цього, він прослуховує сокет 49201 замість 49200, як це робив перший раз. netstat виявляє, що 49200 знаходиться в стані TIME_WAIT.

Чи є програма, яку можна запустити, щоб негайно змусити цей сокет вийти з стану TIME_WAIT?


1
Якщо вас тут через "занадто багато TIME_WAITна сервері" , просто пропустіть перші три відповіді, які уникають питання, а не відповіді на нього.
Pacerier

Відповіді:


148
/etc/init.d/networking restart

Дозвольте мені детальніше. Протокол управління передачею (TCP) призначений для двостороннього, упорядкованого та надійного протоколу передачі даних між двома кінцевими точками (програмами). У цьому контексті термін надійний означає, що він повторно передасть пакети, якщо він загубиться посередині. TCP гарантує надійність, відсилаючи назад пакети підтвердження (ACK) назад для одного або діапазону пакетів, отриманих від однорангових.

Це стосується і керуючих сигналів, таких як запит на припинення / відповідь. RFC 793 визначає стан TIME-WAIT таким чином:

TIME-WAIT - являє собою очікування достатнього часу, щоб пройти впевненість, що віддалений TCP отримав підтвердження свого запиту на припинення з'єднання.

Дивіться таку схему стану TCP: alt текст

TCP - це протокол двостороннього зв’язку, тому при встановленні з'єднання між клієнтом і сервером немає різниці. Також кожен може зателефонувати, і обидва колеги повинні домовитись про закриття, щоб повністю закрити встановлене TCP-з'єднання.

Давайте зателефонуємо першому, щоб викликати quit як активний ближче, а другий підглядає пасивний ближче. Коли активний ближче посилає FIN, стан переходить до FIN-WAIT-1. Потім він отримує ACK для відправленого FIN і стан переходить до FIN-WAIT-2. Як тільки він отримує FIN також від пасивного ближчого, активний ближче посилає ACK до FIN і стан переходить до ЧАСУ-ЗАЧЕКАЙТЕ. Якщо пасивний ближчий не отримав ACK до другого FIN, він повторно передасть пакет FIN.

RFC 793 встановлює TIME-OUT вдвічі більше, ніж максимальний термін служби сегмента, або 2MSL. Оскільки MSL, максимальний час, коли пакет може бродити по Інтернету, встановлюється на 2 хвилини, 2MSL - 4 хвилини. Оскільки ACK немає до ACK, активний ближній не може нічого, крім чекати 4 хвилини, якщо він правильно дотримується протокол TCP / IP, на випадок, якщо пасивний відправник не отримав ACK до своєї FIN (теоретично) .

Насправді відсутні пакети, мабуть, рідкісні, і дуже рідкісні, якщо все відбувається в межах локальної мережі або в межах однієї машини.

Щоб відповісти на запитання дослівно: Як насильно закрити розетку в TIME_WAIT ?, я все одно дотримуватимусь своєї початкової відповіді:

/etc/init.d/networking restart

Практично кажучи, я би запрограмував це, щоб він ігнорував стан TIME-WAIT, використовуючи параметр SO_REUSEADDR, як згадується WMR. Що саме робить SO_REUSEADDR?

Цей параметр сокета повідомляє ядро, що навіть якщо цей порт зайнятий (у
стані TIME_WAIT), продовжуйте його і будь-коли повторно використовуйте. Якщо він зайнятий, але з іншим станом, ви все одно отримаєте адресу, яка вже використовується помилкою. Це корисно, якщо ваш сервер був закритий, а потім перезапущений відразу, поки сокети все ще активні на його порту. Ви повинні знати, що якщо надійдуть якісь несподівані дані, вони можуть заплутати ваш сервер, але, хоча це можливо, це малоймовірно.


8
Чудова відповідь, але не правильна відповідь на його запитання. Перезапуск мережі працюватиме, але потім перезавантаження, тож це не може бути правильним.
Кріс Хуанг-Лівер

3
@Chris Huang-Leaver, питання: "Чи існує програма, яку можна запустити, щоб негайно змусити цей сокет вийти з стану TIME_WAIT?" якщо перезавантаження можна вважати запуском програми, то це теж буде правильною відповіддю. Чому, на вашу думку, це не може бути правильним?
Євген Йокота

8
WMR має найкориснішу відповідь (що я роблю, коли стикаюся з таким питанням). Перезапуск мережі занадто радикальний, щоб вирішити, і може зайняти більше часу, ніж просто чекати очікування. Правильна відповідь на його запитання - «Ні», але ТАК не дозволить вам набрати два відповіді на лист :-)
Кріс Хуан- Залишити

6
о добре, наступного разу, коли якийсь процес зависне на SIGTERM, я просто розтрощую комп’ютер, а не виправляю.
Longpoke

Узагальненням цього є "перезапуск мережевих служб". Конкретне розташування /etc/init.d/networkingзалежить від платформи (Debian?), Тому точний командний рядок буде відрізнятися (іноді досить радикально) для інших систем. Я погоджуюся з іншими коментаторами, що це здається серйозним надмірним навантаженням і, очевидно, руйнівним для будь-яких непов'язаних мережевих послуг.
трійка

51

Я не знаю, чи є у вас вихідний код тієї конкретної програми, яку ви запускаєте, але якщо так, ви можете просто встановити SO_REUSEADDR, завдяки setsockopt(2)якій ви можете прив’язати до тієї ж локальної адреси, навіть якщо сокет знаходиться в стані TIME_WAIT (якщо тільки це socket активно слухає, дивіться socket(7)).

Для отримання додаткової інформації про стан TIME_WAIT див . Поширені запитання про сокет Unix .


але я не отримав вже пов'язану помилку. коли я знову виконую програму, вона прослуховує пост (123456), також я можу побачити, що система показує TIME_WAIT для цього порту, але все одно я можу підключитися. чому?
Джаяпал Чандран

2
Навіть із SO_REUSEADDR все одно можна отримати помилку "Адреса вже використовується". Докладніше див . У розділі hea-www.harvard.edu/~fine/Tech/addrinuse.html .
Jingguo Yao

@WMR SO_REUSEADDRне "закриває" розетку. Це просто дозволяє повторно використовувати ті, що вже відкрилися. Тож питання все ще залишається "Як насильно закрити розетку TIME_WAIT?"
Pacerier

Це правильна відповідь, але питання було не зовсім правильним. Принаймні добре вирішив мою проблему (не так, як перезапустити всю мережу, порушивши всі інші з'єднання).
V-Марк

SO_REUSEADDRдозволить bind()продовжувати; але якщо ви хочете прослухати цей сокет, listen()повернете EADDRINUSEвсе одно. Іншими словами, ця відповідь може допомогти клієнтському програмному забезпеченню за допомогою ефемерних портів, але не вирішує проблему з серверним програмним забезпеченням.
Чи буде

33

Наскільки я знаю, немає способу примусово закрити гніздо поза записом кращого обробника сигналу у вашу програму, але є файл / proc, який контролює, як триває час очікування. Файл є

/proc/sys/net/ipv4/tcp_tw_recycle

і ви можете встановити час очікування на 1 секунду, виконавши це:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

Однак ця сторінка містить попередження про можливі проблеми надійності під час встановлення цієї змінної.

Існує також пов'язаний файл

/proc/sys/net/ipv4/tcp_tw_reuse

який контролює, чи можна повторно використовувати TIME_WAIT розетки (імовірно, без будь-якого тайм-ауту).

До речі, документація на ядро ​​попереджає вас не змінювати жодне з цих значень без "порад / запитів технічних експертів". Якою я не є.

Програма повинна бути написана для спроби прив'язки до порту 49200, а потім збільшення на 1, якщо порт вже використовується. Тому, якщо у вас є контроль над вихідним кодом, ви можете змінити цю поведінку, щоб почекати кілька секунд і спробувати знову на тому ж порту, а не збільшувати.


думаю, що два інші приклади повинні бути s / rw / tw /, я б редагував, але не вистачає представника.

1
Взяте з документації на ядро: Обережно. І tcp_tw_recycle, і tcp_tw_reuse можуть спричинити проблеми. Не слід вмикати жодне, не розуміючи мережеву топологію між вузлами (ями), які використовують або використовуються вузлом, де параметр включений. З'єднання, які йдуть через вузли, які знають про стан з'єднання TCP, такі як брандмауер, NAT або балансир завантаження, можуть почати скидати кадри через налаштування. Проблема стане видимою, коли буде достатньо велика кількість з'єднань.

Встановлення його 1працює для майбутніх з'єднань, а як бути з тими поточними, які вже відкриті?
Pacerier

18

Насправді існує спосіб вбити зв’язок - killcx . Вони стверджують, що він працює в будь-якому стані з'єднання (що я не підтвердив). Вам потрібно знати інтерфейс, де відбувається комунікація, однак за замовчуванням він вважає, що eth0.

ОНОВЛЕННЯ: іншим рішенням є розріз, який надходить у сховища деяких дистрибутивів Linux.


3

Іншим варіантом є використання опції SO_LINGER з таймаутом 0. Таким чином, коли ви закриваєте сокет, примусово закривається, надсилаючи RST замість того, щоб переходити до поведінки закриття FIN / ACK. Це дозволить уникнути стану TIME_WAIT і може бути більш підходящим для деяких застосувань.


2
Він також втрачає будь-які вихідні дані, які все ще перебувають у дорозі, і може спричинити помилку на іншому кінці. Не рекомендовано.
користувач207421

@EJP Невдача рано - це майже завжди правильний дзвінок. Мережа не є надійною, і боротьба, яка сповільнить справи. Збійний додаток не може припустити, що будь-які дані вивели його безпечно.
Тобу

1
Насправді, я б рекомендував це в будь-який день, коли інша кінцева точка являє собою глючний, вбудований шлюз промислової шини, який реалізує власний надійний транспорт на рівні додатку через TCP, де зазначений транспорт не дозволяє з'єднанню закриватися, якщо він не отримує RST і таким чином заповнює межа з'єднання на цьому шлюзі. Там. Я дав вам дуже конкретний і дуже реальний приклад, який, на жаль, вимагає вдатися до таких хакерів.
Андін

@Tobu Мережа не є надійною, але TCP намагається бути, а те, що гірше, не означає зробити нічого кращого, а дозволити TCP виконувати свою роботу не означає нічого "боротися".
користувач207421

2

Альтернативним рішенням було б мати якийсь надійний проксі-сервер або програмне забезпечення для переадресації портів, яке прослуховує порт 49200, а потім переадресувати з'єднання на один із декількох екземплярів вашої менш надійної програми, використовуючи різні порти ... HAPROXY виникає на увазі.

До речі, порт, на який ви підключаєтесь, досить високий. Ви можете спробувати використати невикористаний трохи вище діапазону 0-1024. Ваша система рідше використовує менший номер порту в якості ефемерного порту.


0

TIME_WAIT - найпоширеніша проблема в архітектурі сервера клієнтського програмування. Почекайте кілька секунд, намагаючись періодично, це найкраще рішення. Для додатків у режимі реального часу їм потрібен сервер повинен негайно встати. Для них є опція SO_REUSEADDR.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.