-------------------------------多路复用的服务器(select)-------------------------------
网络通信被Unix系统抽象为文件的读写,通常是一个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回改文件描述符。当遍历结束之后,如果仍然没有一个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。
缺点:
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.对socket进行扫描时是依次扫描的,即采用轮询的方法,效率较低。当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。
应用:
#import select:对应import包的引用操作
#readable,writeable,exceptinal=select.select(inputs,[],[]):使用select对套接字组成的列表进行遍历,过滤出对应的未堵塞的套接字
python代码示例:
1 #coding=utf-8 2 3 #引用对应的包 4 from socket import * 5 6 import select 7 8 import sys 9 10 #函数:main 11 def main(): 12 #创建套接字 13 serTcpSocket=socket(AF_INET,SOCK_STREAM) 14 15 #绑定端口和ip,sys.argv[1] 运行时传递的参数 16 serTcpSocket.bind(("",int(sys.argv[1]))) 17 18 #打开被动监听 19 serTcpSocket.listen(5) 20 21 print("-----服务器开启-----") 22 23 #创建列表,存储对应的套接字 24 inputs=[serTcpSocket] 25 26 #循环 27 while True: 28 #使用select进行对应套接字的处理过滤 29 readList,writeList,exceptList=select.select(inputs,[],[]) 30 31 #遍历readList列表进行操作 32 for sockItem in readList: 33 #如果为服务器套接字,进行accept()数据的接收 34 if sockItem==serTcpSocket: 35 #监听接受客户端传递过来的数据信息 36 newSocket,destAddr=sockItem.accept() 37 38 print("客户端(%s)以上线"%str(destAddr)) 39 40 #将用于与客户端通信的套接字进行存储 41 inputs.append(newSocket) 42 43 else: 44 #进行客户端发送过来的数据的接收 45 recvData=sockItem.recv(1024) 46 47 #进行判断,如果传递过来的数据不为空 48 if len(recvData)>0: 49 #进行客户端发送过来的数据的打印操作 50 print("客户端(%s):%s"%(str(destAddr),recvData)) 51 else: 52 #表示客户端下线 53 print("客户端(%s)以下线!"%str(destAddr)) 54 55 #进行对应该客户端套接字的关闭操作 56 sockItem.close() 57 58 #将该套接字从对应的列表中移除 59 inputs.remove(sockItem) 60 61 #程序入口 62 if __name__=="__main__": 63 main()