python 不一样的 非阻塞套接字与IO多路复用

本文介绍了Python中的非阻塞IO模型与IO多路复用,包括数据流的概念、阻塞IO的工作原理,详细讲解了非阻塞IO的实现并发及epoll在IO多路复用中的应用。

文章目录

  • 复习
  • 基本IO模
  • 非阻塞IO模型与非阻塞套接字
  • 非阻塞套接字实现并发
  • IO多路复
  • 总结

前言:

    很抱歉得说明一下,本月中旬会暂停更新博客因有事请假回家咯,后面会慢慢补齐,谢谢理解.

一 复习

一.1.1  传输模型

  • 掌握:传输层

一.1.2  TCP协议

  • 掌握: TCP 、 UDP

一.1.3 建立套接字

  • 掌握:sokcet

一.1.4 传输数据

  • 掌握: send,recv

详情见  :   http://t.csdn.cn/C1Ac0

二、基本IO模

二.1.1 数据流的概念

数据流(data stream)是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

数据流最初是通信领域使用的概念,代表传输中所使用的信息的数字编码信号序列。这个概念最初在1998年由Henzinger在文献87中提出,他将数据流定义为“只能以事先规定好的顺序被读取一次的数据的一个序列”。   详细见  :  数据流_百度百科

数据流(data stream):  是一组有序,有起点和 重点的字节的数据序列。是只能被读取一次或少 数几次的点的有序序列。

其包括输入流和输出流 数据流分为输入流(inputStream)输出流 (OutputStream)两类。

输入流只能读不能写而 输出流只能写不能读。通常程序中使用输入流读 出数据,输出流写入数据,就好像数据流入到程 序并从程序中流出。采用数据流使程序的输入输 出操作独立于相关设备。

输入流可以从键盘货或文件中获取数据,输出 流可向显示器、打印机或文件中传输数据

二.1.2  IO解释与IO交互

IOinput and output。在Unix世界里,一切皆文件。 而文件是什么呢?文件就是一串二进制流,不管 socket、还是FIFO、管道、终端,一切都是文件,一 切都是流。在信息交换的过程种,对这些流进行数据 的收发操作,简称I/O操作(input and output)

往流中读取数,系统调用read。写入数据,系统调用 write。但是计算机里会有很多的流,怎么知道要操作 哪个流呢?做到这个的就是文件描述符,即通常所说 的fd。一个fd就是一个整数,所以对这个整数的操作, 就是对这个文件(流)的操作。创建一个socket,通过 系统调用会返回一个文件描述符,那么剩下对socket 的操作就会转化为对这个描述符的操作

硬盘 -----👉 硬盘控制器   ------👉  (内核空间  (缓冲区)  )  -----👉(用户空间 (进程(缓冲区)))

二.1.3 阻塞IO

在实际情况汇总,很多时候数据在一 开始还没有到达,这个时候内核就要等 待足够的数据到来。 而在用户进程这边,整个进程会被阻 塞,当内核一直等到数据准备好了,它 就会将数据从内核中拷贝到用户内存, 然后返回结果,用户进程才接触阻塞的 状态,重新运行起来

三.非阻塞IO模型与非阻塞套接字

三.1.1非阻塞IO模型

从用户进程角度讲,它就是发起一个read操作后,并不需要等待,而是马上就得到一个结果,用户进程判断结果是一个error时,它就知道数据还没有准备好,

这里它就可以发送read操作,一旦内核中的数据准备好了,并且又再次收到用户进程系统的调用,那么它马上就将数据拷贝到用户内存,然后返回,非阻塞的接口相对比阻塞接口的显著差异再次,在被调用之后又立即返回.

 三.1.2非阻塞IO

利用异常处理来处理非阻塞IO产生的异常

import socket

server = socket.socket()
server.setblocking(False)  # 设置非阻塞套接字
server.bind(('127.0.0.1', 9000))
server.listen(10)

while True:
    try:
        conn, addr = server.accept()  # 创建对待连接套接字,等待别人与我进行链接。tcp建立稳定安全通道
    except BlockingIOError:
        pass
    except Exception as e:
        print('发生了未知的异常', e)
    else:
        data = conn.recv(1024)
        print(data)
        conn.send(data)

 三.1.3并发与并行

