python select.epoll

来源:互联网 发布:win10虚拟机装mac 编辑:程序博客网 时间:2024/06/05 17:40

参考:

http://www.360doc.com/content/11/1119/15/2660674_165748138.shtml

http://blog.csdn.net/my2010sam/article/details/9877717


#!/usr/bin/env python'''epoll实现服务器时,需要用到register()和unregister()方法,作用是加入和移除对象,epoll()的返回值包括了文件描述符和事件,polling的事件常量有POLLIN,读取数据POLLPRI,紧急数据POLLPOUT,文件描述符已经准备好POLLERR,文件描述符出错POLLHUP,连接丢失POLLVAL,无效请求。epoll 对象操作流程建立一个epoll对象告诉epoll对象, 对于一些socket监控一些事件.问epoll, 从上次查询以来什么socket产生了什么事件.针对这些socket做特定操作.告诉epoll, 修改监控socket和/或监控事件.重复第3步到第5步, 直到结束.销毁epoll对象.采用异步socket的时候第3步重复了第2步的事情. 这里的程序更复杂, 因为一个线程需要和多个客户端交互.http://www.360doc.com/content/11/1119/15/2660674_165748138.shtml'''import socketimport selectimport argparseSERVER_HOST = ''EOL1 = b'\n\n'EOL2 = b'\n\r\n'SERVER_RESPONSE  = b"""HTTP/1.1 200 OK\r\nDate: Mon, 1 Apr 2013 01:01:01 GMT\r\nContent-Type: text/plain\r\nContent-Length: 25\r\n\r\nHello from Epoll Server!"""class EpollServer(object):    """ A socket server using Epoll"""    def __init__(self, host=SERVER_HOST, port=0):        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)        self.sock.bind((host, port))        self.sock.listen(5)        self.sock.setblocking(0)#设置socket为非阻塞模式        self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)        print "Started Epoll Server"        self.epoll = select.epoll() #建立一个epoll对象.        self.epoll.register(self.sock.fileno(), select.EPOLLIN)#注册服务器socket, 监听读取事件. 服务器socket接收一个连接的时候, 产生一个读取事件. fileno是文件描述符, 是一个整型数.    def run(self):        """Executes epoll server operation"""        try:            #connections表映射文件描述符(file descriptors, 整型)到对应的socket对象上面            #requests            #responses            connections = {}; requests = {}; responses = {}            while True:                #epoll对象查询一下是否有感兴趣的事件发生, 参数1说明我们最多等待1秒的时间. 如果有对应事件发生, 立刻会返回一个事件列表                events = self.epoll.poll(1)                #返回的events是一个(fileno, event code)tuple元组列表. fileno是文件描述符, 是一个整型数.                for fileno, event in events:                    #如果是服务器socket的事件, 那么需要针对新的连接建立一个socket.                    if fileno == self.sock.fileno():                        connection, address = self.sock.accept()                        connection.setblocking(0)#设置socket为非阻塞模式                        self.epoll.register(connection.fileno(), select.EPOLLIN) #注册socket的read(EPOLLIN)事件.                        connections[connection.fileno()] = connection                        requests[connection.fileno()] = b''                        responses[connection.fileno()] = SERVER_RESPONSE                    elif event & select.EPOLLIN:                        #如果读取事件发生, 从客户端读取新数据.(打印client http 请求头)                        requests[fileno] += connections[fileno].recv(1024)                        if EOL1 in requests[fileno] or EOL2 in requests[fileno]:                            #一旦完整的http请求接收到, 取消注册读取事件, 注册写入事件(EPOLLOUT), 写入事件在能够发送数据回客户端的时候产生                            self.epoll.modify(fileno, select.EPOLLOUT)                            #打印完整的http请求, 展示即使通讯是交错的, 数据本身是作为一个完整的信息组合和处理的.                            print('-'*40 + '\n' + requests[fileno].decode()[:-2])                    elif event & select.EPOLLOUT:                        # 如果写入事件发生在一个客户端socket上面, 我们就可以发送新数据到客户端了.                        # 一次发送一部分返回数据, 直到所有数据都交给操作系统的发送队列.                        byteswritten = connections[fileno].send(responses[fileno])                        responses[fileno] = responses[fileno][byteswritten:]                        if len(responses[fileno]) == 0:                            self.epoll.modify(fileno, 0)#一旦所有的返回数据都发送完, 取消监听读取和写入事件.                            connections[fileno].shutdown(socket.SHUT_RDWR)#如果连接被明确关闭掉, 这一步是可选的. 这个例子采用这个方法是为了让客户端首先断开, 告诉客户端没有数据需要发送和接收了, 然后让客户端断开连接.                    elif event & select.EPOLLHUP:#HUP(hang-up)事件表示客户端断开了连接(比如 closed), 所以服务器这端也会断开. 不需要注册HUP事件, 因为它们都会标示到注册在epoll的socket.                        self.epoll.unregister(fileno)                        connections[fileno].close()                        del connections[fileno]        finally:            #在这里的异常捕捉的作用是, 我们的例子总是采用键盘中断来停止程序执行.            #虽然开启的socket不需要手动关闭, 程序退出的时候会自动关闭, 明确写出来这样的代码, 是更好的编码风格.            self.epoll.unregister(self.sock.fileno())            self.epoll.close()            self.sock.close()if __name__ == '__main__':    parser = argparse.ArgumentParser(description='Socket Server Example with Epoll')    parser.add_argument('--port', action="store", dest="port", type=int, required=True)    given_args = parser.parse_args()    port = given_args.port    server = EpollServer(host=SERVER_HOST, port=port)    server.run()


0 0