Python多路IO复用之select

前言

这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题

于是乎,我自己开发了一款面试宝典,希望能帮到大家,也希望有更多的Python新人真正加入从事到这个行业里,让python火不只是停留在广告上。

微信小程序搜索:Python面试宝典

或可关注原创个人博客:https://lienze.tech

也可关注微信公众号,不定时发送各类有趣猎奇的技术文章:Python编程学习

Select

  • io复用

I/O复用就是单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流

目前支持的模型有Select、Poll与Epoll;


select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点

但是select有一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024


用Select完成非阻塞方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况,会告诉程序哪些套接字是读、写或是异常状态,我们可以遍历这些套接字的序列,再进行一对一处理

  • select模型来自于unix操作系统中,由c而来
int select(int maxfdpl, fd_set * readset, fd_set *writeset, fd_set *exceptset, const struct timeval * tiomeout)
// maxfdpl: 最大的文件描述符长度
// readset: 监听的可读集合
// writeset: 监听的可写集合
// exceptset: 监听的异常集合
// tiomeout: 超时判断
  • 在python中对于select模型应用,使用select模块
import select
select.select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
# rlist: 监听的可读套接字,第一个值往往是服务端套接字
# wlist: 监听的可写套接字
# xlist: 监听的异常套接字
  • 使用select复用模型,需要三个循环
循环响应读事件 -> rlist
循环用来处理消息发送 -> wlist
循环处理异常事件 -> xlist
  • 参考代码
import select
import socket
import queue

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 服务端套接字
s.setblocking(0) # 非阻塞

s.bind(('',8000)) # 绑定可用ip端口
s.listen(5) # 监听套接字

in_ = [s] # 待监听的可读队列 
'''
s的可读行为: 有新的链接
客户端套接字行为: 有数据发来
''' 
out_ = [] # 可写队列,发送消息

msg_queue = {}

while in_:
    print('等待下一次事件...')
    readable, writable, exceptional = select.select(in_, out_, in_)
    for socket_ in readable: # 监听可读队列
        if socket_ is s: # 当前套接字为服务端套接字,相应的可读事件为有用户链接来
            c,c_addr = s.accept()
            print('有新的套接字连接')
            c.setblocking(0) # 新连接的客户端套接字设置为非阻塞
            in_.append(c) # 加入可读监听队列中
            msg_queue[c] = queue.Queue() # 为当前客户端放置消息存放队列
        else: # 非服务端套接字,客户端套接字可读事件为有消息发来
            data = socket_.recv(1024) # 接收客户端数据
            if data == '': # 客户端断开连接
                print('套接字关闭...[%s]' % socket_)
                if socket_ in out_:
                    out_.remove(socket_)
                in_.remove(socket_) # 在两个监听队列里删除
                socket_.close()
            else: # 有客户端消息发来
                msg_queue[socket_].put(data) # 放置数据到该客户端的消息队列中
                if socket_ not in out_: # 放置监听套接字到可写事件队列中
                    out_.append(socket_)
                    
    for socket_ in writable:
        try:
            q = msg_queue.get(socket_) # 获取当前是否含有消息队列,待群发消息
            if q: # 如果存在
                if q.empty(): # 无消息 踢出队列
                    out_.remove(socket_)
                else:
                    send_data = q.get_nowait() # 取出消息
        except:
            out_.remove(socket_)
        else:
            for s_ in in_:
                if s_ is not s: # 不是服务端套接字
                    try:
                        s_.send(send_data)
                    except:
                        in_.remove(s_)
                        if s_ in out_:
                            out_.remove(s_)
                        s_.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李恩泽的技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值