Netty - NIO 之 Selector模式
一、总结
豁然开朗,之前以为非阻塞的实现是因为selector。现在才知道selector的为了让非阻塞变成更好:
- 无事件时,阻塞
- 有事件时,非阻塞
二、背景知识
2.1 事件的类型
三、Selector 模式
3.1 selector 处理accept事件
3.2 selector 取消事件
如果selector观测到了accept事件,而代码没有处理.那么此时selector.select()函数就不会阻塞,代码又会进入无限循环的模式...
因为selector会认为没有处理的事件,依然为新事件,所以会不停的把该事件放进selector.selectedKeys
事件发生后,要么处理,要么取消。不能置之不理。
3.3 selector read事件
由于iter是循环的selector上面所有的key,因此包含了ServerSocketChannel注册的key,也包括SocketChannel注册的key。
- ServerSocketChannel注册的key,关心accept事件。(代码1,说明当ServerSocketChannel接收到accept时间后,会把接收到的SocketChannel也注册进selector里面)
- SocketChannel注册的key,关心read事件
因此在循环里,需要区别对待。
1)为何处理过的SelectionKey,要手动删除?
- 当channel向selector注册时(红色横线标注的代码),会生成一个SelectionKey,放入红色框中,还会记录该key感兴趣的是哪个事件,已经对于的是哪个channel.
- 当有事件触发时,seletor.select()会接收到,并把事件对应的SelectionKey放入绿色的框中(此时依然会携带该SelectionKey感兴趣的是哪个事件)。但是一旦处理了该事件(例如是accept事件:对应着channel.accept()),绿色框中的SelectionKey就会抹去感兴趣的时间。如果不主动删除,一旦下次循环再次来到channel.accept(),就会返回null.
因此,要手动清除处理过的SelectionKey。
- 这里的remove()是从绿色的框中,移除。
- 但key.cancel()是从红色的框中,移除。
2)处理 客户端断开 问题
无论是 异常断开 还是 正常断开,都会自动触发一个read事件。
key.cancel()是从红色的框(selector的keys集合)中,移除
3)处理 消息边界 问题
问题描述
UTF-8编码中,一个中文是3个字节。由于服务器的buffer里面,只设置了4个字节。因此:
- 第一次读取是完整的“中”,加上三分之一的“国”
- 第二次读取是剩下三分之二的“国”
因此输出中,只有“中”是正常显示,其余都是乱码。
不完美的解决方案
如果buffer空间不够,就扩容。扩容后的新buffer需要把之前的数据copy过来,再去读新的数据。
注意点:如何把buffer和SelectionKey关联
selector会关联多个channel,可能同时有多个channel有read事件。如果公用一个buffer,内容就会乱掉。
因此必须一个channel,一个buffer。
这里就用到了,channel在像selector注册时的第三个参数att...
Tips: selector何时不阻塞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?