我想编写一个服务器,客户端可以连接该服务器并接收定期更新而不必轮询。我使用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,从而节省一些系统调用。

09-27 03:19
查看更多