epoll编写web服务器

编写一个简单的web服务器,向每一个连接服务器的网页浏览器返回一行文本。

脚本核心在web服务器的初始化过程中调用select.epoll(),注册服务器的文件描述符,已达到事件通知的目的。

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import socket
 5 import select
 6 import argparse
 7 
 8 SERVER_HOST = 'localhost'
 9 
10 EOL1 = b'\n\n'
11 EOL2 = b'\n\r\n'
12 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!"""
13 
14 class EpollServer(object):
15     """ a socket server using epoll"""
16     def __init__(self, host=SERVER_HOST, port=0):
17         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18         #创建套接字
19         self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
20         #设置当前套接字选项为可重用
21         self.sock.bind((host, port))#绑定
22         self.sock.listen(1)#监听
23         self.sock.setblocking(0)#设置套接字模式为非阻塞
24         self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
25         #socket阻塞模式自动开启Nagle算法。设置套接字选项关闭。Nagle算法用于对缓冲区内的一定数量的消息进行自动连接。
26         print "Started Epoll Server"
27         self.epoll = select.epoll()#创建epoll对象
28         self.epoll.register(self.sock.fileno(), select.EPOLLIN)
29            #为该socket的read event注册interest
30            #EPOLLIN表示对应的文件描述符可以读,包括对端SOCKET正常关闭
31            #fileno()返回的是该socket的一个整型文件描述符
32 
33     def run(self):
34         """Executes epoll server operation"""
35         try:
36             connections = {}    #连接对象与socket对象的映射
37             requests = {}        
38             responses = {}
39             while True:
40                 events = self.epoll.poll(1)
41                 #查询epoll对象是否可能发生任何interest的事件。1等待一秒
42                 for fileno, event in events:#遍历事件
43                     #如果事件发生在服务器
44                     if fileno == self.sock.fileno():
45                         connection, address = self.sock.accept()#接收客户端socket和地址
46                         connection.setblocking(0)#设置非阻塞模式
47                         self.epoll.register(connection.fileno(), select.EPOLLIN)
48                         #为新的socket的read event注册兴趣                    
49                         connections[connection.fileno()] = connection#添加到connections
50                         requests[connection.fileno()] = b''
51                         responses[connection.fileno()] = SERVER_RESPONSE#要发送的内容
52 
53                     #如果一个读事件发生在客户端,那么读取从客户端发来的新数据
54                     elif event & select.EPOLLIN:
55                         requests[fileno] += connections[fileno].recv(1024)
56                         if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
57                             self.epoll.modify(fileno, select.EPOLLOUT)
58                              #注销对read event的interest,注册对write event的interest
59                              print('-'*40 + '\n' + requests[fileno].decode()[:-2])
60                             #输出完整的请求,去除最后一个\r\n
61 
62                      #如果一个写事件发生在客户端,那么可能要接受来自客户端的新数据
63                     elif event & select.EPOLLOUT:
64                     #EPOLLOUT表示对应的文件描述符可以写
65                         byteswritten = connections[fileno].send(responses[fileno])
66                         responses[fileno] = responses[fileno][byteswritten:]
67                         if len(responses[fileno]) == 0:#如果无响应
68                             self.epoll.modify(fileno, 0)#禁用interest
69                             connections[fileno].shutdown(socket.SHUT_RDWR)
70                             #将对应的socket连接关闭
71 
72                     #如果一个中止事件发生在客户端
73                     elif event & select.EPOLLHUP:
74                     #EPOLLHUP表示对应的文件描述符被挂断
75                         self.epoll.unregister(fileno)#注销客户端interest
76                         connections[fileno].close()#关闭socket连接
77                         del connections[fileno]#删除映射
78         finally:
79             self.epoll.unregister(self.sock.fileno())#注销服务器interest
80             self.epoll.close()#关闭服务器epoll
81             self.sock.close()#关闭服务器socket
82 
83 if __name__ == '__main__':
84     parser = argparse.ArgumentParser(description='Socket Server Example with Epoll')
85     parser.add_argument('--port', action="store", dest="port", type=int, required=True)
86     given_args = parser.parse_args()
87     port = given_args.port
88     server = EpollServer(host=SERVER_HOST, port=port)
89     server.run()

 

参考文献: 《Python Network Programming Cookbook》

      http://scotdoyle.com/python-epoll-howto.html#source-code

转载于:https://www.cnblogs.com/tindin/p/4650799.html

你可能感兴趣的:(epoll,python)