Python select -> select
一、select介绍-来自百度百科
select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读或可写。主要用于Socket通信当中。
总结:select主要用于socket通信当中,能监视我们需要的文件描述变化
二、非阻塞时I/O编程-I/O多路复用
这篇文章讲的很详细,就不复述了。
Linux IO模式及 select、poll、epoll详解
三、python中的select
# In python
select.select(rlist, wlist, xlist[, timeout])
# In C/C++
int select(int maxfdpl, fd_set * readset, fd_set *writeset, fd_set *exceptset, const struct timeval * tiomeout)
python的select相对与C省去了文件描述符的参数
select能够监听可读的套接字列表,可写的套接字列表和异常的套接字列表。监听的套接字发生变化时,比如客户端连接,收到客户端消息等都会触发select。timeout指定了监听的超时时间,默认为None,会一直阻塞到套接字事件发生。指定了具体的数字后,在指定的时间内没有套接字事件发生,select会直接返回。select会返回准备好的三个对象列表,是三个输入参数的子集。
下面是测试代码,服务端监听,收到客户端的消息后,发送同样的消息给客户端
服务端代码:
from __future__ import unicode_literals
import socket
import select
import Queue
import logging
logging.basicConfig(level=logging.DEBUG, format='(%(threadName)-4s) %(message)s %(asctime)s')
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
server_address = ('localhost', 8090)
server.bind(server_address)
server.listen(5)
inputs = [server]
outputs = []
message_queues = {}
while inputs:
logging.debug('waiting for the next event.')
rlist, wlist, xlist = select.select(inputs, outputs, inputs)
for r in rlist:
if r is server:
connection, client_address = server.accept()
logging.debug('connection from: {0}'.format(client_address))
# 客户端加入监听列表, 服务端收到客户端发送消息时,触发select
inputs.append(connection)
# 为连接的客户端单独创建一个消息队列,用来保存客户端发送的消息
message_queues[connection] = Queue.Queue()
else:
# 服务端收到客户端发送消息时,触发select
data = r.recv(1024)
if data != '':
logging.debug('received "{0}" from {1}'.format(data, r.getpeername()))
message_queues[r].put(data)
# 将需要进行回复操作的socket放入output,select监听发送
if r not in outputs:
outputs.append(r)
# 客户端断开
else:
logging.debug('closing "{0}"'.format(r.getpeername()))
if r in outputs:
outputs.remove(r)
if r in inputs:
inputs.remove(r)
if r in message_queues:
del message_queues[r]
for w in wlist:
try:
message_queue = message_queues.get(w)
send_data = ''
if message_queue is not None:
send_data = message_queue.get_nowait()
else:
logging.debug('closing "{0}"'.format(w.getpeername()))
except Queue.Empty:
# 没有收到客户端消息,从发送列表中除去
outputs.remove(w)
else:
logging.debug('send {0} to {1}'.format(send_data, w.getpeername()))
w.send(send_data)
for x in xlist:
logging.debug('exception on {0}'.format(x.getpeername()))
if x in inputs:
inputs.remove(x)
if x in outputs:
outputs.remove(x)
if x in message_queues:
del message_queues[x]
x.close()
客户端代码:
import socket
import time
sock = socket.socket()
dest = ('localhost', 8090)
sock.connect(dest)
if __name__ == "__main__":
count = 0
while count < 3:
sock.send('hello')
data = sock.recv(1024)
if data:
print(data)
time.sleep(1)
count += 1
sock.close()
以下是运行结果:
(MainThread) waiting for the next event. 2018-09-09 21:19:10,355
(MainThread) connection from: ('127.0.0.1', 12669) 2018-09-09 21:19:12,173
(MainThread) waiting for the next event. 2018-09-09 21:19:12,173
(MainThread) received "hello" from ('127.0.0.1', 12669) 2018-09-09 21:19:12,174
(MainThread) waiting for the next event. 2018-09-09 21:19:12,174
(MainThread) send hello to ('127.0.0.1', 12669) 2018-09-09 21:19:12,174
(MainThread) waiting for the next event. 2018-09-09 21:19:12,174
(MainThread) waiting for the next event. 2018-09-09 21:19:12,174
(MainThread) received "hello" from ('127.0.0.1', 12669) 2018-09-09 21:19:13,174
从运行结果可以看到,客户连与服端的连接、断开和互相传递消息都会触发select。
服务端发送hello
给客户触发了两次的原因,我还没找到,后面继续研究吧
参考网址
python select.select模块通信全过程详解
Linux IO模式及 select、poll、epoll详解