python中的epoll

来源:互联网 发布:iphone4s可以用4g网络 编辑:程序博客网 时间:2024/06/04 17:53

epoll和select、pool都属于IO多路复用机制。
I/O多路复用:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(可读或可写),就能够通知程序进行相应的读写操作。(epoll采用事件通知机制)。
下表我们对比select模块中的select()、pool、epoll的优缺点以及机制。(有一个共同的优点就是在单进程中完成了并发)

这里写图片描述

以epoll的简单使用代码进行理解:
使用epoll搭建一个简单的TCP服务器为例:
功能一:等待客户端的进入

import select as selimport socket as sock#创建负责接收新客户端来访的套接字(TCP模式)server_socket = sock.socket(sock.AF_INET, sock.SOCK_STREAM)#重复绑定套接字端口,避免服务器异常关闭时再次启动的等待server_socket.setsockopt(sock.SOL_SOCKET, sock.SO_REUSEADDR, 1)#服务器绑定端口号server_socket.bind(('', 7788))#切换套接字状态server_socket.listen(10)#创建epoll对象check_atttr = sel.epoll()#注册套接字的文件描述符以及触发状态和触发机制(因为服务器只负责接收客户信息,所以只需要检测是否可以读)check_atttr.register(server_socket.fileno(), sel.EPOLLIN|sel.EPOLLET)#循环,使用事件通知机制检测,并对结果进行判断处理while True:    #epoll.poll()等待已注册的状态就绪的文件描述符并返回一个可迭代对象,这里与select() poll()不同,是等待不是轮询.    #如果没有状态就绪的文件描述符,那么就会堵塞等待    check_result = check_atttr.poll()    print(check_result)

尝试使用多个客户端连接的结果为:

[(3, 1)][(3, 1)][(3, 1)][(3, 1)]............

这里会返回一个固定的列表,里面有一个元祖,其中3代表,我们创建的套接字的fileno,1代表这个套接字的状态,也就是说当输出这个结果的时候,我们创建的套接字就被激活了,我们可以使用下一步,accept(),然后创建新的为客户端服务的套接字。
可以通过一个图来理解:

使用epoll.poll()时套接字的状态变化

对代码继续进行完善(创建为一个客户端服务的套接字):

import select as selimport socket as sock#创建负责接收新客户端来访的套接字(TCP模式)server_socket = sock.socket(sock.AF_INET, sock.SOCK_STREAM)#重复绑定套接字端口,避免服务器异常关闭时再次启动的等待server_socket.setsockopt(sock.SOL_SOCKET, sock.SO_REUSEADDR, 1)#服务器绑定端口号server_socket.bind(('', 7788))#切换套接字状态server_socket.listen(10)#创建epoll对象check_atttr = sel.epoll()#注册套接字的文件描述符以及触发状态和触发机制(因为服务器只负责接收客户信息,所以只需要检测是否可以读)print('----1------')check_atttr.register(server_socket.fileno(), sel.EPOLLIN|sel.EPOLLET)print('-----2-----')#通过文件描述符作为键,套接字或地址作为值的字典s2c_scoket = {}s2c_addr = {}#循环,使用事件通知机制检测,并对结果进行判断处理while True:    #epoll.poll()等待已注册的状态就绪的文件描述符并返回一个可迭代对象,这里与select() poll()不同,是等待不是轮询.    #如果没有状态就绪的文件描述符,那么就会堵塞等待    check_result = check_atttr.poll()    #print(check_result) 如果返回的套接字文件描述符和server_socket相等,就是说有新的客户端与我们进行了链接    for fd, event in check_result:        if fd == server_socket.fileno():            #完成与客户端的链接,并创建新的套接字            server2cilent, addr = server_socket.accept()            #要等待新的套接字的通知,因此将新套接字注册到epoll中            check_atttr.register(server2cilent.fileno(), sel.EPOLLIN|sel.EPOLLET)            #如果我们依然是通过文件描述符来确定是哪个server2cilent,这时候直接使用            #server2cilent只能找到最后创建的一个server2cilent,所以我们使用键值对保存套接字            s2c_scoket[server2cilent.fileno()] = server2cilent            s2c_addr[server2cilent.fileno()] = addr        #此时如果有一个非服务器套接字准备好,那么会得到这个套接字的文件描述符,因为只有两种套接字        else:            #通过键找到对应的值即套接字,堵塞等待这个客户端的消息到来            recvData = s2c_scoket[fd].recv(1024)            if len(recvData) > 0:                print('来自于%s的消息:%s'%(str(s2c_addr[fd]), recvData))            else:                print('%s已经与服务器断开了连接....'%str(s2c_addr[fd]))                s2c_scoket[fd].close()                #因为这个客户端已经断开了连接,当前这个套接字也就没有保存的意义,避免占用空间,将其注销,并删除掉键值对                check_atttr.unregister(fd)                del s2c_scoket[fd]                del s2c_addr[fd]check_atttr.close()

附:几种eventmask
这里写图片描述
最后的疑问是:EPOLLOUT怎么用??

0 0