https://www.zybuluo.com/wzhang1117/note/8202
SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
创建服务器的步骤。首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。其次,你必须实例化一个服务器类,传入服务器的地址和请求处理程序类。最后,调用handle_request()(一般是调用其他事件循环或者使用select())或serve_forever()。
HTTPServer.__init__(self, *args, **kwargs)
File "/usr/local/lib/python2.6/SocketServer.py", line 402, in __init__
self.server_bind()
File "/usr/local/lib/python2.6/BaseHTTPServer.py", line 108, in server_bind
SocketServer.TCPServer.server_bind(self)
File "/usr/local/lib/python2.6/SocketServer.py", line 413, in server_bind
self.socket.bind(self.server_address)
File "<string>", line 1, in bind
error: [Errno 98] Address already in use
先贴一段示例代码(主要针对ThreadingTCPServer,其它类似):
from SocketServer import ThreadingTCPServer,StreamRequestHandler
from time import ctime
HOST = ''
PORT = 12345
ADDR = (HOST, PORT)
class MyRequestHandler(StreamRequestHandler):
def handle(self):
print 'connected from:', self.client_address
self.wfile.write('[%s] %s' % (ctime(),self.rfile.readline()))
tcpServer = ThreadingTCPServer(ADDR, MyRequestHandler)#1
print 'waiting for connection'
tcpServer.serve_forever()#2
1.先分析这一行:tcpServer = ThreadingTCPServer(ADDR, MyRequestHandler)
在源代码在中查找ThreadingTCPServer的定义
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
这里使用了Mix-in技术,字面理解就是给TCP类增加了多线程功能,主体还是TCPServer,继续在源代码在中查找TCPServer的定义
class TCPServer(BaseServer):
暂时先不管BaseServer,分析下构造函数
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family, self.socket_type)
if bind_and_activate:
self.server_bind()
self.server_activate()
server_bind()和server_activate()分别调用bind()和listen()函数,熟悉socket编程的应该很清楚
至此,初始化工作就完成了
2.分析:tcpServer.serve_forever()
通过查找TCPServer的定义知道,它本身没有这个成员,这个函数是BaseServer的成员,它的构造函数主要是保存服务器地址ADDR和MyRequestHandler类,下面看函数定义:
def serve_forever(self, poll_interval=0.5):
从字面很容易看出这是一个主循环,还有个时间周期默认为0.5s,主循环主要代码:
while not self.__shutdown_request:
r, w, e = _eintr_retry(select.select, [self], [], [], poll_interval)
if self in r:
self._handle_request_noblock()
通过不断调用select函数,看看自己是否处于可写状态,如果是就调用_handle_request_noblock(),这里有一个问题select函数操作集合的时候有个要求,要么集合本身是描述符,要么他提供一个fileno()接口,返回一个描述符,查找TCPServer的成员函数确实存在这样一个函数,它返回socket的描述符
下面分析_handle_request_noblock(),一般以下划线开头的函数属于内部函数,有点像C++类里面的private函数,一般不用于外部调用,主要内容就两行:
request, client_address = self.get_request()
self.process_request(request, client_address)
get_request()在TCPServer中实现,就是调用accept()函数,返回客户端socket和客户端地址
process_request()也是两行
self.finish_request(request, client_address)###线程阻塞
self.shutdown_request(request)
主要就是完成请求,关闭请求。注意函数里面的注释Overridden by ForkingMixIn and ThreadingMixIn.一会儿再分析。finish_request()函数的工作就是将最开始的MyRequestHandler类初始化
3.下面开始分析BaseRequestHandler
因为MyRequestHandler和StreamRequestHandler都没有构造函数,因此finish_request()执行的是BaseRequestHandler的构造函数
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
具体setup()函数和finish()函数在StreamRequestHandler中实现,而handle函数由MyRequestHandler实现
setup()和finish()主要是设置和清理读写描述符,以便在handle()中可以进行读写操作
4.之前还提到过一个ThreadingMixIn(ForkingMixIn这个在windows平台不支持)
这个类主要是覆盖了BaseServer的process_request()方法,从而实现多线程,因为在原版的process_request()中,函数会阻塞在finish_request()函数中,从而无法处理多个请求,而多线程版的方法中,finish_request()和shutdown_request()在一个新的线程中完成,从而不会阻塞process_request(),主循环得以继续
总结:到这里整个程序运行过程就分析完了,当然还有一些细节,如模块初始化,主线程清理工作等没有仔细分析,但是本文的主要目的是分析优秀代码的设计思路和实现方法,因此忽略这些细节。SocketServer模块的主要设计思路是将我们平时的socket编程两个主要部分进行了分解,一个是主循环监听过程,一个是具体客户端请求处理过程,两个过程分别对应到Server和Request类,Server和Request又进一步抽象成为BaseServer和BaseRequestHandler,在这两个抽象类中完成了对应处理过程,而这些过程的具体实现则分别在不同的具体类中实现,如socket初始化,绑定连接,接受请求,关闭连接等;使用MixIn技术为BaseServer提供了多线程和多进程特性。
ps:我阅读源代码的主要目的是学习优秀代码的设计方法(好的设计方法总是在不断的做解耦和抽象工作),当然也可以学习到很多实现上的技巧。但是直接阅读源代码往往效率很低,很容易被细节所拖累,因此将程序先运行起来,观察到程序的主干路径是一个比较好的方法,根据主干路径逐步提高抽象层次从而逆向分析出原始顶层设计。