Исходный код 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()
. Это должно сказать вам значение дескриптора файла во время запуска ошибки.