网络编程6(IO模型)
IO sock对象本质是文件描述符,什么是文件描述符,是一个非零的整数,内核区接受的数据,在用户拷贝完之后就没有了
IO多路复用比阻塞IO的好处是可以监听多个sock对象,能处理多个连接,IO多路复用全程阻塞,能实现并发现象,一定不是同时聊,没有开多线程多进程
什么是阻塞IO,(什么是非阻塞IO),主进程会一直卡住不能干其他事,知道对应的操作结束,非阻塞IO不会卡,
阻塞IO全程阻塞,非阻塞IO,copydaty时进程阻塞,IO多路复用全程阻塞;这三个是同步IO模型,只有第四个异步IO全程无阻塞,但是实现麻烦,全程由操作系统完成(waitdata copydata)
同步IO只要两个过程(wait data,copy data)有一个是阻塞的就叫同步IO
同步: 阻塞IO 非阻塞IO io多路复用(select epoll poll)
异步: 异步IO
IO模型有几个角色: 进程,操作系统(内核),IO都是进程的Io
http://www.cnblogs.com/yuanchenqi/articles/6755717.html
IO模型
进程如果有数据的交换,进程发系统调用进入内核态,内核态的数据cp到用户空间,分两个过程,wait for data ,copy data
阻塞IO
![](https://images2017.cnblogs.com/blog/959053/201711/959053-20171118210707327-1344481485.png)
非阻塞IO
![](https://images2017.cnblogs.com/blog/959053/201711/959053-20171118210738921-457289394.png)
IO多路复用
IO multiplexing这个词可能有点陌生,但是如果我说select,epoll,大概就都能明白了。有些地方也称这种IO方式为event driven IO。我们都知道,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。它的流程如图:
![](https://images2017.cnblogs.com/blog/959053/201711/959053-20171123070536321-1564174815.png)
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。(多说一句。所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。
结论: select的优势在于可以处理多个连接,不适用于单个连接
总结##################################################################################
IO就是进程有需要数据的时候才会产生IO系统调用,分两个阶段 1:wait for data;2: copy data
阻塞IO全程阻塞(进程在需要IO调用的时候,用户程序发动系统调用,从用户态转到内核态(内核态是操作系统管理,有操作硬件的权限)直到数据从内核缓存区cp到用户区,此时内核的数据直接清除),
非阻塞IO wait for data不阻塞,用户程序会隔一段时间就到内核区问问,数据准备好了没有,一次次的发起系统调用,直到有数据了,阻塞cp数据
IO多路复用:这个最大的改善是用户程序调用select函数发起系统调用阻塞等数据,等数据到了,用户进程发起系统调用cp数据,然后清除内核缓存中的数据,比IO阻塞多了个系统调用,下面的例子多个客户端聊天
就是及于此
1 阻塞IO: 全程阻塞 2 非阻塞IO:
优点:wait for data时无阻塞 缺点:1 系统调用太多 2 数据不是实时接受的 两个阶段:wait for data:非阻塞 copy data :阻塞 3 IO多路复用(监听多个连接)
sock::sock <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8800)> 224就是文件打桩符 对于文件描述符(套接字对象): 1 是一个非零整数,不会变 2 收发数据的时候,对于接收端而言,数据先到内核空间, 然后copy到用户空间,同时,内核空间数据清除。 特点:1 全程(wait for data,copy)阻塞 2 能监听多个文件描述符 实现并发 4 异步IO 全程无阻塞, 5 驱动信号 总结: 同步: 阻塞IO 非阻塞IO io多路复用 异步: 异步IO
#server端 #简单聊天,最大接受5个client排队,第一个连接后就进入while第二个,阻塞socket模块 import socket import time sock=socket.socket() sock.bind(("127.0.0.1",8800)) sock.listen(5) #sock.setblocking(False) # while 1: # try: # conn,addr=sock.accept() # 阻塞等待链接 # except Exception as e: # print(e) # time.sleep(3) # data=conn.recv(1024) # # print(data.decode("utf8")) while 1: conn,addr=sock.accept() print("server working.......") while 1: data=conn.recv(1024) print(data.decode("utf8")) send_data=input(">>>") conn.send(send_data.encode("utf8")) conn.close() #client端 import socket sock=socket.socket() sock.connect(("127.0.0.1",8800)) while 1: data=input("input>>>") sock.send(data.encode("utf8")) rece_data=sock.recv(1024) print(rece_data.decode("utf8")) sock.close()
#给予select机制实现多客户端并发聊天 如果几个客户端先后发消息过来,监控的列表中conn几个会有变化,server端会吧有变化的conn for遍历一遍回消息再 #server端 #sock是客户来连接才有变化,conn是客户发消息才有变化,主要还是解决IO操作 import socket import time import select sock=socket.socket() sock.bind(("127.0.0.1",8800)) sock.listen(5) sock.setblocking(False) inputs=[sock,] print("sock",sock) while 1: r,w,e=select.select(inputs,[],[]) # select只监听有变化的套接字 inputs=[sock,conn1,conn2,conn3..] #开启select首先到这, print("r",r) for obj in r: # 第一次 [sock,] 第二次(客户端发消息) #[conn1,] if obj==sock:#客户端第一次连接走这条路 print('change') conn,addr=obj.accept() inputs.append(conn) # inputs=[sock,conn] else:#客户端第二次发消息走这条路 data=obj.recv(1024) print(data.decode("utf8")) send_data=input(">>>") obj.send(send_data.encode("utf8"))
#client 端
#跟上面的一样