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 2019-06-04 10:43  Treelight  阅读(339)  评论(0编辑  收藏  举报