python16_day09【Select多路复用】

一、select多路复用

 1 句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
 2  
 3 参数: 可接受四个参数(前三个必须)
 4 返回值:三个列表
 5  
 6 select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
 7 1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
 8 2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
 9 3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
10 4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
11    当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。
 1 import select
 2 import socket
 3 
 4 sk1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 5 sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 6 sk1.bind(('127.0.0.1', 8001),)
 7 sk1.listen(5)
 8 
 9 sk2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
10 sk2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
11 sk2.bind(('127.0.0.1', 8002),)
12 sk2.listen(5)
13 
14 inputs = [sk1, sk2]
15 w_inputs = []
16 while True:
17     # IO多路复用,同时监听多个SOCKET对象.
18     r, w, e = select.select(inputs, w_inputs, inputs, 0.05)  #
19 
20     for obj in r:
21         print("obj::::", obj)
22         print("sk1::::", sk1)
23         if obj in [sk1, sk2]:
24             # 新连接
25             print("新连接....", obj)
26             conn, addr = obj.accept()
27             inputs.append(conn)
28         else:
29             # 有连接用户发送消息来了...
30             print("有用户发数据了", obj)
31             try:
32                 data = obj.recv(1024)
33             except Exception as e:
34                 data = ""
35             if data:
36                 w_inputs.append(obj)
37                 # obj.sendall(data)
38             else:
39                 obj.close()
40                 inputs.remove(obj)
41                 w_inputs.remove(obj)
42 
43     for obj in w_inputs:
44         obj.sendall(b'ok')
45         w_inputs.remove(obj)
利用select实现伪同时处理多个Socket客户端请求:服务端
 1 import socket
 2 
 3 client = socket.socket()
 4 
 5 client.connect(('127.0.0.1', 8001))
 6 
 7 while True:
 8     v = input(">>>>")
 9     if v == 'exit':
10         break
11     client.sendall(bytes(v, encoding='utf8'))
12     ret = client.recv(1024)
13     print("server respone:", ret)
14 
15 client.close()
利用select实现伪同时处理多个Socket客户端请求:客户端

 

二、socketserver模块

  SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。

  即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

  

  1.ThreadingTCPServer

  ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

  使用ThreadingTCPServer:

  • 创建一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer
 1 import SocketServer
 2 
 3 class MyServer(SocketServer.BaseRequestHandler):
 4 
 5     def handle(self):
 6         # print self.request,self.client_address,self.server
 7         conn = self.request
 8         conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
 9         Flag = True
10         while Flag:
11             data = conn.recv(1024)
12             if data == 'exit':
13                 Flag = False
14             elif data == '0':
15                 conn.sendall('通过可能会被录音.balabala一大推')
16             else:
17                 conn.sendall('请重新输入.')
18 
19 
20 if __name__ == '__main__':
21     server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
22     server.serve_forever()
secketserver服务端
 1 import socket
 2 
 3 
 4 ip_port = ('127.0.0.1',8009)
 5 sk = socket.socket()
 6 sk.connect(ip_port)
 7 sk.settimeout(5)
 8 
 9 while True:
10     data = sk.recv(1024)
11     print 'receive:',data
12     inp = raw_input('please input:')
13     sk.sendall(inp)
14     if inp == 'exit':
15         break
16 
17 sk.close()
socketserver客户端

  2.源码剖析

  

内部调用流程为:

  • 启动服务端程序
  • 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
  • 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
  • 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
  • 当客户端连接到达服务器
  • 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
  • 执行 ThreadingMixIn.process_request_thread 方法
  • 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)

 

 1 import socket
 2 import threading
 3 import select
 4 
 5 
 6 def process(request, client_address):
 7     print request,client_address
 8     conn = request
 9     conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
10     flag = True
11     while flag:
12         data = conn.recv(1024)
13         if data == 'exit':
14             flag = False
15         elif data == '0':
16             conn.sendall('通过可能会被录音.balabala一大推')
17         else:
18             conn.sendall('请重新输入.')
19 
20 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21 sk.bind(('127.0.0.1',8002))
22 sk.listen(5)
23 
24 while True:
25     r, w, e = select.select([sk,],[],[],1)
26     print 'looping'
27     if sk in r:
28         print 'get request'
29         request, client_address = sk.accept()
30         t = threading.Thread(target=process, args=(request, client_address))
31         t.daemon = False
32         t.start()
33 
34 sk.close()
精简版sockserver

如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。

 

posted @ 2017-03-22 10:25  willianflasky  阅读(160)  评论(0编辑  收藏  举报