day39

day39
 
traceback.print.exc()
捕获异常
join的注意点
 
IO模型
    模型就是解决某个问题的套路
IO问题
    输入输出
    需要一个用吗来执行登陆操作,问题是用户名需要用户输入,输入需要耗时,如果输入没有完成,后续的逻辑无法继续,所以默认处理方式就是等待.
怎么等(将当前进程阻塞住,切换至其他进程执行,等到按下回车键,拿到用户名,再唤醒刚才的进程,将状态调整为就绪态),这个处理方案就称之为,阻塞IO模型
 
 
 
 
网络IO中必经的两个阶段
copy犹豫是本地IO,所以速度非常快,不是我们要解决的
 
 
################
存在的问题:
    当执行到recv的时候,如果对方没有发送数据,程序阻塞,无法执行其他任务
解决方案:
    多线程,多进程:
            当客户端并发量非常大的时候,服务器资源就无法开启新的线程或者进程,如果不对数量加以限制,服务器就容易崩了,
    线程池或者进程池
            1.限制了数量,保证服务器正常运行,但是问题是如果客户端都处于阻塞状态,这些线程,就业阻塞了.
    协程:
            使用一个线程处理所有客户端,当一个客户端处于阻塞状态时,就可以切换到其他客户端任务
 
 
非阻塞IO模型
      阻塞IO模型在执行recv和accept的时候,都需要经历一个wait data.
        非阻塞IO模型在执行recv和accept的时候不阻塞,会往下执行
 
 
代码????
非阻塞IO模型(还没全)
服务器
import socket
server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
server.setblocking(False)
clients = []
while True:
try:
client,addr = server.accept()
clients.append(client)
except BlockingIOError as e:
# print(e)
for c in clients[:]:
try:
data = c.recv(2048)
c.send(data.upper())
except BlockingIOError as e:
print("不需要处理")
except ConnectionResetError as e:
clients.remove(c)
# print("==============",len(clients))
 
 
如何使用:
        将server的blocking设置为False,非阻塞
存在的问题
        这样一来,进程效率非常高,没有任何的阻塞
        但是很多情况下,并没有很多数据需要处理,但是我们的进程也需要不停的问操作系统,所以会导致CPU占用过高.并且这个            是 无意义的
多路复用
 
多个socket对象,一个socket就是一个传输通道
复用:就是同一个线程处理所有socket.
原理,在非阻塞IO模型中,我们需要不断的询问操作系统,是否有数据需要处理,造成资源浪费.
        多路复用,使用select,来检测是否与有socket,可以被处理  
 
#########多路复用代码初级
select参数:
参数1:rlist,里面存储需要被检测是否可读的(是否可以执行recv)socket对象
参数2:wlist,里面存储需要被检测是否可写的(是否可以执行send)socket对象
参数3:xlist,就是存储需要关注的异常条件
参数4:timeout超时时间,超过一定时间,还是没有可以被处理的socket,就返回空列表
返回值   三个列表
1,已经有数据到达的socket对象
2.可以发送数据的socket对象.怎么叫可以发,就是缓冲区没满
3不管
 
import socket
import select
server = socket.socket()
server.bind(("127.0.0.1",1888))
server.listen()
rlist = [server,]
wlist =[]
while True:
    readable_list,wirteable_list,_ = select.select(rlist,wlist,[])
    print(readable_list,wirteable_list)
    for soc in readable_list:
        if soc == server:
            client,addr = server.accept()
            rlist.append(client)
 
        else:
            data = soc.recv(2048)
            if not data:
            soc.close()
               rlist.remove(soc)
                continue
            soc.send(data.upper())
 
############多路复用代码终极
 
 
import socket
import select
server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
 
# server.setblocking(False)
 
rlist = [server,]  # 将需要检测(是否可读==recv)的socket对象放到该列表中
                   # accept也是一个读数据操作,默认也会阻塞  也需要让select来检测
 
# 注意 select最多能检测1024个socket 超出直接报错    这是select自身设计的问题    最终的解决方案epoll
 
wlist = []  # 将需要检测(是否可写==send)的socket对象放到该列表中
            # 只要缓冲区不满都可以写
 
msgs = [("socket","msg")]  # 存储需要发送的数据  等待select 检测后 在进行发送
 
 
print("start")
while True:
    readable_list,writeable_list,_ = select.select(rlist,wlist,[])  # 会阻塞等到 有一个或多个socket 可以被处理
    print("%s个socket可读" % len(readable_list),"%s个socket可写" % len(writeable_list))
 
    """
    readable_list 中存储的是已经可以读取数据的socket对象   可能是服务器 可能是客户端
    """
    # 处理可读列表
    for soc in readable_list:
        if soc == server:
            # 服务器的处理
            client,addr = server.accept()
            #将新连接的socket对象 加入到待检测列表中
            rlist.append(client)
        else:
            try:
                # 客户端的处理
                data = soc.recv(2048)
                if not data:
                    soc.close()
                    rlist.remove(soc)   # 如果对方下线  关闭socket 并且从待检测列表中删除
                    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) # 否则 只要缓冲区不满 一直处于可写 导致死循环
 
 
posted @ 2019-06-10 21:59  轩辕12  阅读(108)  评论(0编辑  收藏  举报