IO多路复用

IO多路复用用在单线程里,实现并发效果。但其实属于同步IO范畴,是伪并发

常用于网络通信,和socket搭配使用
第一种:

import socket
import select
import time
sk=socket.socket()
sk.bind(("127.0.0.1",9904))
sk.listen(5)
while True:
    r,w,e=select.select([sk,],[],[],5)  #([监听的输入],[监听的输入],[监听的reeor],[轮循监视的时间间隔],)

    print("轮循")
    for i in r:
        conn,add=i.accept()
        print(conn)
        print("hello")
    print('干其他事')
    time.sleep(5)

实现了只能允许一个客户端连接,循环聊天

'''
上面的代码中
 ???在有一个客户端连接的时候,为什么如果不调用accept,会反复print?

很多答案说是什么select是水平触发方式,不太容易理解

首先一个客户端连接之后,内核态里有了一个sk的双向链接,如果不执行它的accept方法,双向链接一直存在于内核态,未被复制及删除,所以select每次轮循都会执行for循环,自然一直print,传输数据同理
 '''

第二种方法:

import socket
import select
import time
sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setblocking(False)
sk.bind(("127.0.0.1",8801))
sk.listen(5)
inputs=[sk,]
while True:
    r,w,e=select.select(inputs,[],[],5)
    for obj in r:
        if obj==sk:
            conn,add=obj.accept()
            inputs.append(conn)
        else:
            data_byte=obj.recv(1024)
            print(str(data_byte,'utf8'))
            inp=input('回答%s号客户>>>'%inputs.index(obj))
            obj.sendall(bytes(inp,'utf8'))
            
    print("这里可以做其他事情")

能允许back_lock个客户端连接,也能和多个客户端循环聊天,只是一个客户端请求到达处理完毕后才能处理下一个,是一种同步模式(这里用input来模拟同步效果)

以上都用到select,除此之外,poll和epoll也可以,性能更优

第三种方法:

import selectors
import socket
sel = selectors.DefaultSelector()       #根据操作系统选择最优IO多路复用方式
def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)
def read(conn, mask):
    try:
        data = conn.recv(1024)  # Should be ready
        conn.send(data)  # Hope it won't block
    except Exception:
        sel.unregister(conn)
        conn.close()
sock = socket.socket()
sock.bind(('localhost', 8090))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)  #注册,监听对象和监听对象有变化后返回值要执行的函数
while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data       #key.date是监视对象有变化时返回的函数地址
        callback(key.fileobj, mask)   #key。fileobj是返回监视的有变化的对象

借助封装select的socketserver库来实现和第二中相同的功能

相关资料:
​网络 IO 演变发展过程和模型介绍
你管这破玩意叫 IO 多路复用?
百万 Go TCP 连接的思考

### IO多路复用的概念 IO多路复用是一种同步I/O模型,允许一个线程同时监控多个文件描述符(如套接字)。这种技术使得程序可以在等待多个输入源中的任何一个变为可用时不会被阻塞。当任意一个文件描述符准备好了读取或写入操作,则会立即通知应用程序去处理相应的工作[^1]。 ### 实现原理 在Linux系统下,可以通过`select`, `poll` 或者更高效的`epoll` 来实现这一功能。这些函数可以让进程一次性监视多个文件描述符,并告知哪些已经准备好进行通信活动。具体来说: - **Select**: 可以监听一定数量的文件描述符集合,在指定时间内检查它们是否有待处理的数据。 - **Poll**: 类似于`select`但是没有最大文件数目的限制,并且性能更好一些因为不需要每次调用都重新构建文件列表。 - **Epoll**: 是一种更为先进的接口,特别适合大量并发连接的情况。它可以注册感兴趣的事件并只报告那些确实发生了变化的对象,从而减少了不必要的上下文切换和资源消耗[^4]。 对于每一个可能发生变化的状态——比如可读、可写或是异常情况发生——都可以设置回调函数以便及时响应。这种方式不仅提高了效率而且简化了编程逻辑。 ### 应用场景 在网络服务端开发领域广泛应用着IO多路复用的技术,尤其是在高负载情况下需要保持大量的活跃TCP连接时表现尤为突出。例如Web服务器(Nginx), 缓存数据库(Redis) 都采用了类似的架构来优化其性能[^5]。 #### Nginx 和 Redis 的例子 这两个软件均采用Reactor模式下的IO多路复用来支持大规模并发请求: - 对于Nginx而言, 主要负责接收新到来的HTTP请求(`accept`)而具体的业务逻辑则由工作进程中完成. - 而像Redis这样的键值存储系统则是完全依赖单个工作循环就能高效地应对成千上万的同时在线客户端. ```python import select import socket server_socket = socket.socket() server_socket.bind(('localhost', 8080)) server_socket.listen(5) inputs = [server_socket] while True: readable, writable, exceptional = select.select(inputs,[],[]) for s in readable: if s is server_socket: client_socket, addr = s.accept() inputs.append(client_socket) else: data = s.recv(1024) if not data: inputs.remove(s) s.close() else: print(f"Received {data.decode()}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值