2013-04-18 5 views
4

У меня есть простой (не-threaded) скрипт, который прослушивает сокет для данных, анализирует его и использует внутренне SIGALRM для отправки электронной почты с предопределенными внутренними таймерами.python socket recv() и сигналы

проблема во время цикла recv(), возникновение SIGALRM, как представляется, приподнять

socket.error: [Errno 4] Interrupted system call 

и, следовательно, завершение программы.

Я могу обернуть recv() с помощью блока try/except, но мне было интересно, не потеряю ли я какие-либо данные за это время или будет ли буфер предотвращать потерю.

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
s.bind((host, port)) 
while True: 
    try: 
     data = s.recv(2048) 
    except socket.error, e: 
     pass 
    yield data 
s.close() 
return 

ответ

6

Стандартный способ справиться с этим в С является петлей вокруг EINTR. И, хотя не должен быть необходимым в Python, это так.

Вашего кода довольно близко к идиоматическому способу борьбы с этим, за исключением двух вещей:

  • Вы не хотите, чтобы игнорировать всех ошибок, просто EINTR.
  • Вы не можете yield data проигнорировать ошибку таким образом, потому что вы будете повторно использовать предыдущий пакет (если есть) или поднять NameError (если это первый раз через цикл).

Итак:

while True: 
    try: 
     data = s.recv(2048) 
    except socket.error, e: 
     if e.errno != errno.EINTR: 
      raise 
    else: 
     yield data 

Итак, почему вы должны это сделать?

POSIX позволяет практически любой системный вызов возвращать EINTR для определенных видов временного отказа, что включает в себя прерывание сигнала. Это делают многие платформы POSIX. Ожидаемое поведение приложения - это просто повторить попытку (если вы пытаетесь заблокировать вызов) или вернуться к циклу (если вы находитесь в реакторе с запуском уровня). This blog post дает довольно хорошее объяснение, почему POSIX работает таким образом. (Это оправдание после факта, и определенно не фактическое обоснование ...) Также см. the glibc documentation.

Как и большинство языков сценариев, Python должен обертывать все EINTR -общественные вызовы изнутри, поэтому вам не следует об этом думать (если вы не используете сторонние расширения C). Но, к сожалению, у него есть ошибки. Самый последний набор найденных и фиксированных случаев - issue 9867 и issue 12268.

Даже если они, наконец, поймали все, это поможет, если вы можете зависеть от достаточно новой версии Python. Учитывая, что вы используете синтаксис except до 2.6, и последние исправления вошли в некоторые выпуски исправлений 2.7.x и 3.2.x, которые, по-видимому, не сработают для вас.


Есть и другие способы решения этой проблемы, но они сложнее и менее переносимы.Например, вы можете заменить блокировку recv на блокировку pselect и неблокирующую recv, добавить pipe в набор fd вместе с сокетом, заменить все ваши обработчики сигналов функциями, которые просто записывают (один байт) на этот канал , и переместить фактический код обработки сигнала в цикл события. Затем на некоторых платформах вы никогда не получите EINTR. Но это, вероятно, не тот подход, который вы хотите использовать в Python.

Смежные вопросы