IO阻塞模型
一、知识储备
1、内存空间分配:在Linux中,有4G的虚拟内存空间,其中前1G为内核空间,后3G为用户空间,为了安全问题,用户空间不能访问内核空间。
2、如果是一台socket服务器,需要等待客户端发送数据过来,这个过程由于要访问网卡等硬件设备,用户是不能直接访问,需要内核访问。这个就需要产生一个系统调用,然后产生以下步骤:
A、内核等待数据的到来
B、数据从内核空间复制到内核空间。
经过以上步骤,则用户空间可接收客户端数据。
二、IO模式
在Linux中有五种IO模型处理socket:阻塞I/O、非阻塞I/O、I/O多路复用、信号驱动I/O(忽略)、异步I/O
1、阻塞I/O
2、非阻塞IO
3、I/O多路复用
4、异步I/O
三、一个IO多路复用实现socketserver的案例

import queue import select import socket server = socket.socket() server.bind(('localhost', 8080)) server.listen() # 设置为非阻塞 server.setblocking(False) # 需要被监听的实例都需放到inputs里,包括server。server活跃,表示有新连接,其它连接活跃,则表示有数据到 inputs = [server] outputs = [] msg_dict = {} # 第一个参数为监听列表,第二个参数为pass,第三个参数为异常列表,均用于内核监听 while True: readable, writable, exceptional = select.select(inputs, outputs, inputs) # 用于告诉内核要监听的连接8 print(readable, writable, exceptional) for r in readable: print('\033[1;31min the readable\033[0m:', r) # 表示有连接进来 if r is server: conn, addr = server.accept() print('添加了新连接:', conn) msg_dict[conn] = queue.Queue() # 添加到监听列表 inputs.append(conn) # 表示有数据到了 else: try: recv_data = r.recv(1024) msg_dict[r].put(recv_data) outputs.append(r) except ConnectionResetError: print('客户端已断开') inputs.remove(r) if r in outputs: outputs.remove(r) del msg_dict[r] # 处理发送数据 for w in writable: send_data = msg_dict[w].get() w.send(send_data) outputs.remove(w) # # 处理异常 for e in exceptional: if e in outputs: outputs.remove(e) inputs.remove(e) del msg_dict[e]

import socket client = socket.socket() client.connect(('localhost', 8080)) while True: msg = input('>>:').strip() client.send(msg.encode('utf-8')) print(client.recv(1024))
四、利用selector实现大并发,也是利用IO多路复用模型。

import selectors import socket def accept(sock, mask): # 建立连接 conn, addr = sock.accept() conn.setblocking(False) sel.register(conn, selectors.EVENT_READ, read) def read(conn, mask): # 处理数据 try: data = conn.recv(1024) if data: print('我收到了', data) conn.send(data) except ConnectionResetError: sel.unregister(conn) # 取消注册 conn.close() sel = selectors.DefaultSelector() server = socket.socket() server.bind(('localhost', 8080)) server.listen() server.setblocking(False) # 设为非阻塞模式 sel.register(server, selectors.EVENT_READ, accept) # 在内核中注册一个事件:如果server活跃,则交给accept函数处理。 while True: events = sel.select() # 默认为阻塞,当有活动连接时就不阻塞 for key, mask in events: callback = key.data # 回调accept或read函数 callback(key.fileobj, mask) # key.fileobj是活跃连接实例

import socket socks_list = [socket.socket() for i in range(10000)] messages = [b'I am Treelight', b'I am 37 years old', b'I am a teacher'] server_address = ('10.62.34.22', 8080) for sock in socks_list: sock.connect(server_address) for sock in socks_list: for msg in messages: sock.send(msg) print(sock.recv(1024))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!