IO模型
select模块
selectors模块
IO模型
网络IO的本质是socket的读取,socket在linux系统中被抽象为流,IO可以理解为对流的操作.对于一次IO访问,数据会先被拷到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间,所以说当一个read操作发生时,它会经理两个阶段:
第一阶段:等待数据准备;第二阶段:将数据从内核拷贝到进程中
对socket流而言:第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。第二步:把数据从内核缓冲区复制到应用进程缓冲区。
IO的模型大致有如下几种:
异步IO(asynchronous IO)
同步IO(synchronous IO)
阻塞IO(bloking IO)
非阻塞IO(non-blocking IO)
多路复用IO(multiplexing IO)
信号驱动式IO(signal-driven IO)
同步阻塞 IO(blocking IO)
同步非阻塞 IO(nonblocking IO)
IO 多路复用( IO multiplexing)
信号驱动式 IO(signal-driven IO)
异步非阻塞 IO(asynchronous IO)
五种模型总结
select模块
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
select(rlist, wlist, xlist, timeout=None)
select 函数监视的文件描述符分3类,分别是readfds、writefds和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。
应用:
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 __author__ = 'BillyLV'
4
5 from socket import *
6 importselect7
8 server = socket(AF_INET, SOCK_STREAM) #ipv4, tcp
9 server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) #端口重用
10 server.bind(('127.0.0.1', 8090))11 server.listen(10)12 server.setblocking(False) #设置socket的套接字为非阻塞
13 read_l = [server, ] #因为不只就那么一个列表要检测,所以不要在参数里面定死了
14 whileTrue:15 r_l, w_l, x_l =select.select(read_l, [], [])16 print(r_l) #检测到有数据
17 for ready_obj inr_l:18 if ready_obj ==server:19 conn, addr = ready_obj.accept() #accept要经历两个阶段,但是程序如果走到这一步,那肯定是数据准备好了
20 #print(addr)
21 read_l.append(conn)22 else:23 try:24 data = ready_obj.recv(1024) #此时的ready_obj等于conn
25 if notdata:26 read_l.remove(ready_obj)27 continue
28 ready_obj.send(data.upper())29 exceptConnectionResetError:30 read_l.remove(ready_obj)
服务端IO多路复用
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 __author__ = 'BillyLV'
4
5 from socket import *
6 client = socket(AF_INET, SOCK_STREAM) #ipv4, tcp
7 client.connect(('127.0.0.1', 8091))8 whileTrue:9 msg = input('>>:')10 if notmsg:11 continue
12 client.send(msg.encode('utf-8'))13 data = client.recv(1024)14 print(data.decode('utf-8'))
客户端IO多路复用
selectors模块
Select\Poll\Epoll这三种IO多路复用模型在不同的平台有着不同的支持,相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。而epoll在windows下就不支持,好在我们有selectors模块,帮我们默认选择当前平台下最合适的。该模块允许基于所选模块原语的高水平和高效的IO多路复用。鼓励用户使用此模块,除非他们希望对所使用的操作系统级原语进行精确控制。
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 __author__ = 'BillyLV'
4
5 importselectors6 from socket import *
7
8 sel = selectors.DefaultSelector() #选择最佳实施
9
10
11 defaccept(server_fileobj, mask):12 conn, addr =server_fileobj.accept()13 print('accepted', conn, 'from', addr)14 conn.setblocking(False)15 sel.register(conn, selectors.EVENT_READ, read)16
17
18 defread(conn, mask):19 data = conn.recv(1024)20 ifdata:21 print('echoing', repr(data), 'to', conn)22 conn.send(data)23 else:24 print('closing', conn)25 sel.unregister(conn)26 conn.close()27
28
29 server_fileobj =socket(AF_INET, SOCK_STREAM)30 server_fileobj.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)31 server_fileobj.bind(('localhost', 2130))32 server_fileobj.listen(200)33 server_fileobj.setblocking(False) #设置socket的接口为非阻塞
34 #相当于往select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept
35 sel.register(server_fileobj, selectors.EVENT_READ, accept)36
37 whileTrue:38 events = sel.select() #检测所有的fileobj,是否有完成wait data的
39 for sel_obj, mask inevents:40 callback = sel_obj.data #callback=accpet
41 callback(sel_obj.fileobj, mask) #accpet(server_fileobj,1)
服务端
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 __author__ = 'BillyLV'
4
5 from socket import *
6 client = socket(AF_INET, SOCK_STREAM) #ipv4, tcp
7 client.connect(('localhost', 2130))8 whileTrue:9 msg = input('>>:')10 if notmsg:11 continue
12 client.send(msg.encode('utf-8'))13 data = client.recv(1024)14 print(data.decode('utf-8'))
客户端
参考:
http://www.cnblogs.com/alex3714
http://www.cnblogs.com/wupeiqi
internet&python books
PS:如侵权,联我删。