11 非阻塞套接字与IO多路复用(进阶)

1、非阻塞套接字

第一部分 基本IO模型

1.普通套接字实现的服务端的缺陷

一次只能服务一个客户端!

2.普通套接字实现的服务端的瓶颈!!!

accept阻塞!

在没有新的套接字来之前,不能处理已经建立连接的套接字的请求。

recv 阻塞!

在没有接受到客户端请求数据之前,

不能与其他客户端建立连接!

3.普通服务器的IO模型

第二部分 非阻塞套接字

1.非阻塞套接字与普通套接字的区别
>>> import socket
>>> server = socket.socket()
# 讲socket设置成非阻塞
>>> server.setblocking(False)   # 注意!这必须要在其他操作之前!
>>> server.bind((‘‘,8080))
>>> server.listen(5)
?
>>> server.accept()     # 没有连接就引发BlockingIOError
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    server.accept()
  File "E:\python\lib\socket.py", line 205, in accept
    fd, addr = self._accept()
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
?
# 使用一个客户端(普通的就行,不需要非阻塞)连接过来
>>> conn,addr = server.accept()     # 有连接则正确返回
>>> conn.recv(1024)     # 没有连接数据就引发BlockingIOError
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    conn.recv(1024)
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
2.使用非阻塞套机字实现阻塞的服务端
# 原来的recv
while True:
    try:
        recv_data = conn.recv(1024)
        break
    except BlockingIOError:
        pass

# 原来的accept
while True:
    try:
        conn,addr = server.accept()
        break
    except:BlockingIOError:
        pass
3。非阻塞客户端套接的注意点

connect操作一定会引发BlockingIOError异常

如果连接没有建立,那么send操作引发OSError异常

第三部分 非阻塞IO模型

第四部分 使用非阻塞套接字实现并发

1.整体思路

吃满 CPU !宁可用 whileTrue ,也不要阻塞发呆!

只要资源没到,就先做别的事!将代码顺序重排,避开阻塞!

2.实现了什么?

并发服务多个客户端!

3.编程范式
import socket
server = socket.socket()        # 生成套接字
server.setblocking(False)       # 非阻塞
server.bind((‘‘,7788))
server.listen(1000)
?
# 我们现在生成的非阻塞套接字,非阻塞套接字在执行accept跟recv的时候不会阻塞,但是会报错,
# 所以我们写非阻塞的并发服务器需要用到异常处理
?
all_conn = []   # 用来保存我们所有的已经生成的套接字(这个客户端还在连接着)
while True:
    # 处理连接,生成对等连接套接字
    try:
        conn,addr = server.accept()
        conn.setblocking(False)     # conn是新生成的,需要给它设置一下非阻塞
        all_conn.append(conn)   # 这一行代码的前提是,上面一行代码正常返回
    except BlockingIOError:
        pass
    for conn in all_conn:
        try:    # 只负责接受数据
            recv_data = conn.recv(1024)
?
            if recv_data:
                res = recv_data.decode()
                print(res)
                conn.send(recv_data)
            else:
                conn.close()
                all_conn.remove(conn) # 客户端关闭连接,就把它移除
        except BlockingIOError:
            pass

2、IO多路复用

第一部分 不完美的CPU利用率

关键一: 任何Python操作都是需要花费CPU资源的 !

关键二: 如果资源还没有到达,那么

? accept、recv以及

? send(在connect没有完成时)

? 操作都是无效的CPU花费 !

关键三: 对应BlockingIOError的异常处理

? 也是无效的CPU花费 !

第二部分 epoll是真正的答案!

IO多路复用技术

我们把socket交给操作系统去监控

2.epoll是惰性的事件回调

惰性事件回调是由用户进程自己调用的。

操作系统只起到通知的作用。

3.为什么是epoll ?

目前Linux上效率最高的IO多路复用 技术 !

第三部分 IO多路复用选择器

1.注册惰性事件回调
>>> import socket
>>> import selectors
>>> server = socket.socket()
>>> server.bind((‘‘,9000))
>>> server.listen(1000)
>>> selector = selectors.EpollSelector()    # 实例化一个 epoll 选择器
>>> def create_conn(server):
...     conn,addr = server.accept()
...     return conn
...
# 套接字、事件、回调函数
>>> selector.register(server,selectors.EVENT_READ,create_conn)
SelectorKey(fileobj=<socket.socket fd=3,    # 生成一个打包对象
family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘0.0.0.0‘, 9000)>,   # fileobj是对应套接字
fd=3,
events=1,   # 事件(1 表示EVENT_READ)
data=<function create_conn at 0xb70b7b24>)  # data是对应的回掉函数
2.事件回调
events = selector.select()      # 查询,返回所有已经准备好的资源打包对象
print(enents)       # 是一个 ‘二元组’ 的列表
[(SelectorKey(fileobj=<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘0.0.0.0‘, 8888)>, fd=4, events=1, data=<function accept at 0xb71f292c>), 1)]
# 我们只需要关心,每个元祖的第一项(即打包对象,其中包含了对应的套接字与回掉函数)
# 接下来并不需要关心是什么套接字,什么事件,只需要调用对应的对调函数即可
callbeck = events[0][0].data
sock = events[0][0].fileojb
callbeck(sock)
3.编程范式
# 使用EpollSelector,实现一个并发的服务器
import socket
import selectors    # IO多路复用选择器的模块,接口,调用epoll
?
epoll_selector = selectors.EpollSelector()  # 创建一个用来和epoll通信的选择器
?
server = socket.socket()
server.bind((‘‘,8888))
server.listen(1000)
?
def read(conn):
    recv_data = conn.recv(1024)
    if recv_data:
        print(recv_data.decode())
        conn.send(recv_data)
    else:
        epoll_selector.unregister(conn)     # 现在数据已经传输完了,那我现在就不用再去监控它了,所以# 关闭监控
        conn.close()    # 关闭连接
