網站首頁 編程語言 正文
什么是epoll
epoll是什么?在linux的網絡編程中,很長的時間都在使用select來做事件觸發。在linux新的內核中,有了一種替換它的機制,就是epoll。當然,這不是2.6內核才有的,它是在2.5.44內核中被引進的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它幾乎具備了之前所說的一切優點,被公認為Linux2.6下性能最好的多路復用I/O就緒通知方法。
相比于select,epoll最大的好處在于它不會隨著監聽fd數目的增長而降低效率。因為在內核中的select實現中,它是采用輪詢來處理的,輪詢的fd數目越多,自然耗時越多。
epoll工作原理
epoll同樣只告知那些就緒的文件描述符,而且當我們調用epoll_wait()獲得就緒文件描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的文件描述符即可,這里也使用了內存映射(mmap)技術,這樣便徹底省掉了這些文件描述符在系統調用時復制的開銷。
另一個本質的改進在于epoll采用基于事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法后,內核才對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來注冊一個文件描述符,一旦基于某個文件描述符就緒時,內核會采用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便得到通知。
從以上可知,epoll是對select、poll模型的改進,提高了網絡編程的性能,廣泛應用于大規模并發請求的C/S架構中。
python中的epoll
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對象的控制文件描述符
5 實例:客戶端發送數據 服務端將接收的數據返回給客戶端
服務端代碼
#!/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()
客戶端代碼:
#!/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()
原文鏈接:https://blog.csdn.net/weixin_43336281/article/details/106035148
相關推薦
- 2023-03-22 tkinter如何實現打開文件對話框并獲取文件絕對路徑_python
- 2022-10-27 Kotlin協程之Flow基礎原理示例解析_Android
- 2022-03-08 使用C語言實現本地socke通訊的方法_C 語言
- 2022-11-03 C語言編寫掃雷小程序_C 語言
- 2022-05-13 FAILED: Execution Error, return code 1 from org.ap
- 2022-12-05 Python最長回文子串問題_python
- 2022-06-25 Docker核心組件之聯合文件系統詳解_docker
- 2022-04-01 K8s產生ERROR的解決方法
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支