IO模型

所需知识

阻塞

  • 线程的运行状态:运行 --> 阻塞 --> 就绪 --> 运行
  • 影响效率

非阻塞

  • 线程的运行状态:运行 --> 就绪 --> 运行

同步

  • 提交一个任务,等待他执行完毕
  • 只有提交的任务执行完毕才能执行其他操作
  • 影响效率

异步

  • 只管提交,不等待他执行完毕
  • 可以直接进行其他操作

IO步骤

  1. 数据等待阶段
  2. 从内核拷贝到进程阶段

非阻塞IO模型

  • 进行recv、recvfrom、accept等操作时不会阻塞,可以直接进行其他操作
  • 请求数据时,没有数据不阻塞,但是会返回error信息
  • 请求数据时,有数据是直接copy到应用程序
  • 不推荐使用,轮询会大量占用CPU

非阻塞IO模型实现socket并发

server

import socket

sk = socket.socket()
sk.bind(("127.0.0.1",9000))
sk.setblocking(False)# 设置socket为非阻塞模式
sk.listen()
conn_l = []# 用于存储conn
del_l = []# 用于存储已经断开连接需要删除的连接
while True:
    try:
        conn,addr = sk.accept()# 不阻塞,但是没有连接会报错
        conn_l.append(conn)# 将连接添加到用于统一管理的conn_l中
    except BlockingIOError:
        try:
            for conn in conn_l:# 利用了列表为空是for不执行的原理
                msg = conn.recv(1024)# 不阻塞,但是没有数据会报错
                if msg == b"":# 返回的内容为空则说明连接已经断开
                    del_l.append(conn)# 添加到待删除的列表中
                    continue

                print(msg)
                conn.send(b"bye")
        except BlockingIOError:pass
        for i in del_l:
            conn_l.remove(i)# 删除已经断开的连接
        del_l.clear()# 清空存储已经断开的连接的列表

client

import socket
import threading

def func():
    sk = socket.socket()
    sk.connect(("127.0.0.1",9000))
    sk.send(b"hello")
    print(sk.recv(1024))
    sk.close()


for i in range(20):
    # 使用多线程来模拟多个客户端连接服务器
    t = threading.Thread(target=func)
    t.start()

IO多路复用模型

  • IO多路复用是系统提供的
  • 相当于一个代理,这个代理可以监听多个对象的状态,当有对象接收到信息是返回一个状态给应用程序
  • 可以解决非阻塞IO高CPU占用的问题

IO多路复用实现socket并发

server

import select
import socket

sk = socket.socket()
sk.bind(("127.0.0.1",8080))
sk.setblocking(False)
sk.listen()
read_list = [sk]
while True:
    r_list,w_list,x_list = select.select(read_list,[],[])
    for i in r_list:
        if i is sk:
            conn,addr = i.accept()
            read_list.append(conn)
        else:
            ret = i.recv(1024)
            if ret == b"":
                i.close()
                read_list.remove(i)
                continue
            print(ret)
            i.send(b"bye")

client

import socket
import threading

def func():
    sk = socket.socket()
    sk.connect(("127.0.0.1",8080))
    sk.send(b"hello")
    print(sk.recv(1024))
    sk.close()


for i in range(20):
    # 使用多线程来模拟多个客户端连接服务器
    t = threading.Thread(target=func)
    t.start()
posted @ 2020-01-19 11:56  长江尾  阅读(146)  评论(0编辑  收藏  举报