python之IO并发-阻塞IO 非阻塞IO IO多路复用 异步IO(协程)

阻塞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

select适用于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模块

  1. 普通函数def之前加async关键字 async def get_request()...
  2. 函数调用生成协程对象c=get_request()
  3. 创建任务对象 task=asyncio.ensure_future(c),多任务则需创建tasks列表,把task append到tasks里。loop.run_until_complete(asyncio.wait(tasks))
  4. 创建事件循环对象loop=asyncio.get_event_loop()
  5. 将任务对象装载在事件循环对象中启动事件循环对象loop.run_until_complete(task)
  6. 如果函数有返回值,则给任务对象绑定回调函数task.add_done_callback(task_callback)
  7. 回调函数必须有一个参数,data=参数.result(),data则为函数的返回值
  8. 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])

  1. 导入monkey模块 from gevent import monkey
  2. 运行相应脚本 monkey.patch_socket()(很多脚本,需要自己选择自己需要的,或者patch_all())
  3. 脚本运行要在相应的模块导入之前
"""
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) # 协程方案

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值