非阻塞或异步编程python

例如,对于一个聊天室来说,因为有多个连接需要同时被处理,所以很显然,阻塞或同步的方法是不合适的,
这就像买票只开了一个窗口,佷多人排队等一样。
那么我们如何解决这个问题呢?
主要有三种方法:

forking、
     threading、
     异步I/O。

Forking和threading的方法非常简单,通过使用SocketServer服务类的min-in类就可以实现。forking只适用于类Unix平台;threading需要注意内存共享的问题。

异步I/O如果底层的方法来实现是有点困难的。
要简单点,我们可以考虑使用标准库中的框架或Twisted
(Twisted是一个非常强大的异步网络编程的框架)。

一、用ScoketServer实现Forking和threading

下面我们使用两个例子来分别创建forking服务器和threading服务器。

Forking 服务器:

from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
class Server(ForkingMixIn, TCPServer): pass
class Handler(StreamRequestHandler):
     def handle(self):
         addr = self.request.getpeername()
         print 'Got connection from', addr
        server = Server(('', 1234), Handler)
 server.serve_forever()

threading服务器:

from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler
class Server(ThreadingMixIn, TCPServer): pass
class Handler(StreamRequestHandler):
     def handle(self):
         addr = self.request.getpeername()
         print 'Got connection from', addr
        server = Server(('', 1234), Handler)
 server.serve_forever()

二、使用select实现异步I/O

所谓异步I/O,打个比方,就是如果一大群人都想你听他说话,那么你就给他们每人一分钟的时间说,大家轮流说,没说完的待会儿轮到时再继续说。也就是一个时间片的方法。

要实现异步I/O,我们可以通过使用框架asyncore/asynchat或Twisted,它们都是基于select函数或poll函数(poll只适于类Unix系统)的。select和poll函数都来自select模块。

select函数要求三个必须序列作为参数和一个可选的以秒为单位的超时值。序列中是表示文件描述符的整数值,它们是我们要等待的连接。这三个序列 是关于输入、输出和异常条件的。如果超时值没有给出的话,select将处于阻塞状态(也就是等待)直到有文件描述符准备动作。如果超时值给出了,那么 select只阻塞给定的时间。如果超时值是0的话,那么将不阻塞。select返回的值是一个由三个序列组成的元组,它们分别代表相应参数的活动的子 集。例如,第一个序列返回的是用于读的输入文件描述符构成的序列。

序列可以包含文件对象(不适于Windows)或socket。下面这个例子创建一个使用select去服务几个连接的服务器(注意:服务端的 socket自身也提供给了select,以便于它能够在有新的连接准备接受时发出信号通知)。这个服务器只是简单地打印接受自客户端的数据。你可以使用 telnet(或写一个基于socket的简单的客户端)来连接测试它。

select server
import socket, select
s = socket.socket()
 host = socket.gethostname()
 port = 1234
 s.bind((host, port))s.listen(5)
 inputs = [s]
 while True:
     rs, ws, es = select.select(inputs, [], [])
     for r in rs:
         if r is s:
             c, addr = s.accept()
             print 'Got connection from', addr
             inputs.append(c)
         else:
             try:
                 data = r.recv(1024)
                 disconnected = not data
             except socket.error:
                           if disconnected:
                 print r.getpeername(), 'disconnected'
                 inputs.remove(r)
             else: