欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python?IO多路復(fù)用之epoll詳解

 更新時(shí)間:2022年08月19日 09:27:37   作者:Alex_996  
這篇文章主要為大家詳細(xì)介紹了python?IO多路復(fù)用之epoll,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

什么是epoll

epoll是什么?在linux的網(wǎng)絡(luò)編程中,很長(zhǎng)的時(shí)間都在使用select來(lái)做事件觸發(fā)。在linux新的內(nèi)核中,有了一種替換它的機(jī)制,就是epoll。當(dāng)然,這不是2.6內(nèi)核才有的,它是在2.5.44內(nèi)核中被引進(jìn)的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它幾乎具備了之前所說(shuō)的一切優(yōu)點(diǎn),被公認(rèn)為L(zhǎng)inux2.6下性能最好的多路復(fù)用I/O就緒通知方法。

相比于select,epoll最大的好處在于它不會(huì)隨著監(jiān)聽(tīng)fd數(shù)目的增長(zhǎng)而降低效率。因?yàn)樵趦?nèi)核中的select實(shí)現(xiàn)中,它是采用輪詢來(lái)處理的,輪詢的fd數(shù)目越多,自然耗時(shí)越多。

epoll工作原理

epoll同樣只告知那些就緒的文件描述符,而且當(dāng)我們調(diào)用epoll_wait()獲得就緒文件描述符時(shí),返回的不是實(shí)際的描述符,而是一個(gè)代表就緒描述符數(shù)量的值,你只需要去epoll指定的一個(gè)數(shù)組中依次取得相應(yīng)數(shù)量的文件描述符即可,這里也使用了內(nèi)存映射(mmap)技術(shù),這樣便徹底省掉了這些文件描述符在系統(tǒng)調(diào)用時(shí)復(fù)制的開(kāi)銷(xiāo)。

另一個(gè)本質(zhì)的改進(jìn)在于epoll采用基于事件的就緒通知方式。在select/poll中,進(jìn)程只有在調(diào)用一定的方法后,內(nèi)核才對(duì)所有監(jiān)視的文件描述符進(jìn)行掃描,而epoll事先通過(guò)epoll_ctl()來(lái)注冊(cè)一個(gè)文件描述符,一旦基于某個(gè)文件描述符就緒時(shí),內(nèi)核會(huì)采用類(lèi)似callback的回調(diào)機(jī)制,迅速激活這個(gè)文件描述符,當(dāng)進(jìn)程調(diào)用epoll_wait()時(shí)便得到通知。

從以上可知,epoll是對(duì)select、poll模型的改進(jìn),提高了網(wǎng)絡(luò)編程的性能,廣泛應(yīng)用于大規(guī)模并發(fā)請(qǐng)求的C/S架構(gòu)中。

python中的epoll

1、觸發(fā)方式:

邊緣觸發(fā)/水平觸發(fā),只適用于Unix/Linux操作系統(tǒng)

2、原理圖

3、一般步驟

Create an epoll object——創(chuàng)建1個(gè)epoll對(duì)象

Tell the epoll object to monitor specific events on specific sockets——告訴epoll對(duì)象,在指定的socket上監(jiān)聽(tīng)指定的事件

Ask the epoll object which sockets may have had the specified event since the last query——詢問(wèn)epoll對(duì)象,從上次查詢以來(lái),哪些socket發(fā)生了哪些指定的事件

Perform some action on those sockets——在這些socket上執(zhí)行一些操作

Tell the epoll object to modify the list of sockets and/or events to monitor——告訴epoll對(duì)象,修改socket列表和(或)事件,并監(jiān)控

Repeat steps 3 through 5 until finished——重復(fù)步驟3-5,直到完成

Destroy the epoll object——銷(xiāo)毀epoll對(duì)象

4、相關(guān)用法

import select 導(dǎo)入select模塊

epoll = select.epoll()創(chuàng)建一個(gè)epoll對(duì)象

epoll.register(文件句柄,事件類(lèi)型)注冊(cè)要監(jiān)控的文件句柄和事件

事件類(lèi)型:

select.EPOLLIN 可讀事件

select.EPOLLOUT 可寫(xiě)事件

select.EPOLLERR 錯(cuò)誤事件

select.EPOLLHUP 客戶端斷開(kāi)事件

epoll.unregister(文件句柄) 銷(xiāo)毀文件句柄

epoll.poll(timeout) 當(dāng)文件句柄發(fā)生變化,則會(huì)以列表的形式主動(dòng)報(bào)告給用戶進(jìn)程,timeout

為超時(shí)時(shí)間,默認(rèn)為-1,即一直等待直到文件句柄發(fā)生變化,如果指定為1

那么epoll每1秒?yún)R報(bào)一次當(dāng)前文件句柄的變化情況,如果無(wú)變化則返回空

epoll.fileno() 返回epoll的控制文件描述符(Return the epoll control file descriptor)

epoll.modfiy(fineno,event)fineno為文件描述符 event為事件類(lèi)型 作用是修改文件描述符所對(duì)應(yīng)的事件

epoll.fromfd(fileno)從1個(gè)指定的文件描述符創(chuàng)建1個(gè)epoll對(duì)象

epoll.close() 關(guān)閉epoll對(duì)象的控制文件描述符

5 實(shí)例:客戶端發(fā)送數(shù)據(jù) 服務(wù)端將接收的數(shù)據(jù)返回給客戶端

