Как побороть » address already in use»
Есть сферический код TCP-сервера на луа:
Подключаюсь к нему с помощью
Если развывать соединение, нажимая Ctrl+C в терминале с nc, то всё норм.
Если же разрывать соединение нажимая Ctrl+C в терминале с сервером, то его последующий запуск вываливается с ошибкой «address already in use» в строке с bind. Через минуту-две запускается нормально
Как побороть «address already in use»? Может подкрутить какие-то опции?
PS: сишников скастовал, потомучто только они разбираются в подобной низкоуровневой магии )
Вышеописанное поведение она не меняет. По-прежнему «address already in use» если тушить сервер при подключенном клиенте
SO_LINGER(с нулевым таймаутом)
reuseaddr влияет если сервер биндится к INADDR_ANY. Но т.к. сокет все-равно в полузакрытом состоянии, законектиться к тому же адресу и тому же порту не получится, пока ОС не отпустит сокет их состояния TIME_WAIT (60 сек, как показывает /proc/sys/net/ipv4/tcp_fin_timeout)
Собственно, переформулируя вопрос из ОП-поста: как повлиять на /proc/sys/net/ipv4/tcp_fin_timeout локально, только для моей програмы?
О, заработало! Спасибо)
убирать time_wait в ноль не очень красиво. навесь обработчик сигнала sigint и с нем рви соединение через tcp rst.
Некрасиво только если потом закрывать ненормально. А если в софте реализован graceful shutdown, то без linger вполне можно жить.
Если же разрывать соединение нажимая Ctrl+C в терминале с сервером
То можно зарегистрировать обработчик соответствующего сигнала. А в нём установить флаг is_shutdown. Теперь после ctrl-c будет вызван обработчик, потом server:accept завершится с ошибкой (EINTR), это можно словить и если флажок is_shutdown взведён, мы знаем, что всё ОК, пользователь хочет всё закрыть, закрываем и выходим
Оффтопик. А вне программы эту проблему можно решить?
Есть некрософт, который давно заброшен разработчиком, но к сожалению в продакшне. Время от времени через него проходят данные, не соответствующие ТЗ, и оно намертво виснет. Давно написал ему watchdog, ( проверяет /proc/$pid/schedstat и /proc/$pid/wchan ), который в случае зависания перезапускает ( при необходимости — убивает по kill -9 ). Но часто при повторном запуске вижу в логах то же самое «address already in use», хотя процесс уже убит. В результате watchdog несколько раз в цикле убивает это говно мамонта, пока тому не удаётся взлететь успешно
Можно ли как-то принудительно очистить состояние порта?
Увы, нет. Это в парафаии ядра, что и настраивается таймаутами на соединение и свойство сокета, опциями SO_REUSEPORT и SO_LINGER. Но любой из этих вариантов имеет последствия. SO_REUSEPORT был разработан в первую очередь для балансировки между несколькими процессами на одном порту, SO_LINGER — непосредственно таймаут в состоянии TIME_WAIT.
Как выше писали, самый лучший вариант — писать обработчик завершения программы по сигналу.
убирать time_wait в ноль не очень красиво
А чем это чревато?
навесь обработчик сигнала sigint и с нем рви соединение через tcp rst.
Пример был сферический. Sigint тоже. Скрипт нужен чтобы тестировать разрывы любимого ростелекома. А тут еще ядро свои разрывы добавляет)
пишу через обработчики уже 10 лет И НИ ЕДИНОГО РАЗРЫВА!!11
миллион способов, все зависит от того насколько «схаченное» решение для вас терпимо. можно сниффать сиквенс намбер и слать tcp rst, например из scapy (из удобного), или даже вроде есть специализированные утилиты. если все зависло так что траффика вообще никакого не проходит (ну, хотя бы кип-алайв зафорсить обычно можно — посылаешь со спуфленного адреса) то тогда можно подглядеть нужный сиквенс намбер прямо в ядре, например через systemtap или кастомным модулем. если же вопрос решается SO_LINGER с нулевым таймуатом, то можно его принудительно установить на сокет — пиши модуль для LD_PRELOAD с перехватом bind() например и устанавливай там дополнительные опции на сокет, я так для одного старого проприетарного говна увеличивал очередь необработанных коннектов.
Какой сигнал сработает когда ростелеком разорвет соединение?)
во-первых, если хочешь получить на свои топики нормальные рабочие ответы, давай в них всю информацию, а то сейчас еще выясниться что речь не про сервер TCP, а еще через две страницу — что и вовсе на самом деле у тебя там винда с SUA.
теперь по делу. если у тебя демон умирает когда говнотелеком рвет соединение, ты что-то делаешь не так. расскажи больше.
Как устранить ошибку» java.net.BindException: Address already in use: JVM Bind»?
в Eclipse, я получил эту ошибку:
Я не уверен, почему он появился сейчас, но он работал нормально всего несколько часов назад. Мне нужно перезагрузить компьютер? Как мне докопаться до сути? Я ценю любые советы или рекомендации.
16 ответов:
Да у вас есть другой процесс, привязанный к тому же порту.
TCPView (только для Windows) с Windows Sysinternals это мое любимое приложение, когда у меня есть ошибка JVM_BIND. Он показывает, какие процессы прослушивают какой порт. Он также предоставляет удобное контекстное меню, чтобы либо убить процесс, либо закрыть соединение, которое мешает.
Если вы знаете, что порт работает процесс, вы можете ввести: lsof -i:
например, lsof -i:8080 , чтобы вывести список процессов (pid), запущенных на порту 8080.
затем убить процесс с kill
перечислит все протоколы, порты и процессы прослушивания . Используйте
убить процесс прослушивания порта. е.г
убить процесс Терминал: kill
найти pid: Терминал: lsof -i:
от Диего Пино ответ
на Ubuntu / Unix мы можем решить эту проблему, выполнив 2 действия, как описано ниже.
это даст выход, как это
здесь порт нет 8080 на котором запущен процесс java. И его идентификатор процесса (pid) составляет 9488 перед java ( 9488/java ).
kill -9 9488
здесь: -9 для принудительной остановки и 9488 — это идентификатор процесса.
теперь снова запустите сервер.Надеюсь, этот ответ будет полезен для вас, ребята.
У вас есть другой процесс, работающий на том же порту.
вы можете попробовать убить одного из java.exe-службы, работающие в вашем диспетчере задач — ps убедитесь, что вы не убиваете eclipse, так как это указано как java.а также exe. Если ничего больше не работает, перезагрузка компьютера будет исправить это в любом случае. Похоже, вы не выключаете сокет из предыдущего теста. Надеюсь, это поможет.
чтобы убить процесс, вам сначала нужно найти идентификатор процесса (pid)
вы получите свой идентификатор процесса (PID), теперь, чтобы убить тот же процесс, выполните эту команду:
в строке CMD Windows найдите идентификатор процесса, который содержит соединение на порту привязки, введя следующую команду:
— a показать все соединения
-o показывает идентификатор процесса
а то завершить процесс.
для тех, кто ищет самый простой из ответов (как это то, что мы обычно пропускаем), просто остановите свой запущенный проект и запустите его снова. В большинстве случаев мы забываем остановить проект, который мы запускали ранее, и когда мы повторно запускаем проект, он показывает такую проблему.
Я также прикрепляю фотографию, чтобы сделать ее более четкой (я использую «Spring tool suite»). Итак, что вам нужно сделать, это либо нажать кнопку в крайнем правом углу, если вы хотите перезапустить тот же проект или сначала нажмите на кнопку, которая является 2-й справа, чтобы остановить ваш проект, а затем кнопку в крайнем левом углу, чтобы запустить свой проект. Я надеюсь, что это решит проблему нескольких новых программистов. 🙂
да, как сказал Гвидо Симоне, потому что другой процесс прослушивает тот же порт.Если вы находитесь в Ubuntu, вы можете просто убить этот процесс, давая команду sudo kill $(sudo lsof -t -i:[port number])
пример: sudo kill $(sudo lsof -t -i:8080)
но однажды это не сработало для меня. я дал команду
и это ничего не показывает.
Я проверил мои контейнеры docker с помощью команды docker ps -a но ни один из них не жив.Все контейнеры остановились (но я помню, я остановил один контейнер, который был использован же порт несколько минут назад.). чтобы убедиться, что docker не является причиной, я останавливаю весь процесс docker с помощью команды sudo service docker stop и попробуйте снова. Удивительно, но eclipse не показал ошибку в то время.он отлично запускал мою программу.
надеюсь, что мой опыт поможет кому-то одному.
порт уже используется каким-то другим процессом, поскольку @Diego Pino сказал, что u может использовать lsof в unix для поиска процесса и убить соответствующий, если вы находитесь в windows, используйте netstat-ano, чтобы получить все pids процесса и порты, которые каждый приобретает. найдите нужный порт и убейте.
чтобы быть очень легко просто перезагрузить машину, если это возможно 🙂
перезагрузите компьютер один раз, я думаю, что это будет работать. Это начало работать в моем случае. Еще одна вещь, которую можно сделать, перейдите в Диспетчер задач и завершите процесс.
Я столкнулся с подобной проблемой в Eclipse, когда были открыты две консоли, когда я сначала запустил серверную программу, а затем клиентскую программу. Я останавливал программу в одной консоли, думая, что она закрыла сервер, но она закрыла только клиент, а не сервер. Я нашел запущенные процессы Java в моем диспетчере задач. Эта проблема была решена путем закрытия как серверных, так и клиентских программ с их отдельных консолей(Eclipse показывает консоль последней активной программы). Так что когда я снова запустил серверную программу, порт снова был открыт для захвата.
ваш порт должен быть занят в другом процессе. Таким образом, Вы можете скачать TCPView на https://technet.microsoft.com/en-us/sysinternals/bb897437 и убить процесс для используемого порта.
Если вы не знаете свой порт, дважды щелкните на сервере, который не запускается и нажмите на странице открыть Свойства сервера и нажмите на glassfish из левой колонки. Здесь вы найдете порты.
(1) Проверьте порт используется или нет, убить этот процесс
$ lsof-i: [port]
(2) Еще одна причина заключается в том, что порт используется ipv6, решение:
edit / etc / sysctl.conf
добавьте это в файл
net.протокол IPv6.конф.все.disable_ipv6 = 1
тогда сделайте это эффект
$ sudo sysctl-p /etc/sysctl.conf
или просто перезагрузка
Это означает, что другой процесс уже использует порт. В случае, если этот порт используется некоторыми другими критическими приложениями, и вы не хотите закрывать это приложение, лучший способ-выбрать любой другой порт, который можно использовать бесплатно.
настройте приложение для использования любого другого свободного порта, и вы увидите, что ваше приложение работает.
«Address Already in Use» или как избежать проблем при завершении TCP соединения
Корректное отключение
Для корректного завершения сетевого подключения обе стороны должны послать пакеты с сигналом о завершении (FIN), которые указывают что стороны не будут больше отсылать данные, также каждая сторона должна подтвердить (ACK) получение сигнала о завершении сетевого обмена данными. FIN инициируется когда приложение вызывает метод close(), shutdown() или exit(). После завершения работы метода close() ядро переходит в режим ожидания подтверждения от второй стороны приема сигнала о завершении. Это делает возможной ситуацию когда процесс инициировавший отключение будет завершен прежде чем ядро освободит рессурсы связанные с подключением, и снова разрешит использовать порт для связывания с другим процесоом (в этом случае, при попытке использования порта мы получим исключение AddressAlreadyInUse).
На изображении:
- Имеется установленное соединение, состояние ESTABLISHED
- Клиент инициирует окончание подключения, посылает серверу сигнал о завершении подключения (FIN), переходит в состояние ожидания ответа сервера (FIN_WAIT_1)
- Сервер получает сигнал о завершении подключения и отправляет подтверждение (ACK), переходит в состояние ожидания завершения подключения (CLOSE_WAIT) (вызывает close())
- Сервер отсылает клиенту сигнал о том что успешно закрыл подключение (FIN) и пробует прочитать подтверждение клиента (ACK), после чего не дожидаясь его отключается.
- Теперь клиенту может придти два сигнала в разной очередности
ACK — клиент получил подтверждение о том что сервер понял его намерение закрыть подключение
- Клиент переходит в состояние ожидания сигнала об окончании закрытия подключения (FIN) от сервера (FIN_WAIT_2)
- Клиент получает сигнал о закрытии подключения сервером (FIN), отправляет подтверждение (ACK), некоторое время ждет(TIME_WAIT) и отключается (ядро освобождает рессурсы) (CLOSED)
FIN — клиент получает сигнал о закрытии подключения на стороне сервера(FIN), раньше чем подтверждение от сервера (ACK), о получении инициирующего сигнала о закрытии от клиента (FIN)
- Клиент отправляет подтверждение приема сигнала о том что сервер закрывает соединение, и переходит в состояние отключения (CLOSING)
- После отключения пробует считать сигнал подтверждения от сервера (который был отправлен сервером сразу после получения от клиента сигнала о завершении работы, пункт 2), некоторое время ожидает(TIME_WAIT) и ядро освобождает рессурсы (CLOSING).
На рисунке показаны все воможные состояния, которые могут быть во время корректного завершения, в зависимости от порядка получения пакетов FIN и ACK от удаленной стороны. Обратите внимание, если вы инициировали завершение подключения (левая половина рисунка), то другая сторона не будет ожидать подтверждения получения вами пакета FIN (правая половина рисунка). Сотояние TIME_WAIT требуется на случай если подтверждение (ACK) которое вы отправили не было получено на другой стороне, или на случай появления ложных пакетов по какой-то причине. Я не знаю почему на стороне сервера не сделали состояние TIME_WAIT, хотя если клиент инициирует закрытие, это безусловно и не должно требовать ожидания. Состояние TIME_WAIT может удерживать порт в течение нескольких минут после завершения процесса. Время удержания варьируется в зависимости от операционной системы, в некоторых операционных системах оно является динамическим, стандартные значения лежат в диапазоне от 1 до 4 минут.
Если обе стороны успеют инициировать сигнал завершения, раньше чем получат его от другой стороны, то обе стороны будут вынуждены пройти через ожидание (TIME_WAIT).
Корректное отключение слушающей стороны
Слушающий сокет может быть закрыт немедленно, при отсутствии входящих подключений, его состояние переходит сразу в CLOSED. При наличии входящих подключений, будет произведен переход к FIN_WAIT_1 и затем к TIME_WAIT.
Обратите внимание, на стороне слушающего сокета невозможно гарантировать чистое закрытие. Пока вы проверяете использование соединения методом select() до закрытия, существует крошечная, но реальная возможность появления входящего подключения после вызова select() и до вызова close().
Непредвиденное отключение удаленной стороны
При внезапном отключении сервера, локальная сторона инициирует закрытие соединения, и в этом случае TIME_WAIT неизбежен. Если удаленная сторона исчезает из-за сбоя сети или перезагрузки машины (редкие случаи), локальный порт будет оставаться привязанным вплоть до истечения таймаута состояния TIME_WAIT. Хуже того, некоторые старые операционные системы, не реализуют таймаут для состояния FIN_WAIT_2, и могут оставаться в нем бесконечно долго, в этом случае спасти может только перезагрузка системы.
Если же локальное приложение (клиент) падает в процессе активного соединения, порт будет занят пока не завершится состояние TIME_WAIT, то же верно для приложений, закрытых в процессе подключения к удаленной стороне(pending).
Способы избежания проблем
Опция SO_REUSEADDR
Можно использовать метод setsockopt(), для установки опции SO_REUSEADDR, что позволит создавать привязку к порту даже если он еще находится в состоянии TIME_WAIT (привязка к порту будет разрешена только для одного процесса). Это самый простой и эффективный метод избежать сообщения «address already in use».
Но, как ни странно, использование опции SO_REUSEADDR, может привести к более трудноотлавливаемым ошибкам чем «address already in use». SO_REUSEADDR позволяет использовать порт застрявший в TIME_WAIT, но вы все еще сможете использовать этот порт в том процессе, в котором он привязан изначально.