python学习【十】 IO多路复用

  在学习Socket的时候,已经知道server端同一时间仅能处理一个client请求。在有第二个连接过来的时候,如果第一个请求在和服务器连接中,那么第二个只能等待第一个断开后第二个才能连接。这样的话,当server端在等待client端输入数据时,server就是空闲状态从而浪费时间,降低效率。为了解决这个问题,出现一种新的机制——IO多路复用。

IO多路复用

所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

看到上面这句解释,可能不了解网络的同学根本就不知道什么是I/O,它的工作原理是怎样的?这个呢,在网上有很多大牛已经做了详细介绍,我就不过的介绍。

这里推荐几篇我感觉不错的供大家参考:

http://blog.csdn.net/yfkiss/article/details/7516589

http://blog.chinaunix.net/uid-28458801-id-4464639.html

http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/

Python IO多路复用

Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。

下面是python在不同环境中可使用的方法:

Windows Python:
    提供: select
Mac Python:
    提供: select
Linux Python:
    提供: select、poll、epoll

注意:网络操作、文件操作、终端操作等均属于IO操作,对于windows只支持Socket操作,其他系统支持其他IO操作,但是无法检测 普通文件操作 自动上次读取是否已经变化。

这里主要介绍select方法的使用,详细内容如下:

句柄列表1, 句柄列表2, 句柄列表3 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
 
参数: 可接受四个参数(前三个必须)
返回值:三个列表
 
select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
   当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

利用select监听终端操作实例

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import select
import sys

while True:
    readable, writeable, error = select.select([sys.stdin,],[],[],1)
    '''select.select([sys.stdin,],[],[],1)用到I/O多路复用,第一个参数是列表,我放进去的是stdin就是我输入进去东西的描述符,
       相当于打开一个文件,和obj = socket(),类似的文件描述符,
       sys.stdin 他只是一个特殊的文件描述符= 终端的输入,一旦你输入OK select I/O多路复用他就感知到了。
       先看readable这个参数,其他的县不用看一旦你发生了我就他他发到readable里了,
       这里添加的就是修改的那个文件描述符,如果你一直没有修改过,那么readable他就是一个空的列表
    '''
    if sys.stdin in readable:
        print 'select get stdin',sys.stdin.readline()

'''
注:
1、[sys.stdin,]  以后不管是列表还是元组在最后的元素后面建议增加一个逗号,那元组举例(1,) | (1) 这两个有区别吗?是不是第二个
更像方法的调用或者函数的调用,加个,是不是更容易分清楚。还有就是在以后写django的配置文件的时候,他是必须要加的。写作习惯
2、select第一个参数他就是监听多个文件句柄,当谁改变了我是不是就可以监听到!
3、select参数里1是超时时间,当到slect那一行后,如果这里还是没有输入,那么我就继续走!
'''

利用select监听终端操作实例

#/usr/bin/env python
#-*- coding:utf-8 -*-
import time
import socket
import select
#创建socket对象
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt
#设置监听的IP与端口
sk.bind(('127.0.0.1',8888))
#设置client最大等待连接数
sk.listen(5)
sk.setblocking(False) #这里设置setblocking为Falseaccept将不在阻塞,但是如果没有收到请求就会报错
while True:
    readable_list, writeable_list, error_list = select.select([sk,],[],[],1)  #监听第一个列表的文件描述符,如果里面有文件描述符发生改变既能捕获并放到readable_list中
    for r in readable_list:    #如果是空列表将不执行,如果是空列表。将执行。
        conn,addr = r.accept()
        print addr
sk.close()

利用select监听多端口操作实例

/usr/bin/env python
#-*- coding:utf-8 -*-
import time
import socket
import select
#创建socket对象
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt
#设置监听的IP与端口
sk.bind(('127.0.0.1',6666))
#设置client最大等待连接数
sk.listen(5)
sk.setblocking(False) #这里设置setblocking为Falseaccept将不在阻塞,但是如果没有收到请求就会报错

sk1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk1.setsockopt
#设置监听的IP与端口
sk1.bind(('127.0.0.1',8888))
#设置client最大等待连接数
sk1.listen(5)
sk1.setblocking(False) #这里设置setblocking为Falseaccept将不在阻塞,但是如果没有收到请求就会报错


while True:
    readable_list, writeable_list, error_list = select.select([sk,sk1,],[],[],1)  #监听第一个列表的文件描述符,如果里面有文件描述符发生改变既能捕获并放到readable_list中
    for r in readable_list:    #如果是空列表将不执行,如果是空列表。将执行。
        conn,address = r.accept()
        print address
sk.close()
sk1.close()

其实,在SocketServer内部就是使用 IO多路复用 以及 “多线程” 和 “多进程” 实现并发处理多个客户端请求的Socket服务端。有兴趣的可以研究下SocketServer的源码实现。

posted @ 2016-01-05 21:43  杜瑞龙  阅读(118)  评论(0)    收藏  举报