LINUX.ORG.RU

Как побороть " address already in use"

 , , ,


3

2

Здравствуйте.

Есть сферический код TCP-сервера на луа:

local socket = require("socket")
local server = assert(socket.tcp())

assert(server:bind("127.0.0.1", 9970))

-- Опции
assert(server:setoption("keepalive", false))
assert(server:setoption("reuseaddr", false))
assert(server:setoption("tcp-nodelay", true))
assert(server:setoption("linger", {on=false, timeout=1}))

assert(server:listen(0))

local con = assert(server:accept())
assert(con:send("HELLO!!!\n"))
assert(con:receive("*a"))

Подключаюсь к нему с помощью

nc localhost 9970 -v

Если развывать соединение, нажимая Ctrl+C в терминале с nc, то всё норм.

Если же разрывать соединение нажимая Ctrl+C в терминале с сервером, то его последующий запуск вываливается с ошибкой «address already in use» в строке с bind. Через минуту-две запускается нормально

Как побороть «address already in use»? Может подкрутить какие-то опции?

PS: сишников скастовал, потомучто только они разбираются в подобной низкоуровневой магии )

★★★★★

Ответ на: комментарий от zolden

Вышеописанное поведение она не меняет. По-прежнему «address already in use» если тушить сервер при подключенном клиенте

makoven ★★★★★
() автор топика
Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от eagleivg

reuseaddr влияет если сервер биндится к INADDR_ANY. Но т.к. сокет все-равно в полузакрытом состоянии, законектиться к тому же адресу и тому же порту не получится, пока ОС не отпустит сокет их состояния TIME_WAIT (60 сек, как показывает /proc/sys/net/ipv4/tcp_fin_timeout)

Собственно, переформулируя вопрос из ОП-поста: как повлиять на /proc/sys/net/ipv4/tcp_fin_timeout локально, только для моей програмы?

makoven ★★★★★
() автор топика

убирать time_wait в ноль не очень красиво. навесь обработчик сигнала sigint и с нем рви соединение через tcp rst.

val-amart ★★★★★
()
Ответ на: комментарий от val-amart

Некрасиво только если потом закрывать ненормально. А если в софте реализован graceful shutdown, то без linger вполне можно жить.

Pavval ★★★★★
()

Если же разрывать соединение нажимая Ctrl+C в терминале с сервером

То можно зарегистрировать обработчик соответствующего сигнала. А в нём установить флаг is_shutdown. Теперь после ctrl-c будет вызван обработчик, потом server:accept завершится с ошибкой (EINTR), это можно словить и если флажок is_shutdown взведён, мы знаем, что всё ОК, пользователь хочет всё закрыть, закрываем и выходим

local con = assert(server:accept())
-- сюда проверку на EINTR и is_shutdown
-- ... хотя нужно сначала убрать там assert, наверное.
assert(con:send("HELLO!!!\n"))
assert(con:receive("*a"))
-- и сюда тоже проверку на EINTR и is_shutdown
-- ... хотя нужно сначала убрать там assert, наверное.
Это с позиции Си.

gag ★★★★★
()
Ответ на: комментарий от urquan

SO_LINGER(с нулевым таймаутом)

Оффтопик. А вне программы эту проблему можно решить?

Есть некрософт, который давно заброшен разработчиком, но к сожалению в продакшне. Время от времени через него проходят данные, не соответствующие ТЗ, и оно намертво виснет. Давно написал ему watchdog, ( проверяет /proc/$pid/schedstat и /proc/$pid/wchan ), который в случае зависания перезапускает ( при необходимости - убивает по kill -9 ). Но часто при повторном запуске вижу в логах то же самое «address already in use», хотя процесс уже убит. В результате watchdog несколько раз в цикле убивает это говно мамонта, пока тому не удаётся взлететь успешно

Можно ли как-то принудительно очистить состояние порта?

router ★★★★★
()
Последнее исправление: router (всего исправлений: 1)
Ответ на: комментарий от router

Увы, нет. Это в парафаии ядра, что и настраивается таймаутами на соединение и свойство сокета, опциями SO_REUSEPORT и SO_LINGER. Но любой из этих вариантов имеет последствия. SO_REUSEPORT был разработан в первую очередь для балансировки между несколькими процессами на одном порту, SO_LINGER - непосредственно таймаут в состоянии TIME_WAIT.

Как выше писали, самый лучший вариант - писать обработчик завершения программы по сигналу.

nickleiten ★★★
()
Ответ на: комментарий от val-amart

убирать time_wait в ноль не очень красиво

А чем это чревато?

навесь обработчик сигнала sigint и с нем рви соединение через tcp rst.

Пример был сферический. Sigint тоже. Скрипт нужен чтобы тестировать разрывы любимого ростелекома. А тут еще ядро свои разрывы добавляет)

makoven ★★★★★
() автор топика
Ответ на: комментарий от router

миллион способов, все зависит от того насколько «схаченное» решение для вас терпимо. можно сниффать сиквенс намбер и слать tcp rst, например из scapy (из удобного), или даже вроде есть специализированные утилиты. если все зависло так что траффика вообще никакого не проходит (ну, хотя бы кип-алайв зафорсить обычно можно — посылаешь со спуфленного адреса) то тогда можно подглядеть нужный сиквенс намбер прямо в ядре, например через systemtap или кастомным модулем. если же вопрос решается SO_LINGER с нулевым таймуатом, то можно его принудительно установить на сокет — пиши модуль для LD_PRELOAD с перехватом bind() например и устанавливай там дополнительные опции на сокет, я так для одного старого проприетарного говна увеличивал очередь необработанных коннектов.

val-amart ★★★★★
()
Ответ на: комментарий от makoven

во-первых, если хочешь получить на свои топики нормальные рабочие ответы, давай в них всю информацию, а то сейчас еще выясниться что речь не про сервер TCP, а еще через две страницу — что и вовсе на самом деле у тебя там винда с SUA.

теперь по делу. если у тебя демон умирает когда говнотелеком рвет соединение, ты что-то делаешь не так. расскажи больше.

val-amart ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.