從以上可知,epoll是對select、poll模型的改進,提高了網絡程式設計的性能,廣泛應用于大規模并發請求的C/S架構中。
1、觸發方式:
邊緣觸發/水準觸發,隻适用于Unix/Linux作業系統
2、原理圖

3、一般步驟
- Create an epoll object——建立1個epoll對象
- Tell the epoll object to monitor specific events on specific sockets——告訴epoll對象,在指定的socket上監聽指定的事件
- Ask the epoll object which sockets may have had the specified event since the last query——詢問epoll對象,從上次查詢以來,哪些socket發生了哪些指定的事件
- Perform some action on those sockets——在這些socket上執行一些操作
- Tell the epoll object to modify the list of sockets and/or events to monitor——告訴epoll對象,修改socket清單和(或)事件,并監控
- Repeat steps 3 through 5 until finished——重複步驟3-5,直到完成
- Destroy the epoll object——銷毀epoll對象
4、相關用法
import select 導入select子產品 epoll = select.epoll() 建立一個epoll對象 epoll.register(檔案句柄,事件類型) 注冊要監控的檔案句柄和事件 事件類型: select.EPOLLIN 可讀事件 select.EPOLLOUT 可寫事件 select.EPOLLERR 錯誤事件 select.EPOLLHUP 用戶端斷開事件 epoll.unregister(檔案句柄) 銷毀檔案句柄 epoll.poll(timeout) 當檔案句柄發生變化,則會以清單的形式主動報告給使用者程序,timeout 為逾時時間,預設為-1,即一直等待直到檔案句柄發生變化,如果指定為1 那麼epoll每1秒彙報一次目前檔案句柄的變化情況,如果無變化則傳回空 epoll.fileno() 傳回epoll的控制檔案描述符(Return the epoll control file descriptor) epoll.modfiy(fineno,event) fineno為檔案描述符 event為事件類型 作用是修改檔案描述符所對應的事件 epoll.fromfd(fileno) 從1個指定的檔案描述符建立1個epoll對象 epoll.close() 關閉epoll對象的控制檔案描述符 |
示例如下:
client:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
#建立用戶端socket對象
clientsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#服務端IP位址和端口号元組
server_address = ('127.0.0.1',8888)
#用戶端連接配接指定的IP位址和端口号
clientsocket.connect(server_address)
while True:
#輸入資料
data = raw_input('please input:')
#用戶端發送資料
clientsocket.sendall(data)
#用戶端接收資料
server_data = clientsocket.recv(1024)
print '用戶端收到的資料:'server_data
#關閉用戶端socket
clientsocket.close()
伺服器端:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
import select
import Queue
#建立socket對象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#設定IP位址複用
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#ip位址和端口号
server_address = ("127.0.0.1", 8888)
#綁定IP位址
serversocket.bind(server_address)
#監聽,并設定最大連接配接數
serversocket.listen(10)
print "伺服器啟動成功,監聽IP:" , server_address
#服務端設定非阻塞
serversocket.setblocking(False)
#逾時時間
timeout = 10
#建立epoll事件對象,後續要監控的事件添加到其中
epoll = select.epoll()
#注冊伺服器監聽fd到等待讀事件集合
epoll.register(serversocket.fileno(), select.EPOLLIN)
#儲存連接配接用戶端消息的字典,格式為{}
message_queues = {}
#檔案句柄到所對應對象的字典,格式為{句柄:對象}
fd_to_socket = {serversocket.fileno():serversocket,}
while True:
print "等待活動連接配接......"
#輪詢注冊的事件集合,傳回值為[(檔案句柄,對應的事件),(...),....]
events = epoll.poll(timeout)
if not events:
print "epoll逾時無活動連接配接,重新輪詢......"
continue
print "有" , len(events), "個新事件,開始處理......"
for fd, event in events:
socket = fd_to_socket[fd]
#如果活動socket為目前伺服器socket,表示有新連接配接
if socket == serversocket:
connection, address = serversocket.accept()
print "新連接配接:" , address
#新連接配接socket設定為非阻塞
connection.setblocking(False)
#注冊新連接配接fd到待讀事件集合
epoll.register(connection.fileno(), select.EPOLLIN)
#把新連接配接的檔案句柄以及對象儲存到字典
fd_to_socket[connection.fileno()] = connection
#以新連接配接的對象為鍵值,值存儲在隊列中,儲存每個連接配接的資訊
message_queues[connection] = Queue.Queue()
#關閉事件
elif event & select.EPOLLHUP:
print 'client close'
#在epoll中登出用戶端的檔案句柄
epoll.unregister(fd)
#關閉用戶端的檔案句柄
fd_to_socket[fd].close()
#在字典中删除與已關閉用戶端相關的資訊
del fd_to_socket[fd]
#可讀事件
elif event & select.EPOLLIN:
#接收資料
data = socket.recv(1024)
if data:
print "收到資料:" , data , "用戶端:" , socket.getpeername()
#将資料放入對應用戶端的字典
message_queues[socket].put(data)
#修改讀取到消息的連接配接到等待寫事件集合(即對應用戶端收到消息後,再将其fd修改并加入寫事件集合)
epoll.modify(fd, select.EPOLLOUT)
#可寫事件
elif event & select.EPOLLOUT:
try:
#從字典中擷取對應用戶端的資訊
msg = message_queues[socket].get_nowait()
except Queue.Empty:
print socket.getpeername() , " queue empty"
#修改檔案句柄為讀事件
epoll.modify(fd, select.EPOLLIN)
else :
print "發送資料:" , data , "用戶端:" , socket.getpeername()
#發送資料
socket.send(msg)
#在epoll中登出服務端檔案句柄
epoll.unregister(serversocket.fileno())
#關閉epoll
epoll.close()
#關閉伺服器socket
serversocket.close()
服務端代碼