在为Python服务器添加超时时出现非阻塞错误

6

我正在用Python编写一个简单的TCP服务器,并尝试输入超时时间。我的当前代码:

import socket


def connect():
    HOST = ''                 # Symbolic name meaning the local host
    PORT = 5007             # Arbitrary non-privileged port
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(1)
    s.settimeout(5)
    conn, addr = s.accept()
    print 'Connected by', addr
    return conn

conn = connect()

while 1:
    data = conn.recv(1024)
    if not data: break
    print data
conn.close()

问题出在我尝试连接时,当代码执行到 data = conn.recv(1024) 这一行时出现了错误。

error: [Errno 10035] A non-blocking socket operation could not be completed immediately

代码可以在没有超时的情况下正常运行。


我非常推荐使用SocketServer模块。它使得切换到多线程等变得容易。同时,SimpleHTTPServer模块也使用了它。 - User
2
你弄清楚为什么会引发异常了吗? - AmitE
4个回答

4
尝试在套接字上设置超时和连接阻塞。像这样:

尝试在套接字上设置timeout和连接的blocking。就像这样:

import socket 

def connect():
    HOST = ''                 # Symbolic name meaning the local host
    PORT = 5007             # Arbitrary non-privileged port
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(5)
    s.bind((HOST, PORT))
    s.listen(1)  
    return s

s = connect()

while 1:
    conn, addr = s.accept()
    print 'Connected by', addr
    conn.setblocking(1)
    data = conn.recv(1024)
    conn.close()
    if not data: break
    print data
s.close()

2
它运行正常,您有什么想法为什么添加超时会导致首次出错? - user3582887
1
此外,这个错误只会在Windows系统中出现。当在Linux上运行时,“conn.setblocking(1)”不是必需的,超时功能可以正常工作。 - user3582887

2
您可以打开阻止功能:
    # ...
    conn.setblocking(1)
    return conn
# ...

2
这根本没有解释为什么设置超时会导致它变成非阻塞的。就我所知,Python文档中肯定没有这个说明,我很好奇为什么会发生这种情况。 - Fulluphigh
5
设置超时时间可以使其非阻塞。根据文档,s.settimeout(0.0) 等同于 s.setblocking(0);s.settimeout(None) 等同于 s.setblocking(1)。参见 https://docs.python.org/2/library/socket.html#socket.socket.settimeout。 - AmitE

1

我30分钟前遇到了同样的问题。找到了一个简单而不雅的解决方法......如果你让套接字休息一下,通过使用time.sleep(1),捕获10035错误并重试,它可以工作。我正在使用2.7.5...也许这是一个已经被修复的bug。不太确定。

代码示例......请理解这只是我使用的非常简单的测试代码(每次只接收1个字节)。所以's'是带有10秒超时的套接字,'numbytes'是我需要的字节数...

def getbytes(s,numbytes):
     din = ''
     socketerror10035count = 0
     while True:
          try:
               r = s.recv(1).encode('hex')
               din += r
               if len(din)/2 == numbytes:
                    print 'returning',len(din)/2, 'bytes'
                    break
          except socket.timeout as e:
               din = 'socket timeout'
               break
          except socket.error as e:
               if e[0] == 10035 and socketerror10035count < 5:
                    socketerror10035count = socketerror10035count  +1
                    time.sleep(1)
               else:
                    din = 'socket error'
                    break
          except:
               din = 'deaddead'
               break
     return din

0

对于 Python 3 及以上版本,引用 e 作为可脚本化对象的上述代码需要更改为 "e.errno"。当然,print 语句需要在参数周围加上括号。

此外,您可能希望将 "except socket.error as e:" 行更改为 "except BlockingIOError as e:"。但是,在 Windows 上的 Python 3.8.5 下,该代码可以正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接