阻塞IO即为之前正常使用的IO 逻辑简单 非阻塞IO 可以把阻塞IO 设置为非阻塞IO,例如sockfd.setblocking(false)。如果设置成了非阻塞,无客户端连接时就会报BlockingIOError错误,通过try来捕获。通过循环来接受客户端连接
还可以设置超时检测,settimeout---sockfd.settimeout(5)超时报错
while True: print("Waiting from connect...") try: connfd,addr = sockfd.accept() except (BlockingIOError,timeout) as e: sleep(2) f.write("%s : %s\n"%(ctime(),e)) f.flush() else: print("Connect from",addr) data = connfd.recv(1024).decode() print(data)
IO多路复用:select poll epoll
elect适用于window,Linux ,unix,poll:inux unix epoll:unix
select最多监控1024个IO
poll比select监控的IO数量多
epoll比select poll的效率高,监控的IO多,触发方式多
select:把要监测的IO放入rlist,wlist,xlist中,rlist适用于被动接受,wlist适用于主动写入,xlist出现错误出发(一般不用)。select()函数返回值也是3个列表,循环3个列表处理IO就绪事件
from select import select from socket import * s=socket() s.bind(('0.0.0.0',8000)) s.listen(5) rlist=[s] wlist=[] xlist=[] while True: rs,ws,xs=select(rlist,wlist,xlist) for r in rs: if r==s: conf, addr = r.accept() print('监听到',addr,'的链接') rlist.append(conf) else: data=r.recv(1024) print(data) r.send(b'ok') if not data: r.close() rlist.remove(r) for w in ws: pass
poll:from select import poll
用到的函数有:p=poll()(建立poll对象),p.register(IO对象,POLLIN|POLLERR)(注册IO事件),p.unregister(IO对象或文件描述符) (取消注册)“event & POLLIN” 按位与检查是否检测POLLIN事件(读IO事件)。events=p.poll()(阻塞等待监控IO事件发生)events为列表里面套元组[(fileno,event),()] 要自己做fileno和IO对象的映射字典,动态维护字典
from select import * from socket import * s=socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('0.0.0.0',8887)) s.listen(3) p=poll() fdmap={s.fileno():s} p.register(s,POLLIN) while True: events=p.poll() for fileno,event in events: if fileno==s.fileno(): conf,addr=s.accept() p.register(conf,POLLIN|POLLERR) fdmap[conf.fileno()]=conf elif event & POLLIN: data=fdmap[fileno].recv(1024).decode() print(data) if not data: #客户端退出,unregister,字典删除,套接字关闭 p.unregister(fileno) fdmap[fileno].close() del fdmap[fileno] continue fdmap[fileno].send(b'ok')
epoll使用方法和poll一样,要加E
协程
asyncio模块
普通函数def之前加async关键字 async def get_request()...
函数调用生成协程对象c=get_request()
创建任务对象 task=asyncio.ensure_future(c),多任务则需创建tasks列表,把task append到tasks里。loop.run_until_complete(asyncio.wait(tasks))
创建事件循环对象loop=asyncio.get_event_loop()
将任务对象装载在事件循环对象中启动事件循环对象loop.run_until_complete(task)
如果函数有返回值,则给任务对象绑定回调函数task.add_done_callback(task_callback)
回调函数必须有一个参数,data=参数.result(),data则为函数的返回值
await关键字:挂起发⽣阻塞操作的任务对象。 在任务对象表示的操作中,凡是阻塞操作的前⾯ 都必须加上await关键字进⾏修饰
如果用协程去获取网络请求就需要用到:aiohttp模块,aiohttp支持异步模块
在每⼀个with前加上async关键字
在阻塞操作前加上await关键字
async def get_request(url): #requests是不⽀持异步的模块 # response = await requests.get(url=url) # page_text = response.text #创建请求对象(sess) async with aiohttp.ClientSession() as sess: #基于请求对象发起请求 #此处的get是发起get请求,常⽤参数:url,headers,params,proxy #post⽅法发起post请求,常⽤参数:url,headers,data,proxy #发现处理代理的参数和requests不⼀样(注意),此处处理代理使⽤proxy='http://ip:port' 多任务异步爬⾍的完整代码实现: async with await sess.get(url=url) as response: page_text = await response.text() #text():获取字符串形式的响应数据 #read():获取⼆进制形式的响应数据 return page_text
第三方库模块:gevent模块
用到的函数:f=gevent.spawn(func,argv)(生成协程函数)gevent.joinall(list,[timeout])
导入monkey模块 from gevent import monkey
运行相应脚本 monkey.patch_socket()(很多脚本,需要自己选择自己需要的,或者patch_all())
脚本运行要在相应的模块导入之前
""" gevent server 基于协成的tcp并发 思路 : 1. 每个客户函数端设置为协成 2. 将socket模块下的阻塞变为可以触发协程跳转 """ import gevent from gevent import monkey monkey.patch_all() # 执行脚本,修改socket from socket import * def handle(c): while True: data = c.recv(1024).decode() if not data: break print(data) c.send(b'OK') c.close() # 创建tcp套接字 s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('0.0.0.0',8888)) s.listen(5) # 循环接收来自客户端连接 while True: c,addr = s.accept() print("Connect from",addr) # handle(c) # 处理具体客户端请求 gevent.spawn(handle,c) # 协程方案标签:fileno,协程,阻塞,IO,poll,data,select From: https://www.cnblogs.com/bighan1/p/18594245