编程语言
首页 > 编程语言> > Python--day39(IO阻塞模型、非阻塞IO、多路复用)

Python--day39(IO阻塞模型、非阻塞IO、多路复用)

作者:互联网

1. IO阻塞模型

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

 

解决方案:

1.1 多线程或多进程

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

1.2 线程池或进程池

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

1.3 协程

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

 

2. 非阻塞IO模型

阻塞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) # 默认为阻塞  设置为False 表示非阻塞

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

# 链接客户端的循环
while True:
    try:
        client, address = server.accept() # 接受三次握手信息
        print("来了一个客户端了...%s " % address[1])
        # 有人链接成功了
        clients.append(client)
    except BlockingIOError as e:
        print("还没有人连过来")
        time.sleep(0.5)

        # 收数据的操作
        for c in clients[:]:
            try:
                # 开始通讯任务
                data = c.recv(1024)
                if not data:
                    c.close()
                    clients.remove(c)
                msgs.append((c, data))
                # c.send(data.upper())
                # 如果碰巧缓存满了,这个数据就可能丢失了
                # 由于此处捕获了异常,所以应该单独来发送数据
            except BlockingIOError:
                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
View Code

 

 

3. 多路复用

 

 服务器

import socket
import select

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


rlist = [server,]
# 将需要检测(是否可读==recv)的socket对象放到该列表中
# accept也是一个读数据操作,默认也会阻塞,也需要让select来检测
wlist = []
# 将需要检测(是否可写==send)的socket对象放到该列表中
# 只要缓冲区不满都可以写

msgs = [("socket", "msg")]
while True:
    readable_list, writeable_list, _ = select.select(rlist, wlist, []) # 阻塞直到socket可读或者可写
    """
    参数1  rlist   里面存储需要被检测是否可读(是否可以执行recv)的socket对象
    参数2  wlist   里面存储需要被检测是否可写(是否可以执行send)的socket对象
    参数3  xlist   存储需要关注异常条件  (忽略)
    参数4  timeout 检测超时时间,一段时间后还是没有可以被处理的socket对象,那就返回空列表
    
    返回值: 三个列表
    1. 已经有数据到达的socket对象
    2. 可以发送数据的socket对象      怎么可以发?缓冲区,没有满
    3. 忽略
    """
    print("%s个socket可读" % len(readable_list), "%s个socket可写" % writeable_list)

    # readable_list 中存储的是已经可以读取数据的socket对象,可能是服务器,也可能是客户端
    for soc in readable_list:
        if soc == server:
            # 服务器的处理
            client, address = server.accept()
            # 将新连接的socket对象 加入到待检测列表中
            rlist.append(client)
        else:
            try:
                # 客户端处理
                data = soc.recv(1024)
                if not data:
                    # 如果对方下线了,关闭socket,并且从待检测列表中删除
                    soc.close()
                    rlist.remove(soc)
                    continue

                # 不能直接发 因为此时缓冲区可能已经满了,导致send阻塞住,所以在发送数据前一定要先将socket交给select来检查
                # soc.send(data.upper())

                if soc not in wlist:
                    wlist.append(soc)
                # 将要发送的数据先存储起来
                msgs.append((soc,data))
            except ConnectionResetError:
                soc.close()
                # 对方下线后 应该从待检测列表中杀出socket
                rlist.remove(soc)
                wlist.remove(soc)

    # 处理可写列表
    for soc in writeable_list:
        # 由于一个客户端可能有多个数据要发送,所以遍历所有客户端
        for i in msgs[:]:
            if i[0] == soc:
                soc.send(i[1])
                # 发送成功 将这个数据从列表中删除
                msgs.remove(i)
            # 数据已经都发送给客户端 这个socket不必继续监测是否可写,必须要删除
            # 否则 只要缓冲区不满,就会一直处于可写,导致死循环
        wlist.remove(soc)
View Code

 

标签:soc,socket,day39,阻塞,server,IO,data,客户端
来源: https://www.cnblogs.com/wangyong123/p/10999993.html