用select完成了对事件的监听,由它来监听关注的事件,但凡监控的某一个socket上面事件发生后,select就会结束阻塞,我们就会遍历拿到的这个列表,就可以找到发生的指定对象都是谁。
如果要做操作,就可以在主从的时候传入data(类,对象,方法都可以)
注册的时候,返回一个selectkey,里面包含4个信息
fileobj 关注的socket文件对象
fd 文件描述符
events 关注哪些事件
data 传入数据
只能关注读和写
官方建议选择非阻塞IO
现在,accept和read的时候定义要一致
定义的参数只有一个
这样定义就只适合accept传入,recv就直接报错 了
传参数可以传,但用不用无所谓,避免报错
现在只有一个线程,调用select,主线程就阻塞了,主线程就什么事情都做不了,把select函数扔到其他线程去跑,主线程就腾出来了
官方建议setblocking(False)选择非阻塞方式
现在的问题是,现在只有这边让主线程阻塞,
按照道理recv和accept都是阻塞函数,本身这两个也是阻塞行为,但是我们调用的时候已经ready了,数据已经有了,recv就不需要等了,本身是阻塞行为,因为通知它的时候数据已经准备好了,直接拿即可
所以现在是否阻塞是没有影响的,调用的时候已经准备好了
多路复用的图,上半部分ready才通知下半部分来拿
把selectt扔到一个函数里去,开启多线程,把select阻塞的扔到线程里去跑
定义的东西都可以往下放
使用一下event
quit退出
链接一下
mapping其实指的就是映射。key value的映射,是类字典的
一个是fds,一个是selectorkeys
遍历出的其实就是当下正在监控的哪些IO
重新链接一下
一个是socket accept,一个是conn
所以通过get_map方法遍历之后,就可以把东西拿出来了
现在退出可以拿到的对象有,select可以拿到,event需要处理一下(用几个socket,就需要清理几个socket)
现在就反注册
socket需要关一下,1。清理,链接,2.把fd交还给操作系统,复用一下
但是这样注册的时候生成map,在反注册的时候,看着是没有pop,但是别人想从kv卸除
链接发送消息
敲quit这一句就出现了问题
反注册就需要先记录下来,然后资源关闭
selector.close才可以清理干净
要把所有用的socket全部清除掉,也就是反注册掉,不要监控了,然后把socket该 关关了,所以quit的时候需要做的事情比较多
所以在这里相当于做一个列表解析式,反注册fileobj
这样就做到了资源一部分清理,因为结束和使用并不在一个线程里,无法使用with语法
现在就正常退出了
再次演化,更改需求,能否借助IO多路复用来完成项目
给一个selector,server里自己使用,新的serversocket和每一次新创建的socket
现在需要注册。,起个线程交给select来处理,也需要出处理一个select函数
现在监听注册在sever。socket上,注册完之后立马开始工作,因为会阻塞当前线程,选择扔到另一个线程里做
想要socket自己传进去
如果有人accept链接,不再阻塞后,拿到的key.data就是accept,accept传入fileobjet拿到的key.data就是accept,accept传入fileobjet传入就是socket
**注册conn,读世界EVENT_READ,data=self.recv **
现在也不需要这个判断了,因为selector帮你做了
紧接关注的是recv,收到消息后,就反注册conn
stop需要循环去清理
下面就需要看一对多的问题,每次accept进来就会生成新的conn,以前一对多的时候需要记录一下,现在self.clients是不保留的。
以前把所有的socket内容保存下容器里
现在从get_map里去拿,除了conn,还可以拿到accept所在的socket也可以拿到
通过data判断谁是谁
先遍历,有可能混入self.socket,需要进行判断
这样判断就区分开来了,两个函数的==就相当于is
清理一下代码
首先先需要用这个selector监控socket,这个soscket是用来接收链接的accept
在监听的上面进行等待,不阻塞后就开始进行操作
不阻塞后就开始进行操作
accept拿到这些连接后可以拿到新的链接
这个链接不要就可以反注册,recv结束,在get_map就找不到了
关闭的时候,get_map可以拿到所有关闭的socket,反注册相当于从map里把kv对去掉,会影响map的size,解决方法一样
反注册相当于从map里把kv对去掉,会影响map的size,解决方法一样,
临时生成容器,里面记录你想要清除的东西
现在就把多线程版本改成了IO多路复用版本(在linux优先使用EPOLL,效率很高)
想要监听写事件,send其实也是阻塞的,取决于缓冲区满不满,满了就阻塞了
**conn有读写,写1read 写2,write,3就是read and write,‘|’现在是按位相或 **
现在就是读写都处理,但是现在无法得知读就绪还是写就绪
最简单的方式就是把mask传入进来,mask是1读就绪,mask是2写就绪,mask是3读写同时就绪
如果是file.obj是self.sock,就是accept,如果不是都是conn创建除的新的socket,
也可以判断成,key.data,如果是self.recv就做下处理,self.accept就做下处理,总之是要做区分
events可能别人accept就绪了,别人也链接进来了,正好conn读写同时进行,所以event里到底有谁不好判断,有可能这里面不止一项,但是只要select不阻塞,那么events里至少有一个被监控对象就绪
这个与完之后,只可能是1,或者2 ,因为其他全是0
**在handle就需要判断究竟谁就绪,
if mask &位与 1,不能使用elif,因为既可能读就绪,也可能写就绪 **
现在的send需要用写就绪的方式,缓冲区没满是可写的,能否读和写彻底分离,现在是读里直接调用写
解耦可以分离开,queue,每个socket给每个queue单独分配,原来是给send fileobj发数据,现在应该是给queue,
现在想为每个socket配queue
data并没有限制用什么对象
现在为每一个conn单独配queue
传一个key对象,这个key对象就是conn
如果是读就绪,就会从缓冲区里拿出来,拿出来做过处理,就是send出去
handle能进来,一定是conn的,才能把mask送进来
key.data【1】就是queue,用这个queque.empty,不为空,key.fileobj,send
修改一下用is socket还是有点问题
运行一下
反复打印加号说明,说明一直是就绪的
3代表,至少是写就绪了
写就绪一直会告诉select要不要写,对于群聊软件大部分是读多写少,才用读监听,写多读少才用写监听
就会一直在跑
conn没活干了就不监听了