python_day39 IO模型 多路复用IO模型

阻塞网络IO模型

什么是IO:就是输入输出

阻塞:遇到IO时停止等待输入或输出  才能进行后续逻辑

默认的IO模型都是阻塞IO模型

非阻塞网络IO模型

字面意思就是:在遇到IO时可以继续执行  不受影响 

首先了解什么网络IO模型在什么阶段会阻塞

发送端:---发送数据(send)--会将数据从进程copy到缓存区 ,这时会阻塞(本地阻塞) 

接收端:--等待接收数据(wait_data),这时会产生堵塞,发送端无数据发送过来就会阻塞---

接收到数据(sent_data),从缓存区copy到内存中去执行时 也会阻塞(本地阻塞)

因为本地阻塞 非常快  所以不必纠结  主要解决是的wait_data的问题

 

如何使用:

1 将server的blocking设置为False   即设置非阻塞 

2 将客户端的  接收server.accept()    client.recv()    client.sent  分开处理  因为是这三个部分产生阻塞

服务器端

import socket
import time

server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
server.setblocking(False) # 默认为阻塞,True    设置为False 表示非阻塞

# 用来存储客户端的列表
clients = []
# 用来存储需要发送的数据和客户端对象
msgs = []

# 链接客户端的循环
while True:
    try:
        client,addr = server.accept()   # 接受三次握手信息  这时是wait_data 会阻塞
        # print("来了一个客户端了.... %s" % addr[1])
        # 有人链接成功了
        clients.append(client)  # 添加到存储客户端列表中去  然后再去处理其他任务  就完成了非阻塞
        # (因为这时有可能客户端只是连接过来,数据还没发过来,那么就可以去处理其他任务)
    except BlockingIOError as e:
        # print("还没有人连过来.....")
        time.sleep(0.01)


        # 收数据的操作
        for c in clients[:]:  # 从客户端出来接收数据  对for循环操作  要切片  防止出错
            try: # 可能这个客户端还没有数据过来
                # 开始通讯任务
                data = c.recv(2048)
                if not data:
                    c.close()
                    clients.remove(c)
                #c.send(data.upper())  # 如果碰巧缓存区满了  这个数据就丢失了
                # 由于此处捕获了异常  所以应该单独来处理发送数据
                msgs.append((c,data))
            except BlockingIOError as e:
                print("这个客户端还不需要处理.....",)
            except ConnectionResetError:
                # 断开后删除这个客户端  防止缓存区占满
                c.close()
                clients.remove(c)


        # 发送数据的操作
        for i in msgs[:]:
            try:
                c,msg = i
                c.send(msg.upper())
                msgs.remove(i) # 如果发送成功! 删除这个数据  
            except BlockingIOError:
                pass

 

 

客户端

import socket

client =socket.socket()
client.connect(('127.0.0.1',1688))

while True:
        msg = input("msg:")
        if not  msg:continue
        client.send(msg.encode('utf-8'))
        data=client.recv(2048).decode('utf-8')
        print(data)

 

 

非阻塞网络IO模型 

带来的问题:但没有数据过来时,cpu也会一直在运行去询问是否有数据要处理  CPU这时就是处于一种无实际用途的占用



  

 

多路复用IO模型

为了解决上述问题的,那么就可以把这个工作交给别人去做(select) ,只需要去管理select就好.那么select解决的问题就是,记录接收的数据 和需要发送的数据 (存在rlist列表和wlist列表中),只要这些数据是非空有只时,那么就将这些数据交给CPU去处理,CPU只需要对其列表遍历找出有数据的就可以,当数据完成后就把多个列表中的数据删除掉

select监控多个socket

select的实现思路比较直接

1.先将所有socket放到一个列表中,

 

2.遍历这个列表将进程A 添加到每个socket的等待队列中   然后阻塞进程

 

3.当数据到达时,cpu执行中断程序将数据copy给socket 同时唤醒处于等待队列中的进程A

 

为了防止重复添加等待队列 还需要移除已经存在的进程A

 

4.进程A唤醒后 由于不清楚那个socket有数据,所以需要遍历一遍所有socket列表

从上面的过程中不难看出

1.select,需要遍历socket列表,频繁的对等待队列进行添加移除操作,

2.数据到达后还需要给遍历所有socket才能获知哪些socket有数据

两个操作消耗的时间随着要监控的socket的数量增加而大大增加,

处于效率考虑才规定了最大只能监视1024个socket

服务器
import socket
import select


server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()

"""
参数1   rlist   里面存储需要被检测是否可读(是否可以执行recv)的socket对象 
参数2   wlist   里面存储需要被检测是否可写(是否可以执行send)的socket对象 
参数3   xlist 存储你需要关注异常条件  忽略即可
参数4   timeout  检测超时时间  一段时间后还是没有可以被处理的socket 那就返回空列表  
返回值:  三个列表
1    已经有数据到达的socket对象 
2    可以发送数据的socket对象    怎么可以发 缓冲区没有满  
3    忽略....
"""
rlist = [server,]
wlist = []

# 要发送的数据和socket
msgs = []
while True:
    readable_list,writeable_list,_ = select.select(rlist,wlist,[])  # 阻塞直到socket可读或是可写
    # 处理可读的socket
    for s in readable_list: # 其中可能是server的socket 可能是client的socket        
        if s == server:
            client,addr = server.accept()
            rlist.append(client)
        else: # 不是server的那肯定就是client的socket对象 那就可以来接收数据
            try:
                # 收数据
                data = s.recv(2048)
                if not data:  # 无数据会收空报错  加一个抛异常
                    raise ConnectionResetError()
                wlist.append(s)
                # s.send(data.upper())
                # 将要发送的数据和socket 保存起来
                msgs.append((s,data))
            except ConnectionResetError:
                s.close()
                rlist.remove(s)
                if s in wlist:wlist.remove(s)

    # 处理可写的socket
    for s in writeable_list:
        for msg in msgs[:]:
            if msg[0] == s:
                s.send(msg[1].upper())
                # 发送成功之后 删除已经无用的数据 并且需要将socket从wlist列表中删除
                # 不删除会造成死循环 因为socket 一直处于可写状态
                msgs.remove(msg)
                wlist.remove(s)

 

 

客户端
import socket
client = socket.socket()
client.connect(("127.0.0.1",1688))

while True:
    msg = input("msg:")
    if not msg:continue
    client.send(msg.encode('utf-8'))
    data=client.recv(2048)
    print(data.decode('utf-8'))

 

 

案例

 

转载于:https://www.cnblogs.com/wakee/p/11006145.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值