epoll編寫web服務器

編寫一個簡單的web服務器,向每個鏈接服務器的網頁瀏覽器返回一行文本。html

腳本核心在web服務器的初始化過程當中調用select.epoll(),註冊服務器的文件描述符,已達到事件通知的目的。python

 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》web

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

相關文章
相關標籤/搜索