zoukankan      html  css  js  c++  java
  • python使用select和epoll实现IO多路复用实现并发服务器

    在select模块中, 有三种方法实现IO多路复用并发服务器

    • select
    • poll
    • epoll

    select的原理: 在多路复用的模型中,比较常用的有select模型和epoll模型。这两个都是系统接口,由操作系统提供。当然,Python的select模块进行了更高级的封装。

    网络通信被Unix系统抽象为文件的读写,通常是一个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。

    这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回改文件描述符。当遍历结束之后,如果仍然没有一个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。

    例如使用select实现echo(回显)服务器

    import select
    import socket
    import sys
    
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('', 7788))
    server.listen(5)
    
    inputs = [server, sys.stdin]
    
    running = True
    
    while True:
    
        # 调用 select 函数,阻塞等待
        readable, writeable, exceptional = select.select(inputs, [], [])
    
        # 数据抵达,循环
        for sock in readable:
    
            # 监听到有新的连接
            if sock == server:
                conn, addr = server.accept()
                # select 监听的socket
                inputs.append(conn)
    
            # 监听到键盘有输入
            elif sock == sys.stdin:
                cmd = sys.stdin.readline()
                running = False
                break
    
            # 有数据到达
            else:
                # 读取客户端连接发送的数据
                data = sock.recv(1024)
                if data:
                    sock.send(data)
                else:
                    # 移除select监听的socket
                    inputs.remove(sock)
                    sock.close()
    
        # 如果检测到用户输入敲击键盘,那么就退出
        if not running:
            break
    
    server.close()

    但是在底层原理中, select和epoll 都是使用轮询原理来实现的. epoll是触发通知机制

    epoll的优点:

    • 没有最大并发连接的限制,能打开的FD(指的是文件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024
    • 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select和poll。
    import socket
    import select
    
    # 创建套接字
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 可重复绑定
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
    # 绑定本机地址端口
    s.bind(("", 7788))
    
    # 变为被动服务器
    s.listen(1024)
    
    # 创建一个epoll对象
    epoll = select.epoll()
    
    # 在epoll中注册s套接字(注意此处不是直接用是s, 而是使用s的fileno)
    epoll.register(s.fileno(), select.EPOLLIN|select.EPOLLET)
    
    
    # 创建两个字典, 来保存fileno和与其对应的套接字和地址
    connections = {}
    addresses = {}
    
    # 开始等待客户端发送来的信息
    while True:
        
        # 对epoll中的套接字进行扫描
        epollList = epoll.poll()
    
        # 对扫描到的事件进行判断
        for fd,events in epollList:
            
            # 如果判断是s套接字
            if fd == s.fileno():
                conn,addr = s.accept()
    
                print("有新的客户端到来...%s"%str(addr))
                connections[conn.fileno()] = conn
                addresses[conn.fileno()] = addr
    
                epoll.register(conn.fileno(), selecte.EPOLLIN|select.EPOLLET)
    
            # 如果是接收到了数据
            elif events == select.EPOLLIN:
                recvData = connections[fd].recv(1024)
    
                if len(recvData) > 0:
                    print("recvData: %s"%recvData)
    
                else:
                    epoll.unregister(fd)
                    connections[fd].close()
    
                    print("%s....offline....."%str(addresses[fd]))

    引:https://www.jianshu.com/p/cdfddb026db0




  • 相关阅读:
    ubuntu下cmake自动化编译的一个例子
    KL变换和PCA的数学推导
    tensorflow c++ API加载.pb模型文件并预测图片
    tensorflow c++接口的编译安装与一些问题记录
    深度增强学习--总结下吧
    深度增强学习--DPPO
    深度增强学习--DDPG
    深度增强学习--A3C
    tomcat远程调试
    springboot问题记录
  • 原文地址:https://www.cnblogs.com/070727sun/p/11065992.html
Copyright © 2011-2022 走看看