服務(wù)端代碼

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
import select
import Queue
#創(chuàng)建socket對(duì)象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#設(shè)置IP地址復(fù)用
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#ip地址和端口號(hào)
server_address = ("127.0.0.1", 8888)
#綁定IP地址
serversocket.bind(server_address)
#監(jiān)聽(tīng),并設(shè)置最大連接數(shù)
serversocket.listen(10)
print ?"服務(wù)器啟動(dòng)成功,監(jiān)聽(tīng)I(yíng)P:" , server_address
#服務(wù)端設(shè)置非阻塞
serversocket.setblocking(False) ?
#超時(shí)時(shí)間
timeout = 10
#創(chuàng)建epoll事件對(duì)象,后續(xù)要監(jiān)控的事件添加到其中
epoll = select.epoll()
#注冊(cè)服務(wù)器監(jiān)聽(tīng)fd到等待讀事件集合
epoll.register(serversocket.fileno(), select.EPOLLIN)
#保存連接客戶端消息的字典,格式為{}
message_queues = {}
#文件句柄到所對(duì)應(yīng)對(duì)象的字典,格式為{句柄:對(duì)象}
fd_to_socket = {serversocket.fileno():serversocket,}
while True:
? print "等待活動(dòng)連接......"
? #輪詢注冊(cè)的事件集合,返回值為[(文件句柄,對(duì)應(yīng)的事件),(...),....]
? events = epoll.poll(timeout)
? if not events:
? ? ?print "epoll超時(shí)無(wú)活動(dòng)連接,重新輪詢......"
? ? ?continue
? print "有" , len(events), "個(gè)新事件,開(kāi)始處理......"
? ?
? for fd, event in events:
? ? ?socket = fd_to_socket[fd]
? ? ?#如果活動(dòng)socket為當(dāng)前服務(wù)器socket,表示有新連接
? ? ?if socket == serversocket:
? ? ? ? ? ? connection, address = serversocket.accept()
? ? ? ? ? ? print "新連接:" , address
? ? ? ? ? ? #新連接socket設(shè)置為非阻塞
? ? ? ? ? ? connection.setblocking(False)
? ? ? ? ? ? #注冊(cè)新連接fd到待讀事件集合
? ? ? ? ? ? epoll.register(connection.fileno(), select.EPOLLIN)
? ? ? ? ? ? #把新連接的文件句柄以及對(duì)象保存到字典
? ? ? ? ? ? fd_to_socket[connection.fileno()] = connection
? ? ? ? ? ? #以新連接的對(duì)象為鍵值,值存儲(chǔ)在隊(duì)列中,保存每個(gè)連接的信息
? ? ? ? ? ? message_queues[connection] ?= Queue.Queue()
? ? ?#關(guān)閉事件
? ? ?elif event & select.EPOLLHUP:
? ? ? ? print 'client close'
? ? ? ? #在epoll中注銷(xiāo)客戶端的文件句柄
? ? ? ? epoll.unregister(fd)
? ? ? ? #關(guān)閉客戶端的文件句柄
? ? ? ? fd_to_socket[fd].close()
? ? ? ? #在字典中刪除與已關(guān)閉客戶端相關(guān)的信息
? ? ? ? del fd_to_socket[fd]
? ? ?#可讀事件
? ? ?elif event & select.EPOLLIN:
? ? ? ? #接收數(shù)據(jù)
? ? ? ? data = socket.recv(1024)
? ? ? ? if data:
? ? ? ? ? ?print "收到數(shù)據(jù):" , data , "客戶端:" , socket.getpeername()
? ? ? ? ? ?#將數(shù)據(jù)放入對(duì)應(yīng)客戶端的字典
? ? ? ? ? ?message_queues[socket].put(data)
? ? ? ? ? ?#修改讀取到消息的連接到等待寫(xiě)事件集合(即對(duì)應(yīng)客戶端收到消息后,再將其fd修改并加入寫(xiě)事件集合)
? ? ? ? ? ?epoll.modify(fd, select.EPOLLOUT)
? ? ?#可寫(xiě)事件
? ? ?elif event & select.EPOLLOUT:
? ? ? ? try:
? ? ? ? ? ?#從字典中獲取對(duì)應(yīng)客戶端的信息
? ? ? ? ? ?msg = message_queues[socket].get_nowait()
? ? ? ? except Queue.Empty:
? ? ? ? ? ?print socket.getpeername() , " queue empty"
? ? ? ? ? ?#修改文件句柄為讀事件
? ? ? ? ? ?epoll.modify(fd, select.EPOLLIN)
? ? ? ? else :
? ? ? ? ? ?print "發(fā)送數(shù)據(jù):" , data , "客戶端:" , socket.getpeername()
? ? ? ? ? ?#發(fā)送數(shù)據(jù)
? ? ? ? ? ?socket.send(msg)
#在epoll中注銷(xiāo)服務(wù)端文件句柄
epoll.unregister(serversocket.fileno())
#關(guān)閉epoll
epoll.close()
#關(guān)閉服務(wù)器socket
serversocket.close()

客戶端代碼:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
#創(chuàng)建客戶端socket對(duì)象
clientsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#服務(wù)端IP地址和端口號(hào)元組
server_address = ('127.0.0.1',8888)
#客戶端連接指定的IP地址和端口號(hào)
clientsocket.connect(server_address)
while True:
? ? #輸入數(shù)據(jù)
? ? data = raw_input('please input:')
? ? #客戶端發(fā)送數(shù)據(jù)
? ? clientsocket.sendall(data)
? ? #客戶端接收數(shù)據(jù)
? ? server_data = clientsocket.recv(1024)
? ? print '客戶端收到的數(shù)據(jù):'server_data
? ? #關(guān)閉客戶端socket
? ? clientsocket.close()

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論