2012-02-20 1 views
3

У меня есть поток, который прослушивает новые соединенияC++ Linux принимает() блокировку после сокет закрыт

new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size); 

и другой поток, который закрывает Listen_fd когда, когда пришло время, чтобы закрыть программу. Однако после того, как Listen_fd закрыт, он все еще блокируется. Когда я использую GDB для попытки и отладки accept(), он не блокируется. Я думал, что это может быть проблемой для SO_LINGER, но он не должен быть включен по умолчанию и не должен меняться при использовании GDB. Любая идея, что происходит, или какое-либо другое предложение закрыть листинг сокета?

ответ

1

Это обходное решение, но вы могли бы select на Listen_fd с таймаутом, и если произошел тайм-аут, проверьте, что вы собираетесь закрыть программу. Если это так, выйдите из цикла, если нет, вернитесь к шагу 1 и выполните следующий select.

6

Поведение accept при вызове чего-то, что не является допустимым сокетом FD, не определено. «Недействительный сокет FD» включает числа, которые были когда-то действительными сокетами, но с тех пор были закрыты. Вы можете сказать «но Borealid, он должен возвращать EINVAL!», Но это не гарантировано - например, тот же номер FD может быть переназначен в другой сокет между вашими звонкамии accept.

Таким образом, даже если вы должны были изолировать и исправить то, что делает вашу программу неудачной, вы все равно можете снова начать сбой в будущем. Не делайте этого - исправьте ошибку, из-за которой вы пытаетесь подключиться к закрытому разъему accept.

Если вы имели в виду, что вызов, который был ранее сделаны acceptпродолжает блокировку после close, то, что вы должны сделать, это отправить сигнал в поток, который блокируется в accept. Это даст ему EINTR, и он может полностью отключиться - и затем закройте розетку. Не закрывайте его из потока, отличного от того, который его использует.

+0

+1 Я думаю, последний сценарий. Тема 1 блокирует принятие. Поток 2 закрывает гнездо. Thread1 блокируется навсегда. – Duck

+0

Это правильно. Обратите внимание, что вы, вероятно, захотите установить пустой обработчик сигнала для используемого сигнала. – caf

+0

О, эта часть POSIX действительно сосет. – thodg

0

Вы проверяете возвращаемое значение закрытия? Из linux manpages, (http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html) «Возможно, неразумно закрывать дескрипторы файлов, пока они могут быть использованы посредством системных вызовов в других потоках в одном и том же процессе. Поскольку дескриптор файла может быть повторно использован, существуют некоторые неясные условия гонки, которые могут вызвать непреднамеренные побочные эффекты ». Вы можете использовать select вместо accept и ждать некоторого события от другого thead, а затем закрыть сокет в потоке прослушивателя.

3

Использование: sock.shutdown (socket.SHUT_RD)

Тогда accept вернется EINVAL. Никаких уродливых сигналов поперечной резьбы не требуется!

Из документации Python: «Примечание close() освобождает ресурсы, связанные с подключением, но не обязательно закрыть соединение немедленно Если вы хотите, чтобы закрыть соединение своевременным способом, называют shutdown() перед close()

http://docs.python.org/3/library/socket.html#socket.socket.close

Я столкнулся с этой проблемой лет назад, в то время как программирование на C. Но я только нашел решение сегодня, после запуска в ту же самую проблему в Python, и обдумывая, используя сигналы (Тьфу!), И ТОГДА вспомнив записку о shutdown!

Что касается комментариев, которые говорят, что вы не должны закрывать/использовать сокеты по потокам ... в CPython блокировка глобального интерпретатора должна защищать вас (при условии, что вы используете файловые объекты, а не сырые целочисленные дескрипторы файлов).

Вот пример кода:

import socket, threading, time 

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
sock.bind (('', 8000)) 
sock.listen (5) 

def child(): 
    print ('child accept ...') 
    try: sock.accept() 
    except OSError as exc : print ('child exception %s' % exc) 
    print ('child exit') 

threading.Thread (target = child).start() 

time.sleep (1) 
print ('main shutdown') 
sock.shutdown (socket.SHUT_RD) 

time.sleep (1) 
print ('main close') 
sock.close() 

time.sleep (1) 
print ('main exit') 
+2

Отлично, спасибо! – Guido

+1

У меня есть программа на C++, которая открывает сокет для прослушивания в основном потоке и порождает дочерний поток для приема входящих соединений. Основной поток должен отключить поток акцептора после приема сигнала. 'close (s)' не работает, потому что 'accept (...)' продолжает блокироваться в закрытом сокете, но 'shutdown (s, SHUT_RD)' делает 'accept (...)' звонок залога немедленно с 'EINVAL'. Идеальное решение, и оно работает, даже если акцепторный поток находится между вызовами 'accept (...)' при завершении работы. Благодаря! –