java - 是否需要注册兴趣才能写入 NIO 套接字以发送数据?
问题描述
是否需要注册兴趣才能写入 NIO 客户端套接字通道以发送数据?socketChannel.register(selector, SelectionKey.OP_WRITE)
在写信给客户之前,我是否必须总是打电话或类似的电话SocketChannel
才能在那里写信?
仅仅在客户端线程中将数据写入客户SocketChannel
端channel.write(outputBuffer)
并唤醒可能阻塞还不够吗?Selector
主选择器循环将如下所示:
Selector selector = SelectorProvider.provider().openSelector();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
while (selector.select() > 0) {
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = readyKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey)keyIterator.next();
keyIterator.remove();
while (keyIterator.hasNext()) {
...
if (key.isAcceptable()) {
...
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
// client socket channel would be permanently in the read mode
...
} else if (key.isReadable()) {
...
} else if (key.isWritable()) {
// the key should know here that the underlying channel
// has something to be send to the wire, so it should get
// here if there are still data to be sent
socketChannel.write(outputBuffer)
}
只有当还有一些东西要发送时,它才会到达分支,即来自初始调用if (key.isWritable())
的剩余数据;channel.write(outputBuffer)
就像当消息太长并且需要发送成块并且我不想阻塞时一样。循环将旋转直到outputBuffer.hasRemaining()
是 finally false
。
我什至在想,是否必须写入 Channel,即发送数据,通过 Selector 完成?只留下传入流量由 Selector 处理,因为只有传入流量需要等待状态?
更新
通过进一步阅读有价值的user207421帖子,我部分地启发了我发布这个问题,NIO Javadoc,在推动加入点的过程中,我总结了这一点:
SocketChannel 和 SelectionKey 是线程安全的。
在选择器循环之外从/向通道套接字读取和写入确实是可能的;它独立于它。外部阅读没有多大意义,写入套接字肯定可以,实际上大部分写入都是这样完成的。
数据的写入通常在选择器循环之外的不同线程中启动。
write(buffer)
不需要发送所有数据 - 如果消息对于套接字缓冲区来说太大,远程客户端会非常慢甚至没有响应。在这种情况下write()
必须反复调用。Buffer 每次调用都会更新当前位置,具体取决于发送了多少,可以是 和 之间的任何0
值buffer.remaining()
。如果
write()
返回0
然后,并且user207421不能再强调这一点,可能是唯一的情况,OP_WRITE
需要注册并且写入然后在选择器循环中处理。写入所有数据后,
OP_READ
必须立即恢复在
OP_WRITE
没有任何数据写入的情况下设置会导致选择器循环无休止地旋转;浪费CPU时间。如果
write()
返回>0
但仍未发送所有剩余的消息数据(即buffer.remaining() > 0
. 我是否也应该设置OP_WRITE
并将其传递给选择器循环,或者write(buffer)
在编写线程内循环,直到没有余数或被0
返回?真的会发生这样的情况吗?官方
SocketChannel
Javadoc最初说除非另有说明,否则写入操作只有在写入所有请求的r个字节后才会返回......意思是,不,它不会发生!但随后他们补充说: 某些类型的通道,取决于它们的状态,可能只写入一些字节......所以这是否意味着write()
可以在发送所有字节之前返回?或者这是否意味着如果它只发送了一半的缓冲区,则返回“0”?在写返回0的场景下,写时传递给选择器循环,即不同的线程;写入线程是否被阻塞以将全新的消息(即新缓冲区)写入同一个套接字通道?或者我必须确保它不会通过其他方式发生?Javadoc 说:可以随时调用此方法。然而,如果另一个线程已经在这个通道上启动了一个写操作,那么这个方法的调用将阻塞,直到第一个操作完成。这是否涵盖了我的情况?
官方 Javadoc 并没有提到
0
作为write()
方法返回值的任何特殊含义。它只是说:写入的字节数,可能为零。唔。我也不确定在写入过程中如何处理无响应或缓慢的客户端。我猜想
0
从选择器循环返回write()
并将其传递给选择器循环正是它这样做的原因。然后选择器可以,我只是在这里猜测,以最佳方式管理写入:加速、减慢、增加间隔、发送更小的块,等等;无论底层 TCP/IP 堆栈返回什么。
解决方案
推荐阅读
- nuxt.js - Nuxt store setup-vuex 中的错误] 未知操作类型:fetchProfiles
- vue.js - Vuelidate:多步表单验证
- javascript - 如何使我的导航栏可点击或响应?
- ios - 如何在主队列中执行两个操作
- java - 密码验证无法正常工作
- javascript - 如何获取已登录的用户信息
- django - 是否可以为 dockerized angular plus django web 应用程序创建一个 .msi 安装程序?
- postgresql - 使用 libpq 准备多行
- java - 如何使用 Java 8 收集器对三重嵌套地图求和
- python - 为什么我的树莓派只搜索 pypi 简单版本?