Python利用选择器模块实现非阻塞式编程?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
前面介绍的插座都是采用阻塞方式进行通信的,当程序调用recv()方法从套接字中读取数据时,如果没有读取到有效的数据,当前线程就会被阻塞。为了解决这个问题,上面程序采用了多线程并发编程,即服务器端为每个客户端连接都启动一个单独的线程,不同的线程负责对应的套接字的通信工作。
通过选择器模块允许插座以非阻塞方式进行通信,选择器相当于一个事件注册中心,程序只要将套接字的所有事件注册给选择器管理,当选择器检测到套接字中的特定事件之后,程序就调用相应的监听方法进行处理。
选择器主要支持两种事件:
选择器。EVENT_READ:当插座有数据可读时触发该事件。当有客户端连接进来时也会触发该事件。
选择器。EVENT_WRITE:当插座将要写数据时触发该事件。
使用选择器实现非阻塞式编程的步骤大致如下:
创建选择器对象。
通过选择器对象为套接字的选择器。EVENT_READ或选择器。EVENT_WRITE事件注册监听器函数。每当插座有数据需要读写时,系统负责触发所注册的监昕器函数。
在监听器函数中处理插座通信。
下面程序使用选择器模块实现非阻塞式通信的服务器端:
import 选择器,插座 #,创建默认的选择器对象 时间=sel selectors.DefaultSelector () #,负责监听”有数据可读”事件的函数 def 阅读(sk电讯,面具): ,,,试一试: ,,,,,,,#,读取数据 ,,,,,,,data =, skt.recv (1024) ,,,,,,,if 数据: ,,,,,,,,,,,#,将读取的数据采用循环向每个插座发送一次 ,,,,,,,,,,,for s 拷贝socket_list: ,,,,,,,,,,,,,,,s.send(数据),,#,Hope it 赢得# 39;t 块 ,,,,,,,其他的: ,,,,,,,,,,,#,如果该套接字已被对方关闭,关闭该套接字, ,,,,,,,,,,,#,并从socket_list列表中删除 ,,,,,,,,,,,印刷(& # 39;关闭& # 39;,,sk电讯) ,,,,,,,,,,,sel.unregister (sk电讯) ,,,,,,,,,,,skt.close () ,,,,,,,,,,,socket_list.remove (sk电讯) ,,,#,如果捕捉到异常,,将该套接字关闭,并从socket_list列表中删除 ,,,除了: ,,,,,,,印刷(& # 39;关闭& # 39;,,sk电讯) ,,,,,,,sel.unregister (sk电讯) ,,,,,,,skt.close () ,,,,,,,socket_list.remove (sk电讯) 时间=socket_list [] #,负责监听“客户端连接进来“事件的函数 def 接受(袜子,面具): 康涅狄格州,,,,,addr =, sock.accept () ,,,#,使用socket_list保存代表客户端的插座 ,,,socket_list.append(康涅狄格州) ,,,conn.setblocking(假) ,,,#,使用选取为康涅狄格州的EVENT_READ事件注册读监听函数 ,,,sel.register(康涅狄格州,selectors.EVENT_READ,,读),,,,#② 时间=sock socket.socket () sock.bind ((& # 39; 192.168.1.88& # 39;,, 30000)) sock.listen () #,设置该套接字是非阻塞的 sock.setblocking(假) #,使用选取为袜子的EVENT_READ事件注册接受监听函数 sel.register(袜子,selectors.EVENT_READ,接受),,,,#① #,采用死循环不断提取选取的事件 while 真正的: ,,,events =, sel.select () ,,,for 关键,,mask 拷贝事件: ,,,,,,,#,关键的数据属性获取为该事件注册的监听函数 ,,,,,,,callback =key.data ,,,,,,,#,调用监听函数,,关键的fileobj属性获取被监听的套接字对象 ,,,,,,,回调(key.fileobj,面具)
上面程序中定义了两个监听器函数接受读()和(),其中接受()函数作为“有客户端连接进来“事件的监听函数,主程序中的①号代码负责为套接字的选择器。EVENT_READ事件注册该函数;阅读()函数则作为“有数据可读”事件的监听函数,如接受()函数中的②号代码所示。
通过上面这种方式,程序避免了采用死循环不断地调用插座的接受()方法来接受客户端连接,也避免了采用死循环不断地调用插座的recv()方法来接收数据.socket的接受(),recv()方法调用都是写在事件监听函数中的,只有当事件(如“有客户端连接进来“事件,“有数据可读”事件)发生时,接受()和recv()方法才会被调用,这样就避免了阻塞式编程。
为了不断地提取选择器中的事件,程序最后使用一个死循环不断地调用选择器的选择()方法”监测”事件,每当监测到相应的事件之后,程序就会调用对应的事件监听函数。