zoukankan      html  css  js  c++  java
  • 非阻塞套接字与IO多路复用(转,python实现版)

    非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。epoll工作在非阻塞模式时,才会发挥作用。

    我们了解了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://blog.csdn.net/Stephen___Qin
  • 相关阅读:
    2018-8-10-win10-uwp-重启软件
    jquery动画滑入滑出
    jquery类操作
    jquery类操作
    jquery手风琴
    jquery突出显示
    jquery隔行变色
    jquery下拉菜单
    jquery筛选选择器
    jquery过滤选择器
  • 原文地址:https://www.cnblogs.com/Stephen-Qin/p/11857432.html
Copyright © 2011-2022 走看看