很多人对NIO的写操作理解的不深,不知道为什么要注册写事件,何时注册写事件,为什么写完之后要取消注册写事件。
Selector.isWriteable()方法监控的是内核的写缓冲器是否可写,所以除非内核的写缓冲区满了,否则一旦一个SocketChannel注册了写事件,每次selector都会选中这个SocketChannel,进入到isWriteable()分支中去。
实际上服务器端写消息时完全可以不注册写事件,直接调用SocketChannel.write(ByteBuffer),也能把数据写到缓冲区并发送出去。但是这种情况下selector不会选择到isWriteable()分支。 在写缓冲区满的情况下处理也不灵活,需要while(ByteBuffer.hasRemain())来检查写的状态,会导致CPU空转while.
更好的做法是注册写事件。 注册写事件也有两种做法,一种是SocketChannel.register(selector, SelectionKey.OP_WRITE),直接注册到SocketChannel。第二种是用SelectionKey.interestOps(SelectionKey.interestOps() | SelectionKey.OP_WRITE),用SelectionKey来注册。每个SelectionKey只会关联一个SocketChannel,相当于注册到了SocketChannel上。
所以NIO写方法的一般做法是:
1. 注册写事件
2. 写数据
3. 取消注册写事件
注意第3步,每次写完之后要取消注册写事件,SelectionKey.interestOps(SelectionKey.interestOps() & ~SelectionKey.OP_WRITE),否则只要内核写缓冲区可写,这个SocketChannel就会一直被selector选中。

本文探讨了NIO在写操作中的理解,解释了为何需要注册和取消注册写事件。通过Selector.isWriteable()监控内核写缓冲区,直接写操作可能造成CPU空转。推荐的流程包括注册写事件、写数据和随后取消注册。使用SelectionKey.interestOps()进行事件管理,并利用Attachment将业务数据与SocketChannel解耦,提高灵活性和框架设计如Thrift的TNonblockingServer。
最低0.47元/天 解锁文章
361

被折叠的 条评论
为什么被折叠?



