python/网络:标准库模块select(IO多路复用实现)

1、多路复用的实现

  https://docs.python.org/zh-cn/3/library/select.html#module-select

  用 select模块,select模块在Windows下只有select方法。

    linux下有select、poll方法和epoll,epoll是select下的实现的一个类,它有自己的一些方法。

   select ---> windows linux unix

     poll ---> linux unix

     epoll ---> linux unix

  【1】select实现IO多路复用

    from select import *

    rs, ws, xs = select(rlist, wlist, xlist[, timeout])

    参数 : rlist ,列表, 存放我们需要等待处理的IO

         wlist, 列表, 存放我们想主动处理的IO

         xlist, 列表, 存放出错希望去处理的IO

         timeout, 超时检测

    功能 : 监控IO事件,阻塞等待监控的IO时间发生 

    返回值: rs ,列表, rlist中准备就绪的IO

        ws, 列表, wlist中准备就绪的IO

        xs, 列表, xlist中准备就绪的IO

    【注意】

        【1】在处理IO时不要形成死循环,让一个客户端单独占有服务端 

        【2】IO多路复兴形成一种可以同时处理多个IO的效果,效率较高 

from socket import * 
from select import select 

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

rlist = [s]
wlist = []
xlist = [s]

while True:
    print("等待IO发生")
    rs,ws,xs = select(rlist,wlist,xlist)
    for r in rs:
        if r is s:
            connfd,addr = r.accept()
            print("Connect from",addr)
            rlist.append(connfd)
        #表示客户端连接套接字准备就绪\'///\\\\\\\\\\\\\\\'
        else:
            data = r.recv(1024)
            if not data:
                #从关注列表移除
                rlist.remove(r)
                r.close()
            else:    
                print("Receive:",data.decode())
                #讲客户端套接字放入wlist
                wlist.append(r)
    for w in ws:
        w.send("这是一条回复消息".encode())
        wlist.remove(w)
    for x in xs:



        if x is s:
            s.close()
            00..
select-server-tcp
from socket import * 

#创建套接字
sockfd = socket()

#发起连接
sockfd.connect(('127.0.0.1',8888))

while True:
    #消息收发
    msg = input("Msg>>")
    if not msg:
        break
    sockfd.sendall(msg.encode())
    data = sockfd.recv(1024)
    print(data.decode())

sockfd.close()
tcp-client

  【2】poll方法实现IO多路复用

    【1】 创建poll对象

      p = select.poll()

    【2】 注册关注的IO

      p.register(s,POLLIN | POLLERR)

      p.unregister(s)   # 取消IO关注

      【事件类别】

          POLLIN       POLLOUT        POLLERR    POLLHUP    POLLPRI

          rlist        wlist      xlist     断开   紧急处理

    【3】 监控IO

      events = p.poll()

      功能: 监控关注的IO事件

      返回值: 返回发生的IO事件, events 是一个列表 [(fileno,evnet),(),()....] ,每个就绪IO对应列表中一个元组:(描述符,就绪事件)

      IO地图 : {s.fileno():s}

    【4】处理IO事件

from socket import * 
from select import *

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

#创建poll对象
p = poll()

#创建地图
fdmap = {s.fileno():s}

#添加关注
p.register(s,POLLIN | POLLERR)

while True:
    #进行IO监控
    #[(fileno,evnet),...]
    events = p.poll()
    for fd,event in events:
        if fd == s.fileno():
            #从地图中找到fd对应的对象
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            #注册新的IO 维护地图
            p.register(c,POLLIN)
            fdmap[c.fileno()] = c 
        else:
            data = fdmap[fd].recv(1024)
            if not data:
                p.unregister(fd) #从关注移除
                fdmap[fd].close()
                del fdmap[fd]  #从地图删除
            else:
                print(data.decode())
                fdmap[fd].send('收到了'.encode())
poll-server-tcp
from socket import * 

#创建套接字
sockfd = socket()

#发起连接
sockfd.connect(('127.0.0.1',8888))

while True:
    #消息收发
    msg = input("Msg>>")
    if not msg:
        break
    sockfd.sendall(msg.encode())
    data = sockfd.recv(1024)
    print(data.decode())

sockfd.close()
tcp-client

 

  【3】epoll方法实现IO多路复用

     代码与poll基本一致

    ① 将生产的对象改为 p = epoll()

    ② 将关注事件类别名称改为epoll的关注事件类别

from socket import * 
from select import *

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

#创建epoll对象
p = epoll()

#创建地图
fdmap = {s.fileno():s}

#添加关注
p.register(s,EPOLLIN | EPOLLERR)

while True:
    #进行IO监控
    #[(fileno,evnet),...]
    events = p.poll()
    for fd,event in events:
        if fd == s.fileno():
            #从地图中找到fd对应的对象
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            #注册新的IO 维护地图
            p.register(c,EPOLLIN)
            fdmap[c.fileno()] = c 
        else:
            data = fdmap[fd].recv(1024)
            if not data:
                p.unregister(fd) #从关注移除
                fdmap[fd].close()
                del fdmap[fd]  #从地图删除
            else:
                print(data.decode())
                fdmap[fd].send('收到了'.encode())
epoll-server-tcp
from socket import * 

#创建套接字
sockfd = socket()

#发起连接
sockfd.connect(('127.0.0.1',8888))

while True:
    #消息收发
    msg = input("Msg>>")
    if not msg:
        break
    sockfd.sendall(msg.encode())
    data = sockfd.recv(1024)
    print(data.decode())

sockfd.close()
tcp-client

2、三者区别 

  【1】epoll 效率要高于select 和 poll

  【2】epoll 的触发机制更多--> EPOLLET(边缘触发)

 

posted @ 2021-01-05 11:17  昱成  阅读(184)  评论(0编辑  收藏  举报