Ошибка сокета Python: невозможно выбрать на сокете - PullRequest
0 голосов
/ 24 августа 2018

У меня есть класс Python, который включает в себя сокет UDP.Исходный код сокета, как показано ниже:

self.cs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.cs.bind(('', 0))

, а также я определяю функции, которые принадлежат классу, и вызываю сокет для отправки / получения данных.Код в функциях, как показано ниже:

with self.lock:
  while True:
    try:
      self.cs.sendto(packet, (self.host, 80))
      self.cs.settimeout(10)
      response = self.cs.recvfrom(1024)
      break

Весь класс разработан в Singleton и создается главным потоком.Программа отлично работает в начале.Данные можно отправлять и получать в обычном режиме.

Однако через некоторое время, примерно через 1 час, программа выдает ошибку «невозможно выбрать на сокете», когда она запускается на линию «response = self».cs.recvfrom (1024) ".

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

Моя версия python 2.7.13, и, пожалуйста, помогите мне понять, как решить эту проблему.

1 Ответ

0 голосов
/ 24 августа 2018

Исходный код CPython (написанный на C, отсюда и название) показывает, что сообщение создается из функции с именем select_error. Это называется для recvfrom (sock_recvfrom_guts) следующим образом:

...
if (!IS_SELECTABLE(s)) {
    select_error();
    return -1;
}
...

Таким образом, это сообщение об ошибке выдается только тогда, когда макрос IS_SELECTABLE возвращает ложное значение. Макрос определен здесь:

#ifdef HAVE_POLL
/* Instead of select(), we'll use poll() since poll() works on any fd. */
#define IS_SELECTABLE(s) 1
/* Can we call select() with this socket without a buffer overrun? */
#else
/* If there's no timeout left, we don't have to call select, so it's a safe,
 * little white lie. */
#define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || (s)->sock_timeout <= 0.0)
#endif

Из этого мы можем сделать вывод, что ваша система не имеет возможности poll (или, по крайней мере, она не была обнаружена / настроена при сборке CPython); в противном случае IS_SELECTABLE никогда не потерпит неудачу. Вызов _PyIsSelectable_fd является единственным соображением (поскольку мы знаем, что для вашего sock_timeout установлено значение 10), и это определяется как:

/* A routine to check if a file descriptor can be select()-ed. */
#ifdef HAVE_SELECT
 #define _PyIsSelectable_fd(FD) (((FD) >= 0) && ((FD) < FD_SETSIZE))
#else
 #define _PyIsSelectable_fd(FD) (1)
#endif /* HAVE_SELECT */

Это указывает на то, что либо сокет был закрыт где-то по пути (что приводит к установке fd на -1), либо проверяемый сокет имеет дескриптор файла, больший или равный FD_SETSIZE. FD_SETSIZE зависит от платформы, но это самый большой дескриптор файла, который можно использовать в массиве битов дескриптора файла фиксированной длины, обрабатываемом системным вызовом select. На моей Linux-платформе он установлен на 1024. Может быть, вы случайно создаете сокет снова и снова, и в итоге у вас так много дескрипторов открытых файлов, что новый сокет получает дескриптор файла выше FD_SETSIZE?

Чтобы точно узнать, что происходит, я бы поймал исключение, в котором вы видите сообщение об ошибке, и вывел бы значение self.cs.fileno(). Это должно сказать вам значение дескриптора файла во время запуска ошибки.

...