- Как правильно закрывать сокетное соединение?
- Re: Как правильно закрывать сокетное соединение?
- Re: Как правильно закрывать сокетное соединение?
- Как принудительно закрыть сокет в TIME_WAIT
- Ответ 1
- Ответ 2
- Ответ 3
- Ответ 4
- Как ПРАВИЛЬНО закрывать сокеты и не только. Руководство.
- Убить tcp соединение в linux
- Как закрыть socket linux
Как правильно закрывать сокетное соединение?
Вопрос такой: имеется приложение-сервер, слушает сетевые запросы с помощью сокета. Сокет создается (socket), привязывается к хосту и порту (bind), включается прослушка (listen) и отменяется блокирование (через fcntl ставится O_NONBLOCK), чтобы accept в случае отсутствия запросов не блокировал работу приложения (есть работа кроме слушания).
Соответственно, при подключении клиента создается новый сокет, из него чего-то читается, в него чего-то пишется, потом он закрывается. На стороне сервера. Чтобы клиент не ждал вечно новых сообщений от сервера, мне приходится делать shutdown для сокета — это нормально? Пробовал от сервера слать EOF, закрывать close’ом — клиент все равно ждал до бесконечности.
Далее. Когда я прибиваю сервер и пытаюсь запустить его по новой, bind отваливается с кодом ошибки EINVAL (в руководстве расшифровка — «сокет уже привязан к этому адресу»). Если подождать некоторое время, то вновь становится возможным запускать сервер на тот же хост/порт.
Прибиваю сервер я сигналом SIG_TERM, а обработчик написать поленился и, соответственно, close для сокета не делаю. Проблема в этом? Разве при завершении процесса его ресурсы не должны освобождаться сами?
Re: Как правильно закрывать сокетное соединение?
Понятия сокетное соединение не существует
выражайся коректнее о каком протоколе идет речь,
когда у тебя подключен клиент и на серваке ты делаешь close(sd)
система посылает клиенту ресет и клиент это должен видеть
если конешно связь не нарушена, проверь правильно ли ты отслеживаешь
состояние сокета на клиенте.
когда SIG_KILL тогда освобождется все.
всунуть в обработчик пару close(sd) не так трудно не ленись
Re: Как правильно закрывать сокетное соединение?
Источник
Как принудительно закрыть сокет в TIME_WAIT
Я запускаю определенную программу на linux, которая иногда аварийно завершается. Если после этого быстро открыть ее, она слушает сокет 49201, а не 49200, как в первый раз. netstat показывает, что 49200 находится в состоянии TIME_WAIT.
Есть ли программа, которую можно запустить, чтобы немедленно заставить этот сокет выйти из состояния TIME_WAIT?
Ответ 1
Transmission Control Protocol (TCP) разработан как двунаправленный, упорядоченный и надежный протокол передачи данных между двумя конечными точками (программами). В данном контексте термин « надежный » означает, что он будет повторно передавать пакеты, если они будут потеряны в процессе передачи. TCP гарантирует надежность, посылая обратно пакеты подтверждения (ACK) для одного или нескольких пакетов, полученных от аналога.
То же самое относится и к управляющим сигналам, таким как запрос/ответ на завершение. RFC793 определяет состояние TIME-WAIT следующим образом:
TIME-WAIT — представляет собой ожидание достаточного времени, чтобы убедиться, что удаленный TCP получил подтверждение своего запроса на разрыв соединения.
TCP — это протокол двунаправленной связи, поэтому, когда соединение установлено, нет разницы между клиентом и сервером. Кроме того, любой из них может объявить о выходе из соединения, и для полного закрытия установленного TCP-соединения оба соединения должны договориться о закрытии.
Назовем первого, кто объявляет о прекращении соединения, активным замыкающим, а другого — пассивным замыкающим. Когда активный доводчик посылает FIN, состояние переходит в FIN-WAIT-1. Затем он получает ACK на отправленный FIN, и состояние переходит в FIN-WAIT-2. Получив FIN также от пассивного доводчика, активный доводчик отправляет ACK на FIN, и состояние переходит в TIME-WAIT. Если пассивный доводчик не получил ACK на второй FIN, он повторно передает FIN-пакет.
RFC793 устанавливает TIME-OUT , равным удвоенному времени жизни максимального сегмента, или 2MSL. Поскольку MSL, максимальное время, в течение которого пакет может блуждать по и нтернету, установлено в 2 минуты, 2MSL равно 4 минутам. Поскольку нет ACK на ACK, активный досылатель не может сделать ничего, кроме как подождать 4 минуты, если он правильно придерживается протокола TCP/IP, на случай, если пассивный отправитель не получил ACK на свой FIN (теоретически).
В реальности пропущенные пакеты, вероятно, редки и очень редки, если все это происходит в пределах локальной сети или в пределах одной машины.
Чтобы ответить на вопрос дословно : « Как принудительно закрыть сокет в TIME_WAIT? » , я буду придерживаться своего первоначального ответа:
Практически говоря, я бы запрограммировал его так, чтобы он игнорировал состояние TIME-WAIT, используя опцию SO_REUSEADDR. Что именно делает SO_REUSEADDR?
Эта опция сокета сообщает ядру, что , даже если этот порт занят (находится в состоянии TIME_WAIT), все равно используйте его повторно. Если он занят, но в другом состоянии, вы все равно получите ошибку « адрес уже используется » . Это полезно, если ваш сервер был выключен, а затем сразу же перезапущен, а сокеты на его порту все еще активны. Вы должны знать, что , если поступят неожиданные данные, это может запутать ваш сервер, хотя такое маловероятно.
Ответ 2
Насколько я знаю, нет способа принудительно закрыть сокет, кроме написания лучшего обработчика сигналов в вашей программе, но есть файл /proc, который управляет временем тайм — аута. Файл имеет следующий вид:
и вы можете установить тайм-аут в 1 секунду, выполнив следующее:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
Однако на этой странице содержится предупреждение о возможных проблемах с надежностью при установке этой переменной.
Существует также соответствующий файл:
который контролирует возможность повторного использования сокетов TIME_WAIT (предположительно без какого-либо тайм — аута).
Кстати, документация ядра предупреждает вас не изменять ни одно из этих значений без «совета/просьбы технических экспертов». Программа должна была быть написана для попытки привязки к порту 49200 и последующего увеличения на 1, если порт уже используется. Поэтому, если у вас есть контроль над исходным кодом, вы можете изменить это поведение, чтобы подождать несколько секунд и повторить попытку на том же порту, вместо инкремента.
Ответ 3
Другой вариант — использовать опцию SO_LINGER с тайм-аутом 0. Таким образом, при закрытии сокета он будет закрыт принудительно, посылая RST, а не переходя в режим закрытия FIN/ACK. Это позволит избежать состояния TIME_WAIT и может быть более подходящим для некоторых случаев.
Ответ 4
Альтернативным решением может быть использование надежного прокси-сервера или программы переадресации портов, которая прослушивает порт 49200, а затем переадресует соединение на один из нескольких экземпляров вашей менее надежной программы, использующих разные порты. Например, HAPROXY.
Кстати, порт, через который вы подключаетесь, довольно высокий. Вы можете попробовать использовать неиспользуемый порт чуть выше диапазона 0-1024. Ваша система с меньшей вероятностью будет использовать более низкий номер порта в качестве него.
Мы будем очень благодарны
если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.
Источник
Как ПРАВИЛЬНО закрывать сокеты и не только. Руководство.
На форуме не раз поднимался вопрос о правильном закрытии сокетов,
и я решил разместить небольшой труд об этом и дать ссылку на ОЧЕНЬ хороший
документ о unix-socket (в конце).
Close() закрывает сокет, но не закрывает соединение. Поэтому, при попытке
прибайндиться к нему появляются ошибки типа «socket error: address already in use».
Сохранение соединения бывает необходимо в мультитриадных приложениях.
Рассмотрим простой пример клиента. При установлении соединения с сервером
он форкается, затем дитё (порождённый процесс) посылает на сервер вводимые
с клавы данные пока не получит EOF. При получении EOF дитё выполняет close(),
т.е. закрывает сокет (но не соединение !). А родитель по этому же соединению
может принять ответ сервера. Удобно . Таким образом при использовании сlose()
ядро не обрывает соединение, а только закрывает сокет процесса, выполнившего close().
Ядро не закроет соединение до тех пор, пока хоть один процесс прибайнден к нему.
И даже если все процессы завершатся, ядро какое-то время продержит соединение
открытым (при условии что все процессы использовали только close() ).
Ядро переведет соединение в состояние TIME_WAIT. Как долго ядро продержит
его в этом состоянии зависит от конкретной реализации стека протоколов TCP/IP.
В среднем от 30 секунд до 4 минут. И в это время к этому
адресу (паре IP адрес/порт) нельзя будет прибайндиться. При выполнении
shutdown(sock, 2) вы сообщаете ядру, что больше не намерены ни читать, ни писать
из/в соединение и ядро закрывает соединение и освобождает адрес (пару IP адрес/порт).
Теперь к этому адресу можно прибайндиться. Получается, что shutdown(sock, 2) это
что-то вроде un_bind(). Если вы завершаете процесс и больше не намерены использовать
соединение, то вы должны выполнить shutdown(sock, 2) и затем close(sock). Будьте
внимательны при выполнении shutdown(sock, 2), если вы завершаете родительский
процесс не дожидаясь завершения порожденных процессов. После этой команды НИ ОДИН
процесс, прибайнденный к этому соединению(адресу), не сможет использовать его.
Они получат EOF при попытке чтения и SIGPIPE при попытке записи (возможно с опозданием,
пока буфер сокета не будет заполнен).
Вот вобщем-то и всё. Здесь я привел вольный перевод документа, который вы можете
найти на
ftp.gigabitsale.com/pub/old_stuff/new/doc/unix-socket-faq.usenet
и на
ftp.tura.ru/pub/old_stuff/new/doc/unix-socket-faq.usenet
Очень рекомендую всем. Начинающим он поможет понять природу unix-socket,
а более опытным поможет избежать ошибок и недоразумений.
ЗЫ. Не ищите команду un_bind(), её не существует 😉
Источник
Убить tcp соединение в linux
У меня есть какое-то мертвое соединение в одном приложении, которое находится в зависшем состоянии, если клиентская машина мертва.
Есть ли способ прекратить эти опции из командной строки Linux без перезагрузки сервера?
После поиска я нашел решение под названием tcpkill. Но это не сработает для меня. Так как он постоянно блокирует этот ip.
Чтобы «убить» сокет, вы должны отправить пакет сброса TCP. Чтобы отправить его (и принять другую сторону), вы должны знать фактический порядковый номер TCP.
1) Уже упомянутый tcpkill метод изучает номер SEQ путем пассивного прослушивания в сети и ожидания получения действительных пакетов этого соединения. Затем он использует полученный номер SEQ для отправки пакетов RSET обеим сторонам. Однако, если соединение неактивно / зависло и данные не передаются, оно ничего не будет делать и будет ждать вечно.
2) Другой метод использует скрипт perl, который называется killcx ( ссылка на Sourceforge ). Это активно отправляет поддельные пакеты SYN и узнает номер SEQ из ответа. Затем он отправляет пакеты RSET так же, как tcpkill .
Альтернативный подход (основанный на том, чего вы хотите добиться) — использовать gdb отладчик для подключения к процессу, владеющему этим сокетом / соединением, и close() выполнять системный вызов от его имени — как подробно описано в этом ответе .
Если вы хотите работать только с зависшими соединениями (другая сторона не работает), существуют различные таймауты (например, TCP keepalive), которые должны автоматически закрывать такие соединения, если в системе правильно настроены.
Источник
Как закрыть socket linux
По THIS я хочу попробовать что-то новое — закрыть сокет с использованием некоторого системного вызова.
Ситуация в двух словах — невозможно установить тайм-аут запроса библиотеки mysql (C API, обратитесь к ссылке для получения дополнительной информации), поэтому я хочу попробовать закрыть сокет, чтобы увидеть, как отреагирует библиотека. Возможно, это не очень хорошая идея, но все же хочу попробовать.
Вот что я сделал — есть еще один запущенный поток — таймер. Итак, после определенного времени ожидания (скажем, 10 секунд), если нет ответа, я хочу закрыть сокет. Структура MYSQL имеет член net , который также является структурой, и содержит` fd`. Но когда я пытаюсь сделать это:
Ничего не произошло. Возвращаемые значения из shutdown и` close` равны 0 , но сокет все еще открыт (потому что после 60 секунд ожидания возвращается результат из БД, это означает, что клиент mysql все еще ожидает ответа от БД ,
EDIT * — Да, запущена транзакция, пока я пытаюсь закрыть сокет. Но это настоящая проблема — я не могу ни завершить запрос, ни закрыть соединение, ничего, и я не хочу ждать весь тайм-аут, который составляет 20 минут и 30 секунд, или что-то вроде этого. Вот почему я ищу грубую силу .. :/
3 Answer
Просто выстрел в темноте, но убедитесь, что вы отменяете / прекращаете любые текущие транзакции. Я не знаком с MySQL C API, но думаю, что есть способ проверить наличие активных соединений / запросов. Возможно, вам не удастся закрыть сокет просто потому, что все еще есть запущенные объекты, и их необходимо привести в какое-то «разрешенное» состояние, будь то зафиксированное или откатанное. Я бы начал там и посмотрел, что происходит. Вы действительно не хотите отключать стиль сокета «грубой силы», если у вас все равно есть что-то ожидающее, потому что ваши данные впоследствии не будут в надежном «состоянии» — вы не будете знать, какие транзакции были успешными, а какие — нет, хотя я предположил бы, что MySQL откатит любые ожидающие транзакции, если соединение внезапно оборвалось.
РЕДАКТИРОВАТЬ *: Из того, что я нашел с помощью Googling «остановка убегающего запроса MySQL», консенсус, похоже, заключается в том, чтобы попросить MySQL завершить поток убегающего / долго выполняющегося запроса
Я полагаю, что идентификатор потока доступен вам в структуре данных MySQL, содержащей сокет. Возможно, вы захотите попробовать это, хотя IIRC для этого требует привилегий суперпользователя.
EDIT # 2 *: Очевидно, MySQL предоставляет отказоустойчивый механизм, который будет перезапускать закрытое соединение, поэтому принудительное отключение сокета фактически не завершит запрос. Как только вы закроете его, MySQL откроет другой и попытается завершить запрос. Отключение этого параметра позволит вам закрыть сокет и заставить запрос завершиться.
Комментарии ниже показывают, как был найден ответ, и мыслительный процесс, связанный с ним.
Источник