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

我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个客户端!

并且,为了使一个客户端能够不断收发消息,我们还要使用while循环来轮询,这极大地降低了我们的效率

accept阻塞!

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

recv 阻塞!

在没有接受到客户端请求数据之前,不能与其他客户端建立连接

可以用非阻塞接口来尝试解决这个问题!

阻塞IO模型

阻塞IO(blocking IO)的特点:就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

什么是阻塞呢?想象这种情形:你要去车站接朋友,到了车站之后发现车还没到站,你现在有两种选择:

  1. 继续在等着,直到朋友的车到站。
  2. 先去干点别的,然后时不时地联系你的朋友询问车是否到站,朋友说到了,你再去车站

很明显,大多数人都会选择第二种方案。

而在计算机世界,这两种情形就对应阻塞和非阻塞忙轮询。

  • 非阻塞轮询:数据没来,进程就不停的去检测数据,直到数据来。
  • 阻塞:数据没来,啥都不做,直到数据来了,才进行下一步的处理。

非阻塞IO模型

非阻塞套接字和阻塞套接字的区别:

把套接字设置为非阻塞之后,如果没有得到想要的数据,就会抛出一个BlockingIOError的异常。我们可以通过捕获处理这个异常,让程序正常完成

非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有

非阻塞如何利用

  • 吃满 CPU !
  • 宁可用 while True ,也不要阻塞发呆!
  • 只要资源没到,就先做别的事!

编程范式:

服务端

import socket

CONN_ADDR = (‘127.0.0.1‘, 9999)
conn_list = []  # 连接列表
server = socket.socket()  # 开启socket
server.setblocking(False)  # 设置为非阻塞
server.bind(CONN_ADDR)  # 绑定IP和端口到套接字
server.listen(5)          # 监听,5表示最大挂起数
print(‘start listen‘)
while True:
    try:
        conn,addr = server.accept() #等待客户端连接,没有就抛出BlockingIOError
        conn.setblocking(False)
        print(‘{}已连接‘.format(addr))
        conn_list.append(conn)
    except BlockingIOError:
        pass
    conn_list = [x for x in conn_list]
    for conn_socket in conn_list:#对已连接的套接字进行轮询
        try:
            data = conn_socket.recv(1024) #如有客户端发送消息,则打印并返回
        except BlockingIOError:
            pass
        else: #else在不报错的时候才执行
            if data: #判断客户端发过来的是不是空
                print(data.decode())
                conn_socket.send(data)
            else: #若为空,表示客户端已断开
                conn_socket.close()
                conn_list.remove(conn_socket)
                print(‘客户端数目:{}‘.format(len(conn_list)))

客户端

import socket
client = socket.socket()
client.connect((‘127.0.0.1‘,9999))
while True:
    data = input(‘>>>>>‘)
    if data == ‘q‘: #按q退出
        break
    client.send(data.encode())

    response = client.recv(1024)

    print(response.decode())

非阻塞IO模型优点:实现了同时服务多个客户端,能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在“”同时“”执行)。

 但是非阻塞IO模型绝不被推荐

非阻塞IO模型缺点:

  • 不停地轮询,占用较多的CPU资源。
  • 对应BlockingIOError的异常处理也是无效的CPU花费 !

如何解决:多路复用IO

多路复用IO

什么是IO多路复用技术呢,简单来说,就是我们把套接字交给操作系统去监控。

使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,感觉效率更差。

但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,

即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

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

epoll是惰性的事件回调,惰性事件回调是由用户进程自己调用的,操作系统只起到通知的作用。

epoll实现并发服务器,处理多个客户端

 

import socket
import selectors

# 注册一个epllo事件参数
# 1. 需要操作系统监控的套接字
# 2.事件(可读还是可写)
# 3.回调函数

def recv_data(conn):
    data = conn.recv(1024)

    if data:
        print(‘接收的数据是:%s‘ % data.decode())
        conn.send(data)
    else:
        print(‘断开连接‘,conn)
        e_poll.unregister(conn)
        conn.close()

def accept_conn(p_server):
    conn, addr = p_server.accept()
    print(‘Connected by‘, addr)
    # 也要注册一个事件
    e_poll.register(conn,selectors.EVENT_READ,recv_data)

CONN_ADDR = (‘127.0.0.1‘, 9999)
server = socket.socket()
server.bind(CONN_ADDR)
server.listen(6)

# 生成一个epllo选择器实例 I/O多路复用,监控多个socket连接
e_poll = selectors.DefaultSelector() # Linux是epoll,Windows是select
e_poll.register(server, selectors.EVENT_READ, accept_conn)

# 事件循环
while True:
    # 事件循环不断地调用select获取发生变化的socket
    events = e_poll.select()
    for key, mask in events:
        call_back = key.data #key.data就是回调函数
        call_back(key.fileobj) #key.fileobj是套接字

原文地址:https://www.cnblogs.com/woaixuexi9999/p/9301879.html

时间: 2024-08-06 14:00:54

非阻塞套接字与IO多路复用的相关文章

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

1.非阻塞套接字 第一部分 基本IO模型 1.普通套接字实现的服务端的缺陷 一次只能服务一个客户端! 2.普通套接字实现的服务端的瓶颈!!! accept阻塞! 在没有新的套接字来之前,不能处理已经建立连接的套接字的请求. recv 阻塞! 在没有接受到客户端请求数据之前, 不能与其他客户端建立连接! 3.普通服务器的IO模型 第二部分 非阻塞套接字 1.非阻塞套接字与普通套接字的区别 >>> import socket >>> server = socket.sock

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

五种网络IO模型-阻塞I/O、非阻塞I/O、I/O多路复用、信号驱动I/O、异步I/O

1.阻塞I/O模型 我去面馆吃面,点了碗面条,我也不知道做面条需要多久,也不敢出去,只能在那里坐着等.等值师傅做完面,我吃完面条后,再去逛街.这里的吃面就是I/O操作. 2.非阻塞I/O模型 我不甘心在这里等着无聊,我想这段时间继续逛商场,但是又怕面条做好了,所以,我逛一会就回来吧台看一眼!为了吃碗面,来来回回跑,差点没跑断腿! 3.I/O多路复用 我同时在不同的地方点了菜,要跑来跑去的轮询几十遍,累死了.后来,善良的管理员安装了电子屏幕:上面写着每个菜的状态,我需要去看屏幕,就知道数据准备好了

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