基本概念
SocketServer内部使用 IO多路复用以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
原理
1、服务器启动socket监听端口
2、服务器内部利用while循环监视句柄的变化
3、客户端请求
4、服务器为这个请求分配线程或进程(底层调用select)。
SocketServer模块有两个方法ThreadingTCPServer和ForkingTCPServer,分别创建线程或者进程。
ThreadingTCPServer
定义
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
使用方法:
1、创建一个继承自 SocketServer.BaseRequestHandler 的类
2、类中必须定义一个名称为 handle 的方法(名称不能更改)
3、启动ThreadingTCPServer
server代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #!/usr/bin/env python # -*- coding:utf-8 -*- import SocketServer class MyServer(SocketServer.BaseRequestHandler): def handle( self ): #函数名必须是handle,源码BaseRequestHandler预定义; print self .request, self .client_address, self .server #BaseRequestHandler的__init__定义好的; conn = self .request #每个连接的socket conn.sendall( '欢迎致电 10086,请输入1xxx,0转人工服务.' ) Flag = True while Flag: data = conn.recv( 1024 ) if data = = 'exit' : Flag = False elif data = = '0' : conn.sendall( '通话可能会被录音.balabala一大推' ) else : conn.sendall( '请重新输入.' ) if __name__ = = '__main__' : server = SocketServer.ThreadingTCPServer(( '127.0.0.1' , 8009 ),MyServer) #实例化ThreadingTCPServer,其实是继承TCPServer类的初始化,需要传入(server_address, RequestHandlerClass),RequestHandlerClass就是MyServer类。 server.serve_forever() #启动服务,用select一直在循环执行,继承自BaseServer类的方法serve_forever(); |
client代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ( '127.0.0.1' , 8009 ) sk = socket.socket() sk.connect(ip_port) sk.settimeout( 5 ) while True : data = sk.recv( 1024 ) print 'receive:' ,data inp = raw_input ( 'please input:' ) sk.sendall(inp) if inp = = 'exit' : break sk.close() |
ForkingTCPServer
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “进程”,该线程用来和客户端进行交互。
ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程” 和 “进程”。
server代码不同处:
server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyRequestHandler)
变更为:
server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyRequestHandler)
ThreadingTCPServer源码分析(ForkingTCPServer方法类似)
类继承关系图
服务器启动程序后:
1、执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
解释:class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass,ThreadingTCPServer先要执行__init__构造函数,ThreadingMixIn里没有,就去TCPServer类里去找
2、执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
解释:TCPServer构造方法中包含BaseServer.__init__(self, server_address, RequestHandlerClass),所以要把自己定义的类传给BaseServer的构造方法
3、执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
解释:serve_forever是BaseServer类中的方法,里面有个while循环,一直调用select.select(),r, w, e = _eintr_retry(select.select, [self], [], [],poll_interval)
客户端接入:
4、执行BaseServer._handle_request_noblock方法
解释:serve_forever的while循环里有一个判断if self in r:self._handle_request_noblock(),客户端连接句柄发生变化就会把句柄放到r列表里,所以,触发了_handle_request_noblock()。
5、执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
解释:调用process_request方法时,从继承类广度优先原则,所以它先调用ThreadingMixIn类中的process_request
6、执行 ThreadingMixIn.process_request_thread 方法
解释:t = threading.Thread(target = self.process_request_thread,args = (request, client_address))多线程模块方法,调用self.process_request_thread,此时才真正启动了线程。
7、执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用自定义的MyRequestHandler的handle方法)
解释:连接创建完成,此时开始执行handle方法中的内容,开始和客户端交互,执行完,后面再执行shutdown_request方法关闭连接。
源码精简
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import socket import threading import select def process(request, client_address): print request,client_address conn = request conn.sendall( '欢迎致电 10086,请输入1xxx,0转人工服务.' ) flag = True while flag: data = conn.recv( 1024 ) if data = = 'exit' : flag = False elif data = = '0' : conn.sendall( '通过可能会被录音.balabala一大推' ) else : conn.sendall( '请重新输入.' ) sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.bind(( '127.0.0.1' , 8002 )) sk.listen( 5 ) while True : r, w, e = select.select([sk,],[],[], 1 ) print 'looping' if sk in r: print 'get request' request, client_address = sk.accept() t = threading.Thread(target = process, args = (request, client_address)) t.daemon = False t.start() sk.close() |
如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。