io模型,select

IO模型

模型就是解决某个问题的套路

IO问题:

输入输出

我要一个用户名用来执行登陆操作,问题用户名需要用户输入,输入需要耗时, 如果输入没有完成,后续逻辑无法继续,所以默认的处理方式就是 等

将当前进程阻塞住,切换至其他进程执行,等到按下回车键,拿到了一个用户名,再唤醒刚才的进程,将状态调整为就绪态

以上处理方案 就称之为阻塞IO模型

存在的问题:

当执行到recv时,如果对象并没有发送数据,程序阻塞了,无法执行其他任务

解决方案:

多线程或多进程,

      当客户端并发量非常大的时候,服务器可能就无法开启新的线程或进程,如果不对数量加以限制 服务器就崩溃了

线程池或进程池

      首先限制了数量 保证服务器正常运行,但是问题是,如果客户端都处于阻塞状态,这些线程也阻塞了

协程:

      使用一个线程处理所有客户端,当一个客户端处于阻塞状态时可以切换至其他客户端任务

使用非阻塞:

      阻塞IO模型在执行recv 和 accept 时 都需要经历wait_data

     非阻塞IO即 在执行recv 和accept时 不会阻塞 可以继续往下执行

如何使用:

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

存在的问题 :

这样一来 你的进程 效率 非常高 没有任何的阻塞

很多情况下 并没有数据需要处理,但是我们的进程也需要不停的询问操作系统 会导致CPU占用过高

而且是无意义的占用

案例:

import socket
import time
server = socket.socket()
server.bind(("127.0.0.1",1688))

server.listen()

server.setblocking(False)

#客户端储存数据的list
clients = []
#发送数据的list
send_data = []
while True:
    try:
        client, addr = server.accept()
        clients.append(client)
    except BlockingIOError as e :
        time.sleep(0.2)
        #获取到每一个客户端的对象 c 就是每一个客户端的对象
        for c in clients[:]:
            try:
                # 通过客户端的对象,收发数据,获取到客户端发过来的数据
                data = c.recv(1024)
                # 判断客户端发过来的数据是否为空 (收空操作)
                if not data:
                    c.close()
                    clients.remove(c)
                    # continue
                # 这里是当客户端发送数据到缓存的时候,刚好到缓存的最大值, 
                # 那么数据就有可能丢失 ,所以就需要try和数据进行处理
                # c.send(data.upper())
                send_data.append((c, data))
            except BlockingIOError as e:
                print("这个客户端还不需要处理")
            except ConnectionResetError as e:
                print(e)
                c.close()
                clients.remove(c)
        #发送数据到客户端
        for i in send_data[:]:
            try:
                c, msg = i
                # 发送数据
                c.send(msg.upper())
                send_data.remove(i)
            except BlockingIOError:
                pass

多路复用IO模型

案例:

# 因为使用使用socket中的非阻塞,太占用CPU ,需要通过不断的询问,这样就是一个死循环
# 所以就使用select这个类来弥补这个问题
import socket
import select
server = socket.socket()
server.bind(("127.0.0.1", 1688))
server.listen()

'''
 在select中需要注意的几个个参数是
  rlist 可读的列表
  wlist, 可写的列表
  xlist, 错误信息的列表 
  timeout 这个是询问时间,如果这个时间超过多少就会停止询问
'''
rlist = [server,]
wlist = []
msgs = []
while True:
    #返回的是可读和可写的列表,这里会阻塞到一个或者多个socket可以被处理
    read_rlist,write_wlist,_ = select.select(rlist,wlist,[])
    for soc in read_rlist:
        if soc == server:
            #这里需要注意的是需要判断这个可读的列表中存放的是socket还是客户端的对象
            client,addr = soc.accept()
            #对客户端进行处理
            rlist.append(client)
        else:
            #对客户端的数据进行处理
            try:
                data = soc.recv(1024)
                if not data:
                    soc.close()
                    rlist.remove(soc)
                    continue
                if soc not in wlist:
                    wlist.append(soc)
                msgs.append((soc,data))
            except ConnectionResetError as e:
                soc.close()
                rlist.remove(soc)
                #如果wlist中还有请求对象就删除这个对象
                if soc in wlist:
                    wlist.remove(soc)
    #发送数据到客服端
    for soc in write_wlist:
        for i in msgs[:]:
            if i[0] == soc:
                soc.send(i[1])
                msgs.remove(i)
        wlist.remove(soc)

 

 

 

 

posted on 2019-06-10 21:58  WBaiC  阅读(109)  评论(0编辑  收藏  举报