了解了IO多路復(fù)用矢渊,我們就來看看python中是如何使用的继准。Python中有一個select模塊,其中提供了:select矮男、poll移必、epoll三個方法,分別調(diào)用系統(tǒng)的 select毡鉴,poll崔泵,epoll 從而實現(xiàn)IO多路復(fù)用。(下面以select方法為例)
Windows Python:
提供: select
Mac Python:
提供: select
Linux Python:
提供: select猪瞬、poll憎瘸、epoll
注意:網(wǎng)絡(luò)操作、文件操作陈瘦、終端操作等均屬于IO操作幌甘,對于windows只支持Socket操作,其他系統(tǒng)支持其他IO操作痊项,但是無法檢測 普通文件操作 自動上次讀取是否已經(jīng)變化锅风。
對于select方法:
句柄列表11, 句柄列表22, 句柄列表33=select.select(句柄序列1, 句柄序列2, 句柄序列3, 超時時間)
參數(shù): 可接受四個參數(shù)(前三個必須)
返回值:三個列表
select方法用來監(jiān)視文件句柄,如果句柄發(fā)生變化鞍泉,則獲取該句柄皱埠。
1、當(dāng) 參數(shù)1序列中的句柄發(fā)生可讀時(accetp和read)咖驮,則獲取發(fā)生變化的句柄并添加到 返回值1序列中
2边器、當(dāng) 參數(shù)2序列中含有句柄時,則將該序列中所有的句柄添加到 返回值2序列中
3托修、當(dāng) 參數(shù)3序列中的句柄發(fā)生錯誤時忘巧,則將該發(fā)生錯誤的句柄添加到 返回值3序列中
4、當(dāng) 超時時間 未設(shè)置诀黍,則select會一直阻塞袋坑,直到監(jiān)聽的句柄發(fā)生變化(時間設(shè)置)
當(dāng) 超時時間 =1時,那么如果監(jiān)聽的句柄均無任何變化眯勾,則select會阻塞1秒枣宫,之后返回三個空列表,如果監(jiān)聽的句柄有變化吃环,則直接執(zhí)行也颤。
利用select實現(xiàn)偽同時處理多個Socket客戶端請求:服務(wù)端
!/usr/bin/python
-- coding: utf-8 --
"""
@version: ??
@author: Guanjie Zhou
@license: Apache Licence
@file: server.py
@time: 17-5-25 下午9:50
"""
'''
服務(wù)器的實現(xiàn) 采用select的方式
'''
import select
import socket
import sys
import Queue
創(chuàng)建套接字并設(shè)置該套接字為非阻塞模式
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
綁定套接字
server_address = ('localhost', 10000)
server.bind(server_address)
將該socket變成服務(wù)模式
backlog等于5,表示內(nèi)核已經(jīng)接到了連接請求郁轻,但服務(wù)器還沒有調(diào)用accept進行處理的連接個數(shù)最大為5
這個值不能無限大翅娶,因為要在內(nèi)核中維護連接隊列
server.listen(5)
初始化讀取數(shù)據(jù)的監(jiān)聽列表,最開始時希望從server這個套接字上讀取數(shù)據(jù)
inputs = [server]
初始化寫入數(shù)據(jù)的監(jiān)聽列表,最開始并沒有客戶端連接進來好唯,所以列表為空
outputs = []
要發(fā)往客戶端的數(shù)據(jù)
message_queues = {}
while inputs:
print('waiting for the next event')
# 調(diào)用select監(jiān)聽所有監(jiān)聽列表中的套接字竭沫,并將準(zhǔn)備好的套接字加入到對應(yīng)的列表中
readable, writable, exceptional = select.select(inputs, outputs, inputs) # 列表中的socket 套接字 如果是文件呢?
# 監(jiān)控文件句柄有某一處發(fā)生了變化 可寫 可讀 異常屬于Linux中的網(wǎng)絡(luò)編程
# 屬于同步I/O操作骑篙,屬于I/O復(fù)用模型的一種
# rlist--等待到準(zhǔn)備好讀
# wlist--等待到準(zhǔn)備好寫
# xlist--等待到一種異常
# 處理可讀取的套接字
'''
如果server這個套接字可讀蜕提,則說明有新鏈接到來
此時在server套接字上調(diào)用accept,生成一個與客戶端通訊的套接字
并將與客戶端通訊的套接字加入inputs列表,下一次可以通過select檢查連接是否可讀
然后在發(fā)往客戶端的緩沖中加入一項靶端,鍵名為:與客戶端通訊的套接字谎势,鍵值為空隊列
select系統(tǒng)調(diào)用是用來讓我們的程序監(jiān)視多個文件句柄(file descrīptor)的狀態(tài)變化的。程序會停在select這里等待杨名,
直到被監(jiān)視的文件句柄有某一個或多個發(fā)生了狀態(tài)改變
'''
'''
若可讀的套接字不是server套接字,有兩種情況:一種是有數(shù)據(jù)到來脏榆,另一種是鏈接斷開
如果有數(shù)據(jù)到來,先接收數(shù)據(jù),然后將收到的數(shù)據(jù)填入往客戶端的緩存區(qū)中的對應(yīng)位置,最后
將于客戶端通訊的套接字加入到寫數(shù)據(jù)的監(jiān)聽列表:
如果套接字可讀.但沒有接收到數(shù)據(jù)台谍,則說明客戶端已經(jīng)斷開须喂。這時需要關(guān)閉與客戶端連接的套接字
進行資源清理
'''
print ('111')
for s in readable:
if s is server:
connection, client_address = s.accept()
print('connection from', client_address)
connection.setblocking(0) # 設(shè)置非阻塞
inputs.append(connection)
# 將新連接的套接字鏈接通話建立到一個隊列中去
message_queues[connection] = Queue.Queue()
else:
data = s.recv(1024).decode()
if data:
print('received "%s" from %s' % \
(data, s.getpeername()))
message_queues[s].put(data)
if s not in outputs:
# 不存在outputs列表中的,加入到outputs中
outputs.append(s)
else:
print('closing', client_address)
if s in outputs:
outputs.remove(s)
inputs.remove(s)
s.close()
del message_queues[s]
# 處理可寫的套接字
'''
在發(fā)送緩沖區(qū)中取出響應(yīng)的數(shù)據(jù)趁蕊,發(fā)往客戶端镊折。
如果沒有數(shù)據(jù)需要寫,則將套接字從發(fā)送隊列中移除介衔,select中不再監(jiān)視
'''
for s in writable:
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
print(' ', s.getpeername(), 'queue empty')
outputs.remove(s)
else:
print('sending "%s" to %s' % \
(next_msg, s.getpeername()))
s.send(next_msg)
# 處理異常情況
for s in exceptional:
for s in exceptional:
print('exception condition on', s.getpeername())
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
利用select監(jiān)聽終端操作實例
!/usr/bin/python
-- coding: utf-8 --
"""
@version: ??
@author: Guanjie Zhou
@license: Apache Licence
@file: client.py
@time: 17-5-26 下午1:11
"""
import socket
ip_port= ('127.0.0.1',10000)
sk = socket.socket()
sk.connect(ip_port)
while True:
inp = raw_input("please input:").encode()
sk.sendall(inp)
data = sk.recv(1024).decode()
print data+'hello'
sk.close()