首页 > 代码库 > Python-Epoll
Python-Epoll
原文:http://scotdoyle.com/python-epoll-howto.html
Asynchronous Socket Programming Examples with epoll
Programs using epoll often perform actions in this sequence:
- Create an epoll object
- Tell the epoll object to monitor specific events on specific sockets
- Ask the epoll object which sockets may have had the specified event since the last query
- Perform some action on those sockets
- Tell the epoll object to modify the list of sockets and/or events to monitor
- Repeat steps 3 through 5 until finished
- Destroy the epoll object
1 import socket, select 2
3 EOL1 = b‘\n\n‘ 4 EOL2 = b‘\n\r\n‘ 5 response = b‘HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n‘ 6 response += b‘Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n‘ 7 response += b‘Hello, world!‘ 8 9 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)10 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)11 serversocket.bind((‘0.0.0.0‘, 8080))12 serversocket.listen(1)
#Since sockets are blocking by default, this is necessary to use non-blocking (asynchronous) mode.13 serversocket.setblocking(0)1415 epoll = select.epoll()16 epoll.register(serversocket.fileno(), select.EPOLLIN)1718 try:19 connections = {}; requests = {}; responses = {}20 while True:21 events = epoll.poll(1)22 for fileno, event in events:23 if fileno == serversocket.fileno():
#Events are returned as a sequence of (fileno, event code) tuples. fileno is a synonym for file descriptor and is always an integer.24 connection, address = serversocket.accept()25 connection.setblocking(0)26 epoll.register(connection.fileno(), select.EPOLLIN)27 connections[connection.fileno()] = connection28 requests[connection.fileno()] = b‘‘29 responses[connection.fileno()] = response30 elif event & select.EPOLLIN:31 requests[fileno] += connections[fileno].recv(1024)32 if EOL1 in requests[fileno] or EOL2 in requests[fileno]:33 epoll.modify(fileno, select.EPOLLOUT)34 print(‘-‘*40 + ‘\n‘ + requests[fileno].decode()[:-2])35 elif event & select.EPOLLOUT:36 byteswritten = connections[fileno].send(responses[fileno])37 responses[fileno] = responses[fileno][byteswritten:]38 if len(responses[fileno]) == 0:39 epoll.modify(fileno, 0)40 connections[fileno].shutdown(socket.SHUT_RDWR)41 elif event & select.EPOLLHUP:42 epoll.unregister(fileno)43 connections[fileno].close()44 del connections[fileno]45 finally:46 epoll.unregister(serversocket.fileno())47 epoll.close()48 serversocket.close()
epoll有两种工作模式:LT(level-triggered,水平触发),ET(edge-triggered,边缘触发)。默认情况下,epoll采用LT模式工作,这时可以处理阻塞和非阻塞套接字。ET模式只支持非阻塞套接字。ET模式与LT模式的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的;而LT模式则相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。
1 import socket, select 2 3 EOL1 = b‘\n\n‘ 4 EOL2 = b‘\n\r\n‘ 5 response = b‘HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n‘ 6 response += b‘Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n‘ 7 response += b‘Hello, world!‘ 8 9 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)10 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)11 serversocket.bind((‘0.0.0.0‘, 8080))12 serversocket.listen(1)13 serversocket.setblocking(0)1415 epoll = select.epoll()16 epoll.register(serversocket.fileno(), select.EPOLLIN | select.EPOLLET)1718 try:19 connections = {}; requests = {}; responses = {}20 while True:21 events = epoll.poll(1)22 for fileno, event in events:23 if fileno == serversocket.fileno():24 try:25 while True:26 connection, address = serversocket.accept()27 connection.setblocking(0)28 epoll.register(connection.fileno(), select.EPOLLIN | select.EPOLLET)29 connections[connection.fileno()] = connection30 requests[connection.fileno()] = b‘‘31 responses[connection.fileno()] = response32 except socket.error:33 pass34 elif event & select.EPOLLIN:35 try:36 while True:37 requests[fileno] += connections[fileno].recv(1024)38 except socket.error:39 pass40 if EOL1 in requests[fileno] or EOL2 in requests[fileno]:41 epoll.modify(fileno, select.EPOLLOUT | select.EPOLLET)42 print(‘-‘*40 + ‘\n‘ + requests[fileno].decode()[:-2])43 elif event & select.EPOLLOUT:44 try:45 while len(responses[fileno]) > 0:46 byteswritten = connections[fileno].send(responses[fileno])47 responses[fileno] = responses[fileno][byteswritten:]48 except socket.error:49 pass50 if len(responses[fileno]) == 0:51 epoll.modify(fileno, select.EPOLLET)52 connections[fileno].shutdown(socket.SHUT_RDWR)53 elif event & select.EPOLLHUP:54 epoll.unregister(fileno)55 connections[fileno].close()56 del connections[fileno]57 finally:58 epoll.unregister(serversocket.fileno())59 epoll.close()60 serversocket.close()
Performance Considerations
Listen Backlog Queue Size
In Examples 1-4, line 12 has shown a call to the serversocket.listen() method. The parameter for this method is the listen backlog queue size. It tells the operating system how many TCP/IP connections to accept and place on the backlog queue before they are accepted by the Python program. Each time the Python program calls accept() on the server socket, one of the connections is removed from the queue and that slot can be used for another incoming connection. If the queue is full, new incoming connections are silently ignored causing unnecessary delays on the client side of the network connection. A production server usually handles tens or hundreds of simultaneous connections, so a value of 1 will usually be inadequate. For example, when using abto perform load testing against these sample programs with 100 concurrent HTTP 1.0 clients, any backlog value less than 50 would often produce performance degradation.
相关:http://www.linuxidc.com/Linux/2013-02/79858.htm
TCP Options
The TCP_CORK option can be used to "bottle up" messages until they are ready to send. This option, illustrated in lines 34 and 40 of Example 5, might be a good option to use for an HTTP server using HTTP/1.1 pipelining.
相关:http://blog.sina.com.cn/s/blog_7303a1dc0101gcld.html
http://blog.csdn.net/dog250/article/details/5941637
On the other hand, the TCP_NODELAY option can be used to tell the operating system that any data passed to socket.send() should immediately be sent to the client without being buffered by the operating system. This option, illustrated in line 14 of Example 6, might be a good option to use for an SSH client or other "real-time" application.
Python-Epoll