原文位址:http://scotdoyle.com/python-epoll-howto.html
介紹
Python已于2.6版本添加通路Linux epoll庫的API。這篇教程使用Python 3簡要介紹如何使用Linux epoll。
阻塞Socket
例1是一個Pyhton服務端程式,它監聽8080端口,接收HTTP請求并将其列印到console,然後對HTTP請求進行回複。
#Example 1
import socket
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'
#建立socket
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080))
serversocket.listen(1)
connectiontoclient, address = serversocket.accept()
request = b''
while EOL1 not in request and EOL2 not in request:
request += connectiontoclient.recv(1024)
print(request.decode())
connectiontoclient.send(response)
connectiontoclient.close()
serversocket.close()
下面的代碼添加了一個循環,不斷接收用戶端的請求直到被我們手動關閉。例2更清楚地展示了服務端建立的socket不直接與用戶端進行資料交換,而是服務端socket接收到來自用戶端的連接配接後,建立一個新的socket與該用戶端進行通信。
#Example 2
import socket
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080))
serversocket.listen(1)
try:
while True:
connectiontoclient, address = serversocket.accept()
request = b''
while EOL1 not in request and EOL2 not in request:
request += connectiontoclient.recv(1024)
print('-'*40 + '\n' + request.decode()[:-2])
connectiontoclient.send(response)
connectiontoclient.close()
#確定程式即使出現exception,socket也會被關閉
finally:
serversocket.close()
非阻塞Socket和Linux epoll的好處
上面兩個例子展示的都是阻塞socket。Python代碼執行到某行會停在那裡直到某一事件發生。如16行的accept()會被阻塞直到有一個用戶端請求連接配接。19行的recv()會被阻塞直到用戶端發送了資料(或沒有資料可讀取)。21行的send()會被阻塞直到所有要發送給用戶端的資料已經Linux被放入了傳輸隊列。
當一個服務端程式使用阻塞socket時,對于每一個用戶端,其經常使用一個線程甚至一個專門的程序進行通訊。服務端程式的主線程則主要包括監聽的被用來接受用戶端請求的socket。其每次接收一個來自用戶端的請求,并把新建立的用于和用戶端通信的socket傳給另外一個線程與用戶端進行互動。因為每個線程隻與一個用戶端進行通訊,某一個線程出現阻塞不會影響到其他線程執行的任務。
使用多線程和阻塞socket進行網絡程式設計寫出的代碼更加直覺、簡單,但是有不少缺點。多線程需要考慮到作業系統關于資源共享的常見問題,并且這種程式設計方式對單核CPU很不友好。
The C10K Problem讨論了若幹種處理并發連接配接的方法,比如使用異步socket。