首页 > 其他分享 >IO模型

IO模型

时间:2024-01-25 14:58:59浏览次数:45  
标签:模型 server client IO msg 链接 conn

(一)IO模型简介

  • 目前我们眼睛的IO都是基于网络IO的
  • Stevens在文章中一共比较了五种IO Model:
    • blocking IO 阻塞IO模型
    • nonblocking IO 非阻塞IO模型
    • IO multiplexing IO多路复用模型
    • signal driven IO ---(忽略)
    • asynchronous IO 异步IO模型
    • 由signal driven IO(信号驱动IO)在实际中并不常用,所以主要介绍其余四种IO Model。
  • 当一个read操作发生时,该操作会经历两个阶段:
    • 1、等待数据准备 (Waiting for the data to be ready)
    • 2、将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
  • 同步
    • 在同步模式下,一个组件必须等待另一个组件完成某个操作,然后再进行下一个操作。
  • 异步
    • 在异步模式下,组件之间互不干扰,它们可以独立地执行任务。当一个组件完成任务后,它可以直接通知另一个组件继续执行操作。
  • 阻塞
    • 在阻塞模式下,一个进程或线程在执行某个操作时,如果该操作没有完成,它将立即停止一切操作,并等待操作完成后再继续进行后续操作。
  • 非阻塞
    • 在非阻塞模式下,一个进程或线程在执行某个操作时,即使该操作没有完成,也会立即返回执行下一个操作,不会出现停滞等待的情况。 如果下一个操作需要依赖于上一个还未完成的操作,它将定期检查上一个操作是否完成,保证这些未完成的操作不会影响后续任务的执行。
  • 常见的网络IO
    • accept
    • recv
    • recvfrom

(二)阻塞IO模型

(1)TCP/UDP协议模型

img

(2)TCP 经典IO

  • 客户端
from socket import *

client = socket()

IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

while True:
    msg_to_server = 'hello world'
    msg_to_server = msg_to_server.encode('utf-8')

    client.send(msg_to_server)

    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('utf-8')

    print(msg_from_server)
  • 服务端
from socket import *

server = socket()

IP = '127.0.0.1'
PORT = 8082
server.bind((IP, PORT))

server.listen(5)

while True:
    conn, addr = server.accept()

    while True:
        try:
            msg_from_client = conn.recv(1024)

            if len(msg_from_client) == 0:
                break

            print(msg_from_client.decode('utf8'))

            conn.send(msg_from_client.upper())

        except  Exception as e:
            print(e)
            break

    conn.close()
  • 问题:服务端只能接待一个客户端

(3)解决方案

  • 在服务端开始多进程或多线程,进程池线程池
  • 但是还没在根本上解决IO问题
  • 因为在服务端 虽然解决了 请求数据这部分的IO 但是没有从根本上解决 向内核要数据和拷贝数据给客户端这个IO

(二)非阻塞IO模型

  • 将所有的阻塞操作变为非阻塞

img

(1)TCP 非阻塞经典IO

  • 服务端
from socket import *
import time

server = socket()

IP = '127.0.0.1'
PORT = 8083
server.bind((IP, PORT))

server.listen(5)

# 将所有的网络阻塞 变为 非阻塞
# BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。 ---- 表示服务端并没有立即建立链接,导致了资源无法接收
server.setblocking(False)

conn_list = []
del_conn_list = []
while True:
    try:
        conn, addr = server.accept()
        conn_list.append(conn)

    # 检测进程资源利用错误
    except BlockingIOError as e:
        print(f'当前无阻塞,可进行其他工作')
        for conn in conn_list:
            try:
                data_from_client = conn.recv(1024)  # 没有消息则报错
                if len(data_from_client) == 0:
                    # 如果接收到的消息为 0  则表示当前链接无效:关闭当前链接
                    conn.close()  # (客户端断开连接)
                    # 将无用的conn链接从链接列表中删除掉
                    del_conn_list.append(conn)  # (将无用的链接放到无用的链接列表里)
                    continue
                    
                # 链接对象正常:反馈消息给客户端
                conn.send(data_from_client.upper())
            # 检测进程资源利用错误
            except BlockingIOError as e:
                # 查看其他链接对象是否有消息发过来
                continue

            # 检测到客户端断开链接错误
            except ConnectionError as e:
                # 断开链接
                conn.close()
                # 将无效的链接添加到废弃列表里
                del_conn_list.append(conn)

        # 回收无用的链接对象
        for conn in del_conn_list:
            # 在有效列表内删除无效的链接对象
            conn_list.remove(conn)

        # 清空无效链接列表
        del_conn_list.clear()
  • 客户端
