IO多路复用

IO多路复用

测试代码

import threading
import logging
import selectors
import socket
import time

logging.basicConfig(format='%(asctime)s %(threadName)s %(message)s',level=logging.INFO)

# 构建本系统最优Selector
selector = selectors.DefaultSelector()

server = socket.socket()  # TCP Server
server.bind(('127.0.0.1',9999))
server.listen()
server.setblocking(False) # 注意:建议非阻塞

# 回调函数,server的读事件
def accept(fileobj,mask):
    """mask:事件的掩码,参考子网掩码的使用"""
    conn,raddr = fileobj.accept()
    conn.send('connected'.encode()) # 注意:建议非阻塞

    conn.setblocking(False) # 注意:建议非阻塞
    key = selector.register(conn,selectors.EVENT_READ,data=recv)
    logging.info(key)

# 回调函数,
def recv(fileobj,mask):
    #  使用死循环抛BlockingIOError,原因是?
    msg = fileobj.recv(1024)
    logging.info(msg)
    fileobj.send('< {} , {}>'.format(msg,mask).encode())

# 注册server为被关注事件,返回SelectorKey对象(namedtuple)
key = selector.register(server,selectors.EVENT_READ,data=accept)
# SelectorKey对象记录了fileobj,fd,events和data
print(key)

while True:
    # 循环监听注册对象的事件,一个或者多个注册对象事件发生,则返回events(列表)
    events = selector.select() #

    # 至少一个注册对象事件发生,进入循环,events -> [(SelectorKey,mask)]
    for key,mask in events:
        key.data(key.fileobj,mask)

群聊实现

import threading
import logging
import selectors
import socket

logging.basicConfig(format='%(asctime)s %(threadName)s %(message)s',level=logging.INFO)


class ChatServer:
    def __init__(self,ip='127.0.0.1',port=9999):
        self._laddr = (ip,port)
        self._server = socket.socket()
        self.selector = selectors.DefaultSelector()
        self.event = threading.Event()

    def start(self):
        self._server.bind(self._laddr)
        self._server.listen()
        self._server.setblocking(False) # 建议非阻塞

        self.selector = selectors.DefaultSelector() # 选择最优Selector
        self.selector.register(self._server,selectors.EVENT_READ,data=self.accept)

        # # 主线程交互,工作线程监听,主线程退出,监听也没有必要存在了
        threading.Thread(target=self.select,name='select',daemon=True).start()

    def select(self):
        while not self.event.is_set():
            events = self.selector.select()
            for key,mask in events:
                if key.fileobj is self._server: # self.accept
                    key.data()
                else: # self.recv
                    key.data(key.fileobj)

    def accept(self):
        conn,raddr = self._server.accept()
        conn.send('connected'.encode())
        conn.setblocking(False) # 注意,建议非阻塞

        self.selector.register(conn,selectors.EVENT_READ,data=self.recv)

    def recv(self,conn):
        msg = conn.recv(1024)

        if msg == b'' or msg == b'quit':
            self.selector.unregister(conn)
            conn.close()

        for _,key in self.selector.get_map().items(): # self.selecotor中有字典存放了注册对象,key为fd,value为SelecotorKey对象
            # if key.fileobj is not self._server:
            #     key.fileobj.send(msg)
            print(id(self.accept)) # 注册的self.accept
            print(id(self.recv)) # 注册时注入的绑定对象
            print(id(key.data))
            print(self.recv is key.data) # 地址不相等
            print(self.recv == key.data) # 内容相等,此处存疑,通过描述器实现
            if key.data == self.recv:
                key.fileobj.send(msg)

    def stop(self):
        self.event.set()
        fileobjs = []

        for _,key in self.selector.get_map().items(): # self.selector.unregister()为字典pop,不能在遍历字典时使用
            fileobjs.append(key.fileobj)

        for fileobj in fileobjs:
            self.selector.unregister(fileobj)
            fileobj.close()
        self._server.close()


if __name__ == "__main__":
    server = ChatServer()
    server.start()

    while True:
        cmd = input('>>>>>')
        if cmd == 'quit':
            server.stop()
            break

        print(threading.enumerate())

九九乘法表

print("\n".join('\t'.join(("{}*{}={}".format(j,i,j*i) for j in range(1,i+1))) for i in range(1,10)))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值