前言

问题:普通套接字实现的服务端的缺陷

一次只能服务一个客户端!

                       

accept阻塞!

在没有新的套接字来之前,不能处理已经建立连接的套接字的请求

recv 阻塞!

在没有接受到客户端请求数据之前,不能与其他客户端建立连接

可以用非阻塞接口来尝试解决这个问题

IO阻塞与非阻塞

阻塞IO模型

  阻塞IO(blocking IO)的特点:就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

什么是阻塞呢?想象这种情形,比如你等快递,但快递一直没来,你会怎么做?有两种方式:

  • 快递没来,我可以先去睡觉,然后快递来了给我打电话叫我去取就行了。
  • 快递没来,我就不停的给快递打电话说:擦,怎么还没来,给老子快点,直到快递来。

很显然,你无法忍受第二种方式,不仅耽搁自己的时间,也会让快递很想打你。
而在计算机世界,这两种情形就对应阻塞和非阻塞忙轮询。

  • 非阻塞忙轮询:数据没来,进程就不停的去检测数据,直到数据来。
  • 阻塞:数据没来,啥都不做,直到数据来了,才进行下一步的处理。

非阻塞IO模型

非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有

非阻塞如何利用

  • 吃满 CPU !
  • 宁可用 while True ,也不要阻塞发呆!
  • 只要资源没到,就先做别的事!

服务器端

import socket

CONN_ADDR = ('127.0.0.1', 9999)
conn_list = [] # 连接列表
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 开启socket
sock.setblocking(False) # 设置为非阻塞
sock.bind(CONN_ADDR) # 绑定IP和端口到套接字
sock.listen(5) # 监听,5表示客户端最大连接数
print('start listen')
while True:
try:
conn, addr = sock.accept() # 被动接受TCP客户的连接,等待连接的到来,收不到时会报异常
print('connect by ', addr)
conn_list.append(conn)
conn.setblocking(False) # 设置非阻塞
except BlockingIOError as e:
pass tmp_list = [conn for conn in conn_list]
for conn in tmp_list:
try:
data = conn.recv(1024) # 接收数据1024字节
if data:
print('收到的数据是{}'.format(data.decode()))
conn.send(data)
else:
print('close conn',conn)
conn.close()
conn_list.remove(conn)
print('还有客户端=>',len(conn_list))
except IOError:
pass

客户端

import socket

client = socket.socket()
client.connect(('127.0.0.1', 9999)) while True:
msg = input(">>>")
if msg != 'q':
client.send(msg.encode())
data = client.recv(1024)
print('收到的数据{}'.format(data.decode()))
else:
client.close()
print('close client socket')
break

输出结果

非阻塞IO模型优点:实现了同时服务多个客户端,能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在“”同时“”执行)。

 但是非阻塞IO模型绝不被推荐

非阻塞IO模型缺点:不停地轮询recv,占用较多的CPU资源。

                                 对应BlockingIOError的异常处理也是无效的CPU花费 !

如何解决:多路复用IO

多路复用IO

把socket交给操作系统去监控,相当于找个代理人(select), 去收快递。快递到了,就通知用户,用户自己去取。

阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作,所以才叫做多路复用

使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,感觉效率更差。

但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,

即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

epoll是目前Linux上效率最高的IO多路复用技术。

epoll是惰性的事件回调,惰性事件回调是由用户进程自己调用的,操作系统只起到通知的作用。

epoll实现并发服务器,处理多个客户端

import socket
import selectors # 注册一个epllo事件
# 1. socket
# 2.事件可读
# 3.回调函数 把一个函数当成变量传到函数里 def recv_data(conn):
data = conn.recv(1024) if data:
print('接收的数据是:%s' % data.decode())
conn.send(data)
else:
e_poll.unregister(conn)
conn.close() def acc_conn(p_server):
conn, addr = p_server.accept()
print('Connected by', addr)
# 也有注册一个epoll
e_poll.register(conn,selectors.EVENT_READ,recv_data) CONN_ADDR = ('127.0.0.1', 9999)
server = socket.socket()
server.bind(CONN_ADDR)
server.listen(6) # 表示一个客户端最大的连接数 # 生成一个epllo选择器实例 I/O多路复用,监控多个socket连接
e_poll = selectors.EpollSelector() # window没有epoll使用selectors.DefaultSelector()实现多路复用
e_poll.register(server, selectors.EVENT_READ, acc_conn) # 事件循环
while True:
# 事件循环不断地调用select获取被激活的socket
events = e_poll.select()
#print(events)
"""[(SelectorKey(fileobj= < socket.socket
laddr = ('127.0.0.1',9999) >,……data = < function acc_conn at 0xb71b96ec >), 1)]
"""
for key, mask in events:
call_back = key.data
#print(key.data)
call_back(key.fileobj)

