epoll是對(duì)select状勤、poll模型的改進(jìn)致燥,提高了網(wǎng)絡(luò)編程的性能莲祸,廣泛應(yīng)用于大規(guī)模并發(fā)請(qǐng)求的C/S架構(gòu)中酪穿。
原理圖
一般步驟
- Create an epoll object——?jiǎng)?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——詢(xún)問(wèn)epoll對(duì)象辫红,從上次查詢(xún)以來(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ì)象
相關(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 客戶(hù)端斷開(kāi)事件
epoll.unregister //(文件句柄) 銷(xiāo)毀文件句柄
epoll.poll(timeout) //當(dāng)文件句柄發(fā)生變化较幌,則會(huì)以列表的形式主動(dòng)報(bào)告給用戶(hù)進(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ì)象的控制文件描述符
實(shí)例:客戶(hù)端發(fā)送數(shù)據(jù) 服務(wù)端將接收的數(shù)據(jù)返回給客戶(hù)端
服務(wù)端代碼
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3
4 import socket
5 import select
6 import Queue
7
8 #創(chuàng)建socket對(duì)象
9 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
10 #設(shè)置IP地址復(fù)用
11 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
12 #ip地址和端口號(hào)
13 server_address = ("127.0.0.1", 8888)
14 #綁定IP地址
15 serversocket.bind(server_address)
16 #監(jiān)聽(tīng),并設(shè)置最大連接數(shù)
17 serversocket.listen(10)
18 print "服務(wù)器啟動(dòng)成功岛琼,監(jiān)聽(tīng)I(yíng)P:" , server_address
19 #服務(wù)端設(shè)置非阻塞
20 serversocket.setblocking(False)
21 #超時(shí)時(shí)間
22 timeout = 10
23 #創(chuàng)建epoll事件對(duì)象底循,后續(xù)要監(jiān)控的事件添加到其中
24 epoll = select.epoll()
25 #注冊(cè)服務(wù)器監(jiān)聽(tīng)fd到等待讀事件集合
26 epoll.register(serversocket.fileno(), select.EPOLLIN)
27 #保存連接客戶(hù)端消息的字典,格式為{}
28 message_queues = {}
29 #文件句柄到所對(duì)應(yīng)對(duì)象的字典槐瑞,格式為{句柄:對(duì)象}
30 fd_to_socket = {serversocket.fileno():serversocket,}
31
32 while True:
33 print "等待活動(dòng)連接......"
34 #輪詢(xún)注冊(cè)的事件集合熙涤,返回值為[(文件句柄,對(duì)應(yīng)的事件),(...),....]
35 events = epoll.poll(timeout)
36 if not events:
37 print "epoll超時(shí)無(wú)活動(dòng)連接祠挫,重新輪詢(xún)......"
38 continue
39 print "有" , len(events), "個(gè)新事件那槽,開(kāi)始處理......"
40
41 for fd, event in events:
42 socket = fd_to_socket[fd]
43 #如果活動(dòng)socket為當(dāng)前服務(wù)器socket,表示有新連接
44 if socket == serversocket:
45 connection, address = serversocket.accept()
46 print "新連接:" , address
47 #新連接socket設(shè)置為非阻塞
48 connection.setblocking(False)
49 #注冊(cè)新連接fd到待讀事件集合
50 epoll.register(connection.fileno(), select.EPOLLIN)
51 #把新連接的文件句柄以及對(duì)象保存到字典
52 fd_to_socket[connection.fileno()] = connection
53 #以新連接的對(duì)象為鍵值等舔,值存儲(chǔ)在隊(duì)列中骚灸,保存每個(gè)連接的信息
54 message_queues[connection] = Queue.Queue()
55 #關(guān)閉事件
56 elif event & select.EPOLLHUP:
57 print 'client close'
58 #在epoll中注銷(xiāo)客戶(hù)端的文件句柄
59 epoll.unregister(fd)
60 #關(guān)閉客戶(hù)端的文件句柄
61 fd_to_socket[fd].close()
62 #在字典中刪除與已關(guān)閉客戶(hù)端相關(guān)的信息
63 del fd_to_socket[fd]
64 #可讀事件
65 elif event & select.EPOLLIN:
66 #接收數(shù)據(jù)
67 data = socket.recv(1024)
68 if data:
69 print "收到數(shù)據(jù):" , data , "客戶(hù)端:" , socket.getpeername()
70 #將數(shù)據(jù)放入對(duì)應(yīng)客戶(hù)端的字典
71 message_queues[socket].put(data)
72 #修改讀取到消息的連接到等待寫(xiě)事件集合(即對(duì)應(yīng)客戶(hù)端收到消息后,再將其fd修改并加入寫(xiě)事件集合)
73 epoll.modify(fd, select.EPOLLOUT)
74 #可寫(xiě)事件
75 elif event & select.EPOLLOUT:
76 try:
77 #從字典中獲取對(duì)應(yīng)客戶(hù)端的信息
78 msg = message_queues[socket].get_nowait()
79 except Queue.Empty:
80 print socket.getpeername() , " queue empty"
81 #修改文件句柄為讀事件
82 epoll.modify(fd, select.EPOLLIN)
83 else :
84 print "發(fā)送數(shù)據(jù):" , data , "客戶(hù)端:" , socket.getpeername()
85 #發(fā)送數(shù)據(jù)
86 socket.send(msg)
87
88 #在epoll中注銷(xiāo)服務(wù)端文件句柄
89 epoll.unregister(serversocket.fileno())
90 #關(guān)閉epoll
91 epoll.close()
92 #關(guān)閉服務(wù)器socket
93 serversocket.close()
客戶(hù)端代碼
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3
4 import socket
5
6 #創(chuàng)建客戶(hù)端socket對(duì)象
7 clientsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
8 #服務(wù)端IP地址和端口號(hào)元組
9 server_address = ('127.0.0.1',8888)
10 #客戶(hù)端連接指定的IP地址和端口號(hào)
11 clientsocket.connect(server_address)
12
13 while True:
14 #輸入數(shù)據(jù)
15 data = raw_input('please input:')
16 #客戶(hù)端發(fā)送數(shù)據(jù)
17 clientsocket.sendall(data)
18 #客戶(hù)端接收數(shù)據(jù)
19 server_data = clientsocket.recv(1024)
20 print '客戶(hù)端收到的數(shù)據(jù):'server_data
21 #關(guān)閉客戶(hù)端socket
22 clientsocket.close()