Python模块解析之SocketServer(二)_BaseServer基类
我们从上一篇文章中的类图可以看出 BaseServer 类是SocketServer模块中所有类的基类。BaseServer类规定了整个模块的框架,所以我们先仔细了解BaseServer类
我们先把整个BaseServer的源码贴出来
1 import socket 2 import select 3 import sys 4 import os 5 import errno 6 try: 7 import threading 8 except ImportError: 9 import dummy_threading as threading 10 11 __all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", 12 "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", 13 "StreamRequestHandler","DatagramRequestHandler", 14 "ThreadingMixIn", "ForkingMixIn"] 15 if hasattr(socket, "AF_UNIX"): 16 __all__.extend(["UnixStreamServer","UnixDatagramServer", 17 "ThreadingUnixStreamServer", 18 "ThreadingUnixDatagramServer"]) 19 20 def _eintr_retry(func, *args): 21 """restart a system call interrupted by EINTR""" 22 while True: 23 try: 24 return func(*args) 25 except (OSError, select.error) as e: 26 if e.args[0] != errno.EINTR: 27 raise 28 29 class BaseServer: 30 31 """Base class for server classes. 32 33 Methods for the caller: 34 35 - __init__(server_address, RequestHandlerClass) 36 - serve_forever(poll_interval=0.5) 37 - shutdown() 38 - handle_request() # if you do not use serve_forever() 39 - fileno() -> int # for select() 40 41 Methods that may be overridden: 42 43 - server_bind() 44 - server_activate() 45 - get_request() -> request, client_address 46 - handle_timeout() 47 - verify_request(request, client_address) 48 - server_close() 49 - process_request(request, client_address) 50 - shutdown_request(request) 51 - close_request(request) 52 - handle_error() 53 54 Methods for derived classes: 55 56 - finish_request(request, client_address) 57 58 Class variables that may be overridden by derived classes or 59 instances: 60 61 - timeout 62 - address_family 63 - socket_type 64 - allow_reuse_address 65 66 Instance variables: 67 68 - RequestHandlerClass 69 - socket 70 71 """ 72 73 timeout = None 74 75 def __init__(self, server_address, RequestHandlerClass): 76 """Constructor. May be extended, do not override.""" 77 self.server_address = server_address 78 self.RequestHandlerClass = RequestHandlerClass 79 self.__is_shut_down = threading.Event() 80 self.__shutdown_request = False 81 82 def server_activate(self): 83 """Called by constructor to activate the server. 84 85 May be overridden. 86 87 """ 88 pass 89 90 def serve_forever(self, poll_interval=0.5): 91 """Handle one request at a time until shutdown. 92 93 Polls for shutdown every poll_interval seconds. Ignores 94 self.timeout. If you need to do periodic tasks, do them in 95 another thread. 96 """ 97 self.__is_shut_down.clear() 98 try: 99 while not self.__shutdown_request: 100 # XXX: Consider using another file descriptor or 101 # connecting to the socket to wake this up instead of 102 # polling. Polling reduces our responsiveness to a 103 # shutdown request and wastes cpu at all other times. 104 r, w, e = _eintr_retry(select.select, [self], [], [], 105 poll_interval) 106 if self in r: 107 self._handle_request_noblock() 108 finally: 109 self.__shutdown_request = False 110 self.__is_shut_down.set() 111 112 def shutdown(self): 113 """Stops the serve_forever loop. 114 115 Blocks until the loop has finished. This must be called while 116 serve_forever() is running in another thread, or it will 117 deadlock. 118 """ 119 self.__shutdown_request = True 120 self.__is_shut_down.wait() 121 122 # The distinction between handling, getting, processing and 123 # finishing a request is fairly arbitrary. Remember: 124 # 125 126 # - handle_request() is the top-level call. It calls 127 # select, get_request(), verify_request() and process_request() 128 # - get_request() is different for stream or datagram sockets 129 # - process_request() is the place that may fork a new process 130 # or create a new thread to finish the request 131 # - finish_request() instantiates the request handler class; 132 # this constructor will handle the request all by itself 133 134 def handle_request(self): 135 """Handle one request, possibly blocking. 136 137 Respects self.timeout. 138 """ 139 # Support people who used socket.settimeout() to escape 140 # handle_request before self.timeout was available. 141 timeout = self.socket.gettimeout() 142 if timeout is None: 143 timeout = self.timeout 144 elif self.timeout is not None: 145 timeout = min(timeout, self.timeout) 146 fd_sets = _eintr_retry(select.select, [self], [], [], timeout) 147 if not fd_sets[0]: 148 self.handle_timeout() 149 return 150 self._handle_request_noblock() 151 152 def _handle_request_noblock(self): 153 """Handle one request, without blocking. 154 155 I assume that select.select has returned that the socket is 156 readable before this function was called, so there should be 157 no risk of blocking in get_request(). 158 """ 159 try: 160 request, client_address = self.get_request() 161 except socket.error: 162 return 163 if self.verify_request(request, client_address): 164 try: 165 self.process_request(request, client_address) 166 except: 167 self.handle_error(request, client_address) 168 self.shutdown_request(request) 169 170 def handle_timeout(self): 171 """Called if no new request arrives within self.timeout. 172 173 Overridden by ForkingMixIn. 174 """ 175 pass 176 177 def verify_request(self, request, client_address): 178 """Verify the request. May be overridden. 179 180 Return True if we should proceed with this request. 181 182 """ 183 return True 184 185 def process_request(self, request, client_address): 186 """Call finish_request. 187 188 Overridden by ForkingMixIn and ThreadingMixIn. 189 190 """ 191 self.finish_request(request, client_address) 192 self.shutdown_request(request) 193 194 def server_close(self): 195 """Called to clean-up the server. 196 197 May be overridden. 198 199 """ 200 pass 201 202 def finish_request(self, request, client_address): 203 """Finish one request by instantiating RequestHandlerClass.""" 204 self.RequestHandlerClass(request, client_address, self) 205 206 def shutdown_request(self, request): 207 """Called to shutdown and close an individual request.""" 208 self.close_request(request) 209 210 def close_request(self, request): 211 """Called to clean up an individual request.""" 212 pass 213 214 def handle_error(self, request, client_address): 215 """Handle an error gracefully. May be overridden. 216 217 The default is to print a traceback and continue. 218 219 """ 220 print '-'*40 221 print 'Exception happened during processing of request from', 222 print client_address 223 import traceback 224 traceback.print_exc() # XXX But this goes to stderr! 225 print '-'*40
我们先从BaseServer的介绍文档来看:
Methods for the caller:
- __init__(server_address, RequestHandlerClass) 初始化,传入构建一个Socket服务器所必需的address以及每个请求到达时的处理类
- serve_forever(poll_interval=0.5) 启动服务器,服务器进入一个无限循环的状态,poll_interval用来设置循环间隔时间,默认为0.5秒。
- shutdown() 关闭服务器
- handle_request() # if you do not use serve_forever() 处理请求的方法,如果你没有调用server_forver方法,请求到达时将会调用此方法
- fileno() -> int # for select() 文件描述符,相当于一个句柄。我们知道在Unix中一切皆为文件,系统操纵每个文件时通过 文件描述符对文件来进行文件操纵的。
以上的方法都是一些基础的方法BaseServer里面已经实现,下面的一些方法可能就需要由子类来重新定义或覆盖
Methods that may be overridden:
- server_bind() 服务器绑定,被构造器调用
- server_activate() 服务器激活
- get_request() -> request, client_address 获取请求的socket
- handle_timeout() 服务器超时处理
- verify_request(request, client_address) 验证请求
- server_close() 关闭服务器
- process_request(request, client_address) 处理请求
- shutdown_request(request) 关闭请求
- close_request(request) 关闭请求
- handle_error() 错误处理
子类的方法
Methods for derived classes:
- finish_request(request, client_address)
可能被子类或者实例覆盖的的类变量
Class variables that may be overridden by derived classes or
instances:
- timeout 超时时间
- address_family 地址类型
- socket_type socket类型 TCP/UDP
- request_queue_size (only for stream sockets) 请求队列大小,只是针对 流socket
- allow_reuse_address 是否允许重置监听的地址
Instance variables: 实例变量
- server_address 服务器地址(服务监听的地址)
- RequestHandlerClass 请求处理类
- socket 套接字
"""
从上面BaseSever的介绍中我们已经大致了解了整个SocketSever的设计思路。构建一个SocketServer,定义SocketServer的一些基本操作 启动,关闭,处理请求,超时处理,错误处理。定义一个SocketServer所需要信息和步骤都一样所以它把他放在初始化构造器里面,由我们在创建BaseServer时传入。启动,关闭这些基础功能在BaseServer里面已经实现。针对我们最感兴趣的 "处理请求" 它把请求处理 抽离出来成一个类,让我们自己编写处理类,因为如何处理只有我们自己知道。把变化抽离出来,,这样提高了服务器可扩展功能。针对不同Socket的更详细的方法 当然就由不同的SocketServer子类来重新定义喽。
有了上面的大致认识后,在研究BaseServer的源代码就有一个主体方向了:
1 timeout = None
定义一个 超时 类变量
def __init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address self.RequestHandlerClass = RequestHandlerClass self.__is_shut_down = threading.Event() self.__shutdown_request = False
初始化构造器,定义了 构造了一个SocketServer 所必须的最基本的server_address 以及我们自己编写的RequestHandlerClass类。我们看到一个 __is_shut_down私有成员变量 被赋值了一个 threading.Event()可以看出这个变量是用来控制 服务器关闭的。还有一个__shutdown_request标识是否关闭请求。
1 def server_activate(self): 2 """Called by constructor to activate the server. 3 4 May be overridden. 5 6 """ 7 pass
服务器激活 由子类覆盖实现。
1 def serve_forever(self, poll_interval=0.5): 2 """Handle one request at a time until shutdown. 3 4 Polls for shutdown every poll_interval seconds. Ignores 5 self.timeout. If you need to do periodic tasks, do them in 6 another thread. 7 """ 8 self.__is_shut_down.clear() 9 try: 10 while not self.__shutdown_request: 11 # XXX: Consider using another file descriptor or 12 # connecting to the socket to wake this up instead of 13 # polling. Polling reduces our responsiveness to a 14 # shutdown request and wastes cpu at all other times. 15 r, w, e = _eintr_retry(select.select, [self], [], [], 16 poll_interval) 17 if self in r: 18 self._handle_request_noblock() 19 finally: 20 self.__shutdown_request = False 21 self.__is_shut_down.set()
从这段代码中我们可以看出BaseServer是一个非阻塞的SocketServer.用Threading.Event来控制服务器的关闭,那么self.__is_shutdown是如何来控制服务器的关闭呢?
我们接着看:
1 def shutdown(self): 2 """Stops the serve_forever loop. 3 4 Blocks until the loop has finished. This must be called while 5 serve_forever() is running in another thread, or it will 6 deadlock. 7 """ 8 self.__shutdown_request = True 9 self.__is_shut_down.wait()
从shutdown()方法中我们 通过调用self.__shutdown_request来停止 serve_forever的循环,但是通过调用self.__is_shut_down.wait()方法是它一直处于阻塞状态,知道调用self.__is_shut_down.set()后进程才算结束。也就是说知道处理完所有请求才关闭掉进程。如果server_forver()方法运行在另一个线程中这必须调用该方法否则会出现死锁。
至于用select实现非阻塞的处理.关于threading.Event 和 select.select可以看我之前的专门介绍Event和select的文章。可能现在有点搞不懂,因为这要结合后面的代码 然后串起来 才能够明白。这里我们只需要明白是通过select来实现非阻塞的服务的,具体细节后面会结合代码来讨论。
下面要介绍的两个方法至关重要,我们要结合 server_forver() 来 把整个服务器如何通过非阻塞方式来 接收请求 处理请求 这个最重要的处理步骤弄明白,
我们先来看看 handler_request()的介绍:
# The distinction between handling, getting, processing and
# finishing a request is fairly arbitrary. Remember:
#
一个请求的handling, getting, processing, finishing它们之间的区别是非常重要的。记住:
# - handle_request() is the top-level call. It calls
# select, get_request(), verify_request() and process_request()
# - get_request() is different for stream or datagram sockets
# - process_request() is the place that may fork a new process
# or create a new thread to finish the request
# - finish_request() instantiates the request handler class;
# this constructor will handle the request all by itself
handler_request()方法是最上层的调用.它调用了 select, get_Request(), verify_request() 和 process_request()方法.
get_request()对于 流套接字(面向连接)和 数据包套接字(面向非连接)来说 是不同的.在Python socket编程中对于 stream(流套接字)接收请求调用socket.accept(),对于 datagram(数据包套接字)接收请求时调用socket.recvfrom().所以说这个方法由子类来实现。针对不同的套接字调用不同的方法。
process_request()处理一个请求时可能会创建一个进程或者线程去处理。
finish_request()完成请求 不同的实例所做的处理也不同。这个无关紧要。
1 def handle_request(self): 2 """Handle one request, possibly blocking. 3 4 Respects self.timeout. 5 """ 6 # Support people who used socket.settimeout() to escape 7 # handle_request before self.timeout was available. 8 timeout = self.socket.gettimeout() 9 if timeout is None: 10 timeout = self.timeout 11 elif self.timeout is not None: 12 timeout = min(timeout, self.timeout) 13 fd_sets = _eintr_retry(select.select, [self], [], [], timeout) 14 if not fd_sets[0]: 15 self.handle_timeout() 16 return 17 self._handle_request_noblock()
支持请求超时,如果请求超时(在timeout时间内没有请求到达)则调用超时处理方法 hanler_timeout(),否则调用self._handle_request_noblock()
1 def _handle_request_noblock(self): 2 """Handle one request, without blocking. 3 4 I assume that select.select has returned that the socket is 5 readable before this function was called, so there should be 6 no risk of blocking in get_request(). 7 """ 8 try: 9 request, client_address = self.get_request() 10 except socket.error: 11 return 12 if self.verify_request(request, client_address): 13 try: 14 self.process_request(request, client_address) 15 except: 16 self.handle_error(request, client_address) 17 self.shutdown_request(request)
一个非阻塞的处理请求的方法。我们在回到 server_forever()方法中:
1 self._handle_request_noblock()
我想可能 下面这段代码 会更明白一些。
1 import socket 2 import traceback 3 import select 4 5 EOL1 = b'\n\n' 6 EOL2 = b'\n\r\n' 7 8 socketmap = {} 9 r, w, e = [], [], [] 10 11 response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n' 12 response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n' 13 response += b'Hello, world!' 14 15 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 17 serversocket.bind(('0.0.0.0', 23456)) 18 serversocket.listen(1) 19 # serversocket.setblocking(0) 20 21 listening_fileno = serversocket.fileno() 22 socketmap[listening_fileno] = serversocket 23 print 'listening_fileno', listening_fileno 24 25 try: 26 while True: 27 r, w, e = [], [], [] 28 for fd in socketmap: 29 r.append(fd) 30 w.append(fd) 31 e.append(fd) 32 r, w, e = select.select(r, w, e, 1) 33 for fd in r: 34 request = b'' 35 isocket = socketmap[fd] 36 if fd == listening_fileno: 37 print 'accepting' 38 clientsock, clientaddr = isocket.accept() 39 # clientsock.setblocking(0) 40 cli_fileno = clientsock.fileno() 41 r.append(cli_fileno) 42 w.append(cli_fileno) 43 e.append(cli_fileno) 44 socketmap[cli_fileno] = clientsock 45 else: 46 print 'reading' 47 while EOL1 not in request and EOL2 not in request: 48 request += isocket.recv(1024) 49 print(request.decode()) 50 51 for fd in w: 52 print 'writing' 53 osocket = socketmap[fd] 54 osocket.send(response) 55 56 for fd in e: 57 esocket = socketmap[fd] 58 print 'socket close', fd 59 esocket.close() 60 del socketmap[fd] 61 62 print "no data coming" 63 64 except Exception, e: 65 print traceback.print_exc() 66 serversocket.close()
我们利用了select实现了非阻塞socket服务.
至于 后面的都是一些细节处理
1 def handle_timeout(self): 2 """Called if no new request arrives within self.timeout. 3 4 Overridden by ForkingMixIn. 5 """ 6 pass 7 8 def verify_request(self, request, client_address): 9 """Verify the request. May be overridden. 10 11 Return True if we should proceed with this request. 12 13 """ 14 return True 15 16 def process_request(self, request, client_address): 17 """Call finish_request. 18 19 Overridden by ForkingMixIn and ThreadingMixIn. 20 21 """ 22 self.finish_request(request, client_address) 23 self.shutdown_request(request) 24 25 def server_close(self): 26 """Called to clean-up the server. 27 28 May be overridden. 29 30 """ 31 pass 32 33 def finish_request(self, request, client_address): 34 """Finish one request by instantiating RequestHandlerClass.""" 35 self.RequestHandlerClass(request, client_address, self) 36 37 def shutdown_request(self, request): 38 """Called to shutdown and close an individual request.""" 39 self.close_request(request) 40 41 def close_request(self, request): 42 """Called to clean up an individual request.""" 43 pass 44 45 def handle_error(self, request, client_address): 46 """Handle an error gracefully. May be overridden. 47 48 The default is to print a traceback and continue. 49 50 """ 51 print '-'*40 52 print 'Exception happened during processing of request from', 53 print client_address 54 import traceback 55 traceback.print_exc() # XXX But this goes to stderr! 56 print '-'*40
总结: 其实BaseServer只是大致搭了一个 模型 。控制整体的服务流程 启动服务->接收请求->处理请求 ,对于接收和处理 则 利用 select 实现非阻塞。把请求处理抽离出来,让我们自己实现。当然对于处理 请求 process_request() 它还提供了 让我们自己实现,使我们可以利用 进程和线程来处理 实现更高效。其实只要理解了 select 和上面我贴出的那段代码 BaseServer也就理解啦。后面我们会详细的介绍处理各个子类的实现细节。
posted on 2013-05-04 14:32 Arts&Crafts 阅读(1833) 评论(0) 编辑 收藏 举报