第二章主要在上一章的基础上介绍了以下内容:
1. ForkingMixIn
2. ThreadingMixIn
3. select.select
4. select.epoll
5. Diesel库
ForkingMixIn 和 ThreadingMixIn属于socketserver(python2是SocketServer)模块,该模块能够简化编写web服务器的工作。其包含四种基本的服务器class:
TCPServer 使用TCP协议,在服务器和客户端之间建立持续的连续,安全;
UDPServer 使用UDP协议,采用数据包的方法在服务器和客户端之间传递数据,有丢失包的可能,但是传输速度很快;
UnixStreamServer和UnixDatagramServer 比较少使用, 分别与TCPServer、UDPServer相似,但是基于Unix上定义的套接字,在其他平台上不能使用。
图1. Servers的继承关系
以上四种类都是同步的,即下一个请求开始前,上一个请求必须完成。显然,他们并不适用于当请求需要较长处理时间的情况。为每个请求创建单独的进程或线程的方式可以解决这个问题,即实现服务器和客户端的异步通信,为此socketserver模块添加了ForkingMixIn和ThreadingMixIn两个类。
为了创建一个服务,首先通过继承BaseRequestHandler类并重写其handler()方法得到一个句柄类,它将用来处理到达服务器的请求;
class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):
然后以服务器地址和上一步得到的句柄实例化前面继承自ForkingMixIn/ThreadingMixIn和TCPServer/UDPServer/UnixStreamServer/UnixDatagramServer的server,
class ForkingServer(SocketServer.ForkingMixIn, SocketServer.TCPServer,):passserver = ForkingServer((SERVER_HOST, SERVER_PORT), ForkingServerRequestHandler)
注意:ForkingMixIn必须写在TCPServer前面,因为它重载了TCPServer类的方法。
最后,调用handler_request()或者server_forever()方法来开始处理请求。
server_thread = threading.Thread(target=server.serve_forever)
值得注意的是,如果server类继承自ThreadingMixIn,则需要明确指定遇到异常停止时的处理方法,ThreadingMixIn类定义了daemon_threads方法,其指定了服务器是否需要等待直到所有线程终止,默认为False。
server_thread.setDaemon(True)
下面是一个简单的例子:
import osimport socketimport threadingimport SocketServerSERVER_HOST = 'localhost'SERVER_PORT = 0 # tells the kernel to pick up a port dynamicallyBUF_SIZE = 1024ECHO_MSG = 'Hello echo server!'class ForkedClient():""" A client to test forking server"""def __init__(self, ip, port):# Create a socketself.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# Connect to the serverself.sock.connect((ip, port))def run(self):""" Client playing with the server"""# Send the data to servercurrent_process_id = os.getpid()print 'PID %s Sending echo message to the server : "%s"' %(current_process_id, ECHO_MSG)sent_data_length = self.sock.send(ECHO_MSG)print "Sent: %d characters, so far..." %sent_data_length# Display server responseresponse = self.sock.recv(BUF_SIZE)print "PID %s received: %s" % (current_process_id,response[5:])def shutdown(self):""" Cleanup the client socket """self.sock.close()class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):def handle(self):# Send the echo back to the clientdata = self.request.recv(BUF_SIZE)current_process_id = os.getpid()response = '%s: %s' % (current_process_id, data)print "Server sending response [current_process_id: data] =[%s]" %responseself.request.send(response)
returnclass ForkingServer(SocketServer.ForkingMixIn,SocketServer.TCPServer,):"""Nothing to add here, inherited everything necessary fromparents"""passdef main():# Launch the serverserver = ForkingServer((SERVER_HOST, SERVER_PORT),ForkingServerRequestHandler)ip, port = server.server_address # Retrieve the port numberserver_thread = threading.Thread(target=server.serve_forever)server_thread.setDaemon(True) # don't hang on exitserver_thread.start()print 'Server loop running PID: %s' %os.getpid()# Launch the client(s)client1 = ForkedClient(ip, port)client1.run()client2 = ForkedClient(ip, port)client2.run()# Clean them upserver.shutdown()client1.shutdown()client2.shutdown()server.socket.close()if __name__ == '__main__':main()