python套接字错误:无法在套接字上选择

时间:2018-08-24 14:30:53

标签: python-2.7 sockets

我有一个包含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

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

但是,大约一小时后,该程序在运行到“ response = self.cs.recvfrom(1024)”这一行时收到“无法在套接字上选择”错误。

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

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

1 个答案:

答案 0 :(得分:0)

CPython源代码(用C编写,因此称为名称)表明,消息是由称为select_error的函数产生的。这样调用recvfromsock_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_SETSIZEFD_SETSIZE是特定于平台的,但是与select系统调用处理的定长文件描述符位数组中使用的文件描述符相比,它是最大的文件描述符。在我的linux平台上,它设置为1024。是否可能是您无意中以某种方式一次又一次地创建了套接字,最终您拥有如此多的打开文件描述符,所以新套接字在FD_SETSIZE之上获得了文件描述符?

要确切地了解正在发生的事情,我会在看到错误消息的地方捕获异常,并打印出self.cs.fileno()的值。那应该告诉您在触发错误时的文件描述符值。