Как ПРАВИЛЬНО закрывать сокеты и не только. Руководство.
На форуме не раз поднимался вопрос о правильном закрытии сокетов,
и я решил разместить небольшой труд об этом и дать ссылку на ОЧЕНЬ хороший
документ о 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(), её не существует ;)