我想编写一个服务器,客户端可以连接该服务器并接收定期更新而不必轮询。我使用asyncore遇到的问题是,如果在调用dispatcher.writable()时未返回true,则必须等到asyncore.loop超时(默认值为30s)之后。
我尝试解决此问题的两种方法是1)将超时减少到较低的值,或者2)查询连接何时下次更新并生成足够的超时值。但是,如果在“man 2 select_tut”中引用“Select Law”,则会指出:“您应始终尝试使用select()而不超时。”
有一个更好的方法吗?扭曲了吗?我想尝试避免多余的线程。我将在此处包括可变超时示例:
#!/usr/bin/python
import time
import socket
import asyncore
# in seconds
UPDATE_PERIOD = 4.0
class Channel(asyncore.dispatcher):
def __init__(self, sock, sck_map):
asyncore.dispatcher.__init__(self, sock=sock, map=sck_map)
self.last_update = 0.0 # should update immediately
self.send_buf = ''
self.recv_buf = ''
def writable(self):
return len(self.send_buf) > 0
def handle_write(self):
nbytes = self.send(self.send_buf)
self.send_buf = self.send_buf[nbytes:]
def handle_read(self):
print 'read'
print 'recv:', self.recv(4096)
def handle_close(self):
print 'close'
self.close()
# added for variable timeout
def update(self):
if time.time() >= self.next_update():
self.send_buf += 'hello %f\n'%(time.time())
self.last_update = time.time()
def next_update(self):
return self.last_update + UPDATE_PERIOD
class Server(asyncore.dispatcher):
def __init__(self, port, sck_map):
asyncore.dispatcher.__init__(self, map=sck_map)
self.port = port
self.sck_map = sck_map
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind( ("", port))
self.listen(16)
print "listening on port", self.port
def handle_accept(self):
(conn, addr) = self.accept()
Channel(sock=conn, sck_map=self.sck_map)
# added for variable timeout
def update(self):
pass
def next_update(self):
return None
sck_map = {}
server = Server(9090, sck_map)
while True:
next_update = time.time() + 30.0
for c in sck_map.values():
c.update() # <-- fill write buffers
n = c.next_update()
#print 'n:',n
if n is not None:
next_update = min(next_update, n)
_timeout = max(0.1, next_update - time.time())
asyncore.loop(timeout=_timeout, count=1, map=sck_map)
最佳答案
“选择法”不适用于您的情况,因为您不仅具有客户端触发的(纯服务器)事件,还具有时间触发的事件-这正是选择超时的目的。法律应该真正说的是“如果指定了超时,请确保在超时到来时确实必须做一些有用的事情”。该法律旨在防止繁忙等待。您的代码不会等待。
我不会将_timeout设置为最大0.1和下一个更新时间,而是将其最大设置为0.0和下一个超时。 IOW,如果您在执行更新时更新期已到期,则应立即执行该特定更新。
您可以将所有 channel 存储在优先级队列中(按下一个更新时间排序),而不是每次都询问每个 channel 是否要更新,然后只对最早的 channel 运行更新(直到找到更新时间尚未更新的 channel 到达的)。您可以为此使用heapq模块。
您也可以不让每个 channel 询问当前时间,而只轮询一次当前时间,然后将其传递给.update,从而节省一些系统调用。