day-39网络编程

IO模型

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

  IO问题:

    输入输出

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

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

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

  存在的问题:   

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

  解决方案:

    1多线程或多进程,

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

    2线程池或进程池

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

    3协程:

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

非阻塞IO模型

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

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

  如何使用:

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

  存在的问题 :

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

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

    而且是无意义的占用

# 用来存储客户端的列表
clients = []

# 链接客户端的循环
while True:
    try:
        client,addr = server.accept()   # 接受三次握手信息
        # print("来了一个客户端了.... %s" % addr[1])
        # 有人链接成功了
        clients.append(client)
    except BlockingIOError as e:
        # print("还没有人连过来.....")
        # time.sleep(0.5)
        # 服务你的客人去
        for c in clients[:]:
            try: # 可能这个客户端还没有数据过来
                # 开始通讯任务
                data = c.recv(2048)
                c.send(data.upper())
            except BlockingIOError as e:
                print("这个客户端还不需要处理.....",)

            except ConnectionResetError:
                # 断开后删除这个客户端
                clients.remove(c)
        print("=======================",len(clients))

多路复用IO模型

import socket
import time
import select


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

"""
参数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:
    ras,was,_ = select.select(rlist,wlist,[])  # 阻塞直到socket可读或是可写
    # 处理可读的socket
    for s in ras:
        if s == server:
            client,addr = server.accept()
            rlist.append(client)
        else:
            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 was:
        for msg in msgs[:]:
            if msg[0] == s:
                s.send(msg[1].upper())
                # 发送成功之后 删除已经无用的数据 并且需要将socket从wlist列表中删除
                # 不删除会造成死循环 因为socket 一直处于可写状态
                msgs.remove(msg)
                wlist.remove(s)

 

 

 

原文地址:https://www.cnblogs.com/klw1/p/11000137.html