随笔 - 105  文章 - 0  评论 - 0  阅读 - 40701

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]
select_socket_server
复制代码
复制代码
import socket
client = socket.socket()
client.connect(('localhost', 8080))
while True:
    msg = input('>>:').strip()
    client.send(msg.encode('utf-8'))
    print(client.recv(1024))
client
复制代码

 四、利用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是活跃连接实例
selector_server
复制代码
复制代码
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))
大并发测试
复制代码

 

posted on   Treelight  阅读(345)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示