输出结果

多路复用模型,使用select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,

            

Python网络编程-IO阻塞与非阻塞及多路复用的更多相关文章

  1. python 网络编程 IO多路复用之epoll

    python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...

  2. python网络编程——IO多路复用之select

    1 IO多路复用的概念 原生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收发数据(调用recv.send.sendall)时也是阻塞的.原生so ...

  3. JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信

    阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...

  4. python网络编程——IO多路复用之epoll

    1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/42643971博文并整理 首先我们来定义流的概念,一个流 ...

  5. python网络编程——IO多路复用select/poll/epoll的使用

    转载博客: http://www.haiyun.me/archives/1056.html http://www.cnblogs.com/coser/archive/2012/01/06/231521 ...

  6. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

  7. IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT(re

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念: 阻塞IO:: socket 的阻塞模式 ...

  8. python并发编程(并发与并行,同步和异步,阻塞与非阻塞)

    最近在学python的网络编程,学了socket通信,并利用socket实现了一个具有用户验证功能,可以上传下载文件.可以实现命令行功能,创建和删除文件夹,可以实现的断点续传等功能的FTP服务器.但在 ...

  9. 网络IO之阻塞、非阻塞、同步、异步总结

    网络IO之阻塞.非阻塞.同步.异步总结 1.前言 在网络编程中,阻塞.非阻塞.同步.异步经常被提到.unix网络编程第一卷第六章专门讨论五种不同的IO模型,Stevens讲的非常详细,我记得去年看第一 ...

随机推荐

  1. Python练习-生成器、迭代器-2018.12.01

    如果列表元素可以按照某种算法推算出来,可以在循环的过程中不断推算出后续的元素.这样就不必创建完整的list,从而节省大量的空间.在Python中,这种一边循环一边计算的机制,称为生成器:generat ...

  2. Springboot学习06-Spring AOP封装接口自定义校验

    Springboot学习06-Spring AOP封装接口自定义校验 关键字 BindingResult.Spring AOP.自定义注解.自定义异常处理.ConstraintValidator 前言 ...

  3. 129. Sum Root to Leaf Numbers pathsum路径求和

    [抄题]: Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a ...

  4. [leetcode]53. Maximum Subarray最大子数组和

    Given an integer array nums, find the contiguous subarray (containing at least one number) which has ...

  5. [leetcode]23. Merge k Sorted Lists归并k个有序链表

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. I ...

  6. maven小结

    1.首先要安装maven,我的在: F:\apache-maven-3.0.4 2.手动创建本地仓库的地址为:F:\.m2\repository 3.从windows的环境变量中增加了:M2_HOME ...

  7. Windows7 VS2015 下编译 PythonQt3.2

    本文在使用vs2015编译python3.6.7源代码后,编译的PythonQt3.2.如果使用python二进制文件进行安装,注意python的路径即可 本机环境: 1.win7 64 旗舰版 2. ...

  8. MongoDB、Hbase、Redis等NoSQL分析

    NoSQL的四大种类 NoSQL数据库在整个数据库领域的江湖地位已经不言而喻.在大数据时代,虽然RDBMS很优秀,但是面对快速增长的数据规模和日渐复杂的数据模型,RDBMS渐渐力不从心,无法应对很多数 ...

  9. zeromq学习记录(七)订阅发布消息封装

    之前也有提到 使用订阅发布 pub sub模式必须要显示定义ZMQ_SUBSCRIBE 只有以此模式定义的过滤字节开头的消息才会被订阅者收到 如果想收到所有信息 可定义subscriber.setso ...

  10. python 的面相对象编程--对应c++

    在python的面相对象编程中,我们常常在class中可以看到a(),  _b() ,  __c(), __d()__这样的函数. 由于我是看廖雪峰老师的教程,廖老师为了简单起见,没有引入太多概念,我 ...