Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。 可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。本人理解的解释:
第一阶段 在普通的socket编写的时候呢,是阻塞模式,也就是监听的时候进行阻塞监听。使用程序本身来监听有多少用户过来,检测用户是否有信息到达等等 第二阶段 使用多线程来处理,主线程负责创建socket进行实例化,线程二负责阻塞链接,一旦有链接就将此次链接的文件描述符存储起来,调用的时候获取到 然后send信息 线程三负责阻塞监听是否有信息到达,并做相应的处理 上面一些操作属于IO操作 下面的就利用到一些名词了... IO多路复用(我用白话讲吧,IO多路复用就是利用自己程序的IO同时再用系统自己的IO,俗称IO多路复用,如果我解释的有错欢迎指点,我及时修改) 第三阶段 使用IO多路复用,根据我上面的解释就是利用系统去检测当前文件描述符(Linux一切皆文件)(白话就是socket当前对象),将这个文件描述符交给操作系统而不是你自己的程序进行监听,让程序去处理,如果检测的能读或者能写,系统来通知你(程序)来读或者写。 第四阶段 使用信号量(本人暂未使用)注释:(我喜欢用白话去说,不喜欢用专业的修饰词,因为感觉说出口模棱两可,具体理解的对或者不对也不知道,专业的修饰词说出口好像是你会了,实际呢?谁知道?当然白话自己解释也会出现理解问题,如果出错请大家即使指正) 专业解释
我觉得解释的还不错,请点击查看
自己代码实现引用包
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
实例化对象
epoll_obj = DefaultSelector()
实例化SOCKET对象
SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 再次对socket对象进行设置,这个是端口复用
SOCKET.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 监听的端口
self.SOCKET.bind(("0.0.0.0" 9999))
# 最大承受量(单次)
SOCKET.listen(100)
# 是否阻塞模式
SOCKET.setblocking(False)
# 查看文件描述符 ()
log.info(str(SOCKET))
# 启动个线程whlie True 去检测用户链接
thread = Thread(target=initial_accept_message, kwargs={"socket": self.SOCKET})
thread.setDaemon(True)
thread.setName("辅助接受用户线程")
thread.start()
# 启动个线程whlie True 去轮训select回调函数
thread = Thread(target=self.loop)
thread.setDaemon(True)
thread.setName("辅助管理线程")
thread.start()
检测用户链接
def initial_accept_message(self, *args, **kwargs):
#获取到socket对象
s = kwargs.get("socket")
while True:
# 阻塞循环接受监听
conn, address = s.accept()
# 打印监听到的文件描述
log.info('accepted%s,from%s,cnn%s' % (str(conn), str(address), str(conn)))
# 是否阻塞模式
conn.setblocking(False)
# 开始注册回调函数
self.epoll_obj.register(conn, EVENT_WRITE,link_write_bank)
当可写的时候
def link_write_bank(self, sock, mask):
# 发送信息
sock.send(bytes.fromhex("我爱中华"))
# 取消当前socket(并不是刚刚实例化的宏,这个socket是刚刚链接进来的那个对象)
self.epoll_obj.unregister(sock)
# 再次注册可读
self.epoll_obj.register(sock, EVENT_READ, self.accept_bankId_host)
可读
def accept_host(self, sock, mask):
# 通过对象读取信息
res = sock.recv(1024)
# 对方地址 __str__
conn = sock.accept
。。。。。后续操作
辅助管理线程
def loop(self):
# 死循环
while True:
# 实例化当前的epool_obj中的阻塞监听事件对象
events = self.epoll_obj.select()
# 循环监听对象
for key, mask in events:
# 拿取回调函数
callback = key.data
# 启动回调函数,输入当前的socket对象
callback(key.fileobj, mask)
最后的解释 DefaultSelector
Windows使用自动调取select Linux 调取epool 下周解释epool