并发

  • 指一个时间段中有几个程序都处于已启动到运行完毕之间,且这几个程序都是在同一个处理 机上运行,但任一个时刻点上只有一个程序在处理机上运行

并行

  • 指一个时间段中有几个程序都处于已启动到运行完毕之间,且这几个程序都是在同一个处理机上运 行,并任一个时刻点上有多个程序在处理机上运行

三.1.4实现并发

并发 

  • 代码实现简单,但是要理解过程,主要是,还是用循环等思想去解决问题
#服务端
import socket

server = socket.socket()
server.setblocking(False)  # 设置非阻塞套接字
server.bind(('127.0.0.1', 8989))
server.listen(10)

# 定义全局变量,列表,保存所有连接进来的额客户端生成的对等连接套接字
all_conn = []
while True:
    try:
        conn, addr = server.accept()
    except BlockingIOError:
        pass
    except Exception as e:
        print("发生了未知的异常", e)

    else:
        print(addr, '连接成功')
        conn.setblocking(False)  # 设置对等连接套接字为非阻塞IO
        all_conn.append((conn, addr))  # 把连接上的客户端生成的对等连接套接字和地址端口保存在元组里面,并添加到all_conn

    for conn, addr in all_conn:  # 依次处理每个客户端发送的数据
        try:
            recv_data = conn.recv(1024)
        except BlockingIOError:
            pass
        except Exception as e:
            print("发生了未知错误", e)

        else:
            if recv_data:  # 如果有数据的话 去读取数据
                print(recv_data)
                conn.send(recv_data)
            else:
                conn.close()
                all_conn.remove((conn,addr))
#客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8989))
# client.send(b'hello')
# client.send(b'hello')
for i in range(10):
    client.send(b'hello')
    print(client.recv(1024))

四 IO多路复

四.1.1IO多路复

 在之前的非阻塞IO模型中,通过不断的询问查看是否有数据,这会造成资源浪费,

将查看的过程由主动的查询,变成交给复用器完成,这样能够更加节省系统资源,并且性能更好.

 四.1.2epoll

  • 非阻塞套接字与多路复用

非阻塞套接字需要自身遍历每个对等连接套接字,并且每次都是进行IO操作

复用器不需要进行大量的IO操作,复用器会告诉你哪个是对等连接套接字有数据过了,然后去处理即可.

  • epoll

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。详情见  :  epoll_百度百科

epoll 是一个懒性时间回调,即回调过程是用户自己去调用的,操作系统只起到一个通知作用,

epoll 是Linux上最好的IO多路复用器,但是也只有Linux上有,其他地方没有

 

#服务端
import socket
import selectors  # io多路复用器模块
import time

# 实例化一个复用器对象
default_sel = selectors.DefaultSelector()  # 默认选择器 根据操作系统自动选择

server = socket.socket()
server.bind(('127.0.0.1', 8989))
server.listen(10)


# 第一个:第一个是数据处理的方法
def f_recv(conn):
    reve_data = conn.recv(1024)
    if reve_data:
        print(reve_data)
        conn.send(reve_data)
    else:
        default_sel.unregister(conn)
        conn.close()


# 第二个:等待sever 有客户连接进来
def f_acept(server):
    conn, addr = server.accept()
    default_sel.register(conn, selectors.EVENT_READ, f_recv)


# 参数一:可能反正事件的对象 参数二:检查是否产生了事件,参数三:发生事件之后需要执行的功能
default_sel.register(server, selectors.EVENT_READ, f_acept)

while True:
    events = default_sel.select()  # 查询是否有事件发生
    # time.sleep(1)
    # print(events)
    for key, i in events:
        obj = key.fileobj  # 注册进来可能发生事件的对象
        func = key.data  # 注册进来需要调用的函数体
        func(obj)  # 具体的去执行
#客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8989))
# client.send(b'hello')
# client.send(b'hello')
for i in range(10):
    client.send(b'hello')
    print(client.recv(1024))

总结

 

                                  感谢大家支持,星月继续加油

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值