EPOLL,阻塞和非阻塞模式以及多路复用说明

阻塞

在网络通信过程中,像recvfrom()/recv()/accept()等相关的函数,在进行网络相关数据的接收时,会默认阻塞的等待,这种通信方式叫做阻塞IO

非阻塞

非阻塞就是没有接收数据的时候,并没有继续等待,而是报出一个异常,这样程序就会执行到下个流程继续执行,不会影响到后面的操作

IO多路复用

多路IO好处就在于单个进程就可以同时处理多个网络连接的IO 通过一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回

轮询

就是在socket通信过程中,通过for/while死循环,监听每个socket的通信状态,并且执行效率和轮询的socket数量息息相关,一般在1024-2048左右

  • EPOLLIN (可读)
  • EPOLLOUT (可写)
  • EPOLLET (ET模式)

epoll对文件描述符的操作有两种模式:

  • LT(level trigger)--水平触发
  • ET(edge trigger)--边界触发

LT模式是默认模式,LT模式与ET模式的区别如下:

  • LT模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll时,会再次响应应用程序并通知此事件。
  • ET模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll时,不会再次响应应用程序并通知此事件。

epoll模型

由操作系统来负责监听每个任务(socket),当某个socket有数据时,操作系统就通知用户进程

import select
import socket

def main():
    # 创建套接字  设置
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    #
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    server_socket.setblocking(False)

    # bind listen
    server_socket.bind(('', 8080))
    server_socket.listen(128)

    # 创建epoll对象
    epoll = select.epoll()

    # socket {1:socket1,2:socket2}
    socket_list = {}

    # address {1:address, 2:address2}
    address_list = {}

    # 将服务器套接字 添加-注册到epoll对象的监视列表 fd = file descriptor
    epoll.register(server_socket.fileno(), select.EPOLLIN)

    while True:
        # 获取监听结果清单列表 [()()()()()]
        epoll_list = epoll.poll()

        # 循环便利 清单列表 对其中客户端进行处理
        # fd表示有事件到达的套接字的文件描述符  events代表事件
        for fd, events in epoll_list:
            #    如果有数据到达的是服务器套接字 说明 服务器收到了来自客户端的连接请求 accept
            if fd == server_socket.fileno():
                new_client_socket, new_client_addr = server_socket.accept()
                print("接受到来自%s的连接请求" % str(new_client_addr))
                # 设置套接字为非阻塞模式
                new_client_socket.setblocking(False)

                # 将新来的客户端添加到 epoll的监视列表中 和 本地的集合中
                # 流式报文协议 -- 没有消息边界
                epoll.register(new_client_socket.fileno(), select.EPOLLIN|select.EPOLLET)
                # epoll.register(new_client_socket.fileno(), select.EPOLLIN)

                # 将新来的客户端的套接字 地址信息保存在集合中
                socket_list[new_client_socket.fileno()] = new_client_socket
                address_list[new_client_socket.fileno()] = new_client_addr

            #                不是                                   数据    recv
            elif events == select.EPOLLIN:  #  客户端
                # 从集合中根据fd获取 对应的套接字信息
                client_socket = socket_list[fd]

                # 接收数据
                recv_data = client_socket.recv(5)

                #
                if recv_data:
                    print("收到来自%s的数据:%s" % (str(address_list[fd]),recv_data))
                else:
                    print("接收到来%s的断开请求" % str(address_list[fd]))

                    # 如果客户端关闭套接字 本地需要关闭套接字
                    # 从epoll监视列表中移除
                    epoll.unregister(fd)

                    # 从集合中删除客户端套接字对象 和 地址对象
                    del socket_list[fd]
                    del address_list[fd]
                    # 关闭客户端套接字
                    client_socket.close()

if __name__ == '__main__':
    main()
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值