python 網路程式設計之epoll使用
從以上可知,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()
服務端程式碼