?
def accept(server):
    conn, addr = server.accept()    # 生成一个对等连接套接字
    # 要准备接受数据
    epoll_selector.register(conn, selectors.EVENT_READ, read)
?
epoll_selector.register(server,selectors.EVENT_READ, accept)   # 注册事件在可以读的时候的回调函数
?
# 事件循环(主动去问epoll,哪些socket可以回调了,如果有了,那我就回调)
while True:
    events = epoll_selector.select()     # 查询所有的已经准备好的事件,返回一个列表(二元组列表)
    # a, b = events
    for key, mask in events:    # 第一项是我们需要用的
        callback = key.data    # 从key里面把回掉函数拿出来
        sock = key.fileobj     # 从key里面把我们注册的那个socket拿出来
        callback(sock)

原文地址:https://www.cnblogs.com/zcmq/p/9275875.html

时间: 2024-10-15 02:07:25

11 非阻塞套接字与IO多路复用(进阶)的相关文章

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

我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个客户端! 并且,为了使一个客户端能够不断收发消息,我们还要使用while循环来轮询,这极大地降低了我们的效率 accept阻塞! 在没有新的套接字来之前,不能处理已经建立连接的套接字的请求 recv 阻塞! 在没有接受到客户端请求数据之前,不能与其他客户端建立连接 可以用非阻塞接口来尝试解决这个问题! 阻塞IO模型 阻塞IO(blocking IO)的特点:就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被

C++ 非阻塞套接字的使用 (3)

异步非阻塞套接字避免了死循环的接收问题,但是软件用起来体验还是很差.究其原因,软件在指令的发送.接收上, 采取了一种不合理的方式:在指令的发送后,立刻调用接收函数,等待回令. 若是采用同步阻塞套接字,那么如果连接出现问题没有回令,那么软件进程会始终等待锁死,这样显然是不合理.于是 采用同步非阻塞的接收方式,timeout被设置为NULL.这时会出现发令与回令的错位问题.这是由于,相应回令没有及时到 达,而接收函数已经返回,第一条发令被当做无回令处理:而此时回令被堆积在了缓冲区,被第二条发令接收到

非阻塞套接字实现并发处理

服务端 import socket server = socket.socket() server.setblocking(False) server.bind(('0.0.0.0',8080)) server.listen(1000) all_connection = []#所有连接的客户端,用集合比列表更快 while True: try: connection,remote_address = server.accept()#非阻塞对等连接套接字生成 connection.setblock

C++ 非阻塞套接字的使用 (2)

继续话题——软件中的异步非阻塞通讯方式. 由于软件基于MFC开发,所以实现异步通讯时使用了CAsyncSocket类. 首先要了解CAsyncSocket异步机制,引用自 http://blog.csdn.net/tianhai110/article/details/2115270. 由于CAsyncSocket采用的是异步非阻塞机制,所以你随时可以发包,也随时可能收到包. 发送.接收函数都是异步非阻塞的,顷刻就能完成,所以收发交错进行着.也正因为如此,仅调用 它们并不能保障发送或接收的完成.例

五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O

五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O 五种I/O 模式:[1]        阻塞 I/O           (Linux下的I/O操作默认是阻塞I/O,即open和socket创建的I/O都是阻塞I/O)[2]        非阻塞 I/O        (可以通过fcntl或者open时使用O_NONBLOCK参数,将fd设置为非阻塞的I/O)[3]        I/O 多路复用     (I/O

阻塞、非阻塞、同步、异步IO

阻塞.非阻塞.同步.异步IO http://www.cnblogs.com/yunxitalk/p/9031306.html 介绍 在谈及网络IO的时候总避不开阻塞.非阻塞.同步.异步.IO多路复用.select.poll.epoll等这几个词语.在面试的时候也会被经常问到这几个的区别.本文就来讲一下这几个词语的含义.区别以及使用方式. Unix网络编程一书中作者给出了五种IO模型: 1.BlockingIO - 阻塞IO 2.NoneBlockingIO - 非阻塞IO 3.IO multip

一文读懂阻塞、非阻塞、同步、异步IO

原文:一文读懂阻塞.非阻塞.同步.异步IO 介绍 在谈及网络IO的时候总避不开阻塞.非阻塞.同步.异步.IO多路复用.select.poll.epoll等这几个词语.在面试的时候也会被经常问到这几个的区别.本文就来讲一下这几个词语的含义.区别以及使用方式.Unix网络编程一书中作者给出了五种IO模型:1.BlockingIO - 阻塞IO2.NoneBlockingIO - 非阻塞IO3.IO multiplexing - IO多路复用4.signal driven IO - 信号驱动IO5.a

sockets: 套接字的IO函数

########################################################### 套接字的IO函數 IO函数都涉及到阻塞问题,所以要考虑超时问题. 推荐使用sendmsg和recvmsg函数. 对socket的操作: #include<sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void*buff, size_t len, int flags); ssize_t s

简述linux同步与异步、阻塞与非阻塞概念以及五种IO模型

1.概念剖析 相信很多从事linux后台开发工作的都接触过同步&异步.阻塞&非阻塞这样的概念,也相信都曾经产生过误解,比如认为同步就是阻塞.异步就是非阻塞,下面我们先剖析下这几个概念分别是什么含义. 同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:异步的概念和同步相对.当一个异步过程