首页 > 解决方案 > python套接字错误:无法在套接字上选择

问题描述

我有一个包含 UDP 套接字的 python 类。套接字初始代码如下:

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

整个类是单例设计的,由主线程创建。该程序在开始时运行良好。可以正常发送和接收数据。

但是,过了一会儿,大约 1 小时后,程序在运行到“response = self.cs.recvfrom(1024)”行时出现“无法在套接字上选择”的错误。

我已经用谷歌搜索了几天,但仍然不知道为什么以及如何触发此错误。

我的 python 版本是 2.7.13,请帮助我了解如何处理这个问题。

标签: python-2.7sockets

解决方案


CPython 源代码(用 C 语言编写,因此得名)显示消息是由名为select_error. 像这样调用recvfrom( sock_recvfrom_guts) :

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

IS_SELECTABLE因此,仅当宏返回 false 值时才会触发该错误消息。宏在这里定义:

#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(). 这应该告诉您触发错误时的文件描述符值。


推荐阅读