from socket import *

client = socket()

IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

while True:
    msg_to_server = 'hello world'
    msg_to_server = msg_to_server.encode('utf-8')

    client.send(msg_to_server)

    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('utf-8')

    print(msg_from_server)

(2)总结

  • 虽然非阻塞 IO 看起来十分优秀
    • 但是该模型会长时间占用 CPU 而不处理数据(大部分资源都浪费在反复发起请求 请求数据上)
  • 实际应用中不会考虑使用非阻塞 IO 模型
    • 占用资源严重

(三)IO多路复用

(1)什么是IO多路复用

img

  • 当监管的对象只有一个的时候,其实IO多路复用连阻塞IO的功能都比不上
  • 但是IO多路复用一次性可以监管多个对象
    • server = socket.socket()
    • conn, addr = socket.accept()
  • 这个监管机制是由操作系统本身就存在的(select)
  • 如果使用需要导入相关的模块(select模块)

(2)基于 select模块解决 IO 阻塞

  • 客户端
from socket import *
import select

server = socket()

IP = '127.0.0.1'
PORT = 8083
server.bind((IP, PORT))
server.listen(5)

# 将阻塞模型变为非阻塞模型
server.setblocking(False)

# 将监管目标(谁来发起链接)添加到监管列表
read_list = [server]

while True:
    r_list, w_list, x_list = select.select(read_list, [], [])  # 检测server端
    # 返回值(server对象,[],[]) : ([<socket.socket fd=408, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8083)>], [], [])
    # 一旦有链接发起,直接拿到监管链接

    # 循环拿到每一个发起的链接对象
    for i in r_list:
        '''
        针对不同的对象进行分类处理
        '''

        # 如果是服务对象就将其添加到服务列表内,再次确认
        if i is server:
            # 拿到每一个链接对象
            conn, addr = i.accept()

            # 将链接对象添加到监管机制中
            read_list.append(conn)
        else:
            # 如果是链接请求就拿到客户端发起的数据
            msg_from_client = i.recv(1024)
            if len(res) == 0:
                # 关闭无效链接
                i.close()

                # 将无效链接从列表中移除
                read_list.remove(i)
                continue
            msg_from_client = msg_from_client.decode('utf-8')
            print(msg_from_client)

            # 反馈信息给客户端
            i.send(msg_from_client.upper())
  • 服务端
from socket import *

client = socket()

IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

while True:
    msg_to_server = 'hello world'
    msg_to_server = msg_to_server.encode('utf-8')

    client.send(msg_to_server)

    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('utf-8')

    print(msg_from_server)

(3)总结

  • 监管机制有很多
  • select机制
    • Windows/Mac 都有
  • poll机制
    • 只有Linux有
    • poll 和 select 机制都可以监管多个对象,但是 poll监管的对象更多

上述两个机制都不是很完美

当监管的对象特别多的时候,可能会出现机器大的延迟响应

  • epoll机制
    • 只在 Linux 机制有
    • 给每个监管对象都绑定了一个回调机制
    • 一旦有响应,回调机制会立刻发起提醒

针对不同的操作系统,需要考虑不同的监管机制,太过麻烦

因此有了一个新的模块(selectors模块)

根据平台的不同而自动选择不同的机制

(四)异步IO

  • 异步IO 是所有模型中效率最高的,也是使用最广泛的

img

  • 由于涉及到调用操作系统底层,我们只能通过C语言代码才能模拟出效果
  • 但是,有相关大佬已经帮我们将相关的方法进行了封装
  • 模块
    • asyncio 模块
  • 异步框架
    • sanic
    • tronado
    • twisted

(1)asyncio 模块安装

pip install asyncio

(2)方法示例

  • 单线程实现并发效果
import threading
import asyncio


async def hello():
    print(f'hello world statr:>>{threading.current_thread()}')

    await asyncio.sleep(1)  # 真正的 IO 操作 ---- 交互时操作 / 响应式操作

    print(f'hello world end:>>>{threading.current_thread()}')


loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))

loop.close()

# 单线程实现并发效果

# hello world statr:>><_MainThread(MainThread, started 8005963776)>
# hello world statr:>><_MainThread(MainThread, started 8005963776)>
# hello world end:>>><_MainThread(MainThread, started 8005963776)>
# hello world end:>>><_MainThread(MainThread, started 8005963776)>

(五)IO总结

  • 四个IO模型对比

img

标签:模型,server,client,IO,msg,链接,conn
From: https://www.cnblogs.com/suyihang/p/17987138

相关文章

  • iOS 多线程复习
    iOS中的线程主要有四种:1.pThread2.NSThread3.GCD4.NSOpreaction基础知识:线程、任务和队列的概念: 异步、同步&并行、串行的特点:组合特点:  1.pThread C语言所写,面向过程,使用较少.oc:#pragmaMark-pThread-(void)pThreadDemo{pthread_tpthre......
  • A Format Compliant Encryption Method for 3D Objects Allowing Hierarchical Decryp
    Abstract—Withtheincreasingpopularityof3Dobjectsinindustryandeverydaylife,3Dobjectsecurityhasbecomeessential.Whilethereexistsmethodsfor3Dselectiveencryption,whereaclear3Dobjectisencryptedsothattheresulthasthedesiredl......
  • F. Sum of Progression
    原题链接题解关键要素:两次后缀和预处理\(d<\sqrt{n}\)的情况,当\(d\)大于\(\sqrt{n}\)时,直接暴力,总时间复杂度为\(O(n\sqrt{n})\)代码比讲述要清晰,请直接看代码code#include<bits/stdc++.h>#definelllonglongusingnamespacestd;lla[100005]={0};llsufs1[100335][......
  • 【精品教程】如何查看iOS崩溃日志
    简介当一个应用程序崩溃,会产生一个崩溃报告(crashreport),并存储到设备中。崩溃报告描述了应用程序崩溃的条件,通常包含每个执行线程的完整回溯。查看崩溃报告可以帮助我们了解应用程序的崩溃情况,并尝试修复问题。符号化崩溃报告崩溃报告需要进行符号化(symbolicated),才能够进行分析......
  • Error: unable to perform an operation on node 'rabbit@pro'. Please see diagnosti
    简短的和全限定RabbitMQ节点名称rabbitmq支持简短的和全限定域名作为节点名称,但是默认的是简短的,我这里使用了全限定的域名,所以在集群操作stop_app的时候报错了  在rabbitmq安装目录下的/etc/rabbitmq加上配置文件rabbitmq-env.conf(环境变量)就可以了#开启使用全限定节点名......
  • 2024-1-25axios错误处理
    目录axios错误处理axios错误处理该错误是当时在POST案例出现的,当提交过一次用户后再次提交出现了报错。场景:再次注册相同的账号,会遇到错误信息处理:用更直观的方式,给普通用户展示错误信息错误处理固定格式语法:在then方法的后面,通过点语法调用catch方法,传入回调函数error并定......
  • 《SAIS Supervising and Augmenting Intermediate Steps for Document-Level Relation
    代码 原文地址 预备知识:1.什么是标记索引(tokenindices)?标记索引是一种用于表示文本中的单词或符号的数字编码。它们可以帮助计算机理解和处理自然语言。例如,假如有一个字典{"我":1,"是":2,"Bing":3,".":4},那么文本"我是Bing."的标记索引就是[1,2,3,4]。不同的模......
  • 【sqlsever】具体案例理解PARTITION BY
    当使用PARTITIONBY时,它通常是与窗口函数一同使用的。下面将提供一个简单的例子,使用一个包含以下列的表:+---------+---------+---------+|column1|column2|column3|+---------+---------+---------+|A|1|10||A|2|20|......
  • BindingException: Invalidbound statement (not found)
    一、报错二、原因未扫描到Mapper文件三、解决方式一mybatis:mapperLocations:classpath:mapper/**/*.xml方式二<!--项目打包时会将java目录中的*.xml文件也进行打包--><build><resources><resource><directory>src/main/java</di......
  • Docker启动Nacos报错:Nacos Server did not start because dumpservice bean construct
    一、表象重启服务器之后Docker运行Nacos容器,启动成功,但是外网无法访问。查看了一下Nacos启动日志(dockerlogsnacos容器名)二、分析很明显是数据库配``置问题。。如果是数据库配置的问题,可以着重检查以下信息尤其是MySQL内网Host,查询方式见Docker安装Nacos三、解决我已......