请教两个NIO的问题
最近看了下nio,有2个问题一直没想明白
希望能有人指点下
第一个是关于SelectionKey的OP_WRITE这个键
首先看下SocketChannel的write(ByteBuffer dst)这个方法的异常
NotYetConnectedException - 如果尚未连接此通道
ClosedChannelException - 如果此通道已关闭
AsynchronousCloseException - 如果正在进行写入操作时另一个线程关闭了此通道
ClosedByInterruptException - 如果正在进行写入操作时另一个线程中断了当前线程,因此关闭了该通道
并将当前线程的状态设置为中断
IOException - 如果发生其他 I/O 错误
因为SocketChannel是全双工的,再结合上面的异常
可以说在连接创建以后到断开之前,这段区间
SocketChannel都是可写的
那么如果有客户端连接进来,
注册了OP_WRITE这个键
那么下面这种代码
while(selector.select() > 0){
....
}
跟
while(true){}
不是等价的吗
我想请教下实际中,OP_WRITE这个是怎么用的?
我的设想是:
先不注册这个键,
当你要写数据的时候,
先把数据放到buffer,设置到attachment
然后再注册OP_WRITE事件,
等selector执行,
key.isWritable()的时候就把buffer取出来写到socket
然后把这个键取消掉
是这么用的吗?
第二个问题是
read(ByteBuffer dst)这个方法,
我看一般都是配合remaining()来用的,
但是如果我在网络延迟高的情况下写转过码的byte数组
不是会出现中断的情况吗
比如用gbk转码"hello world",写入的是"[104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]"
但是可能网络延迟高,我收到的只有"104, 101, 108, 108, 111"
剩下的部分还在传输,这个时候我拿着收到这部分去转码就会问题
我想请教的就是,有没有办法知道这个接受完成了?
这两天写了个简单的工具,我的办法是先把bytes的length写过来
读的时候先读这个长度,然后没有读够这个长度的bytes就一直阻塞,
不知道有没有其他的解决方案?
或者是设置某个特别的值表示一次写的结束之类的?
[最优解释]
问题一,你可能忽略了selector.select()的特性:
This method performs a blocking selection operation. It returns only after at least one channel is selected, this selector's wakeup method is invoked, or the current thread is interrupted, whichever comes first.
也就是它是阻塞式的,如果没有新的事件到达,就持续阻塞,而不是傻乎乎的狂循环浪费CPU。
关于OP_WRITE,其实如果你不考虑写给对方的输出流可能会因为对方没有及时读取而满了,导致你这边写阻塞的话,是可以不用考虑OP_WRITE;但由此就可能会在写的时候发生阻塞而丧失部分NIO的意义。
问题二,显然会,尤其是你发送的数据量越大,就约有可能出现部分传输,比如你发一个贼大的图片。需要自己设法解决,没有十分简易的解决方案。
比如可以另外使用一条专用的读线程来进行阻塞式读取;也可以考虑更复杂的自行组装。
这里有个不错的介绍:
https://www.ibm.com/developerworks/cn/java/l-niosvr/
[其他解释]
再顶一下,没人回12点结贴算了
[其他解释]
selector的阻塞我是知道的,
问题是只要有socketchannel连接进来,
并且注册了OP_WRITE这个键以后
每次select都会有key().isWritable()是true
也许我说得不清楚,用代码表示下
while (selector.select() > 0) {
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
it.remove();
if (key.isWritable()) {
System.out.println(11);
}
}
}
比如这段,只要存在没有断开的连接,
就疯狂的打印11
因为那个channel确实是一直都是可写的
一个cpu的占用也在80%的样子
基本上跟死循环是一样的
那我就认为OP_WRITE这个键应该不是这么用的..
我是想问实际中OP_WRITE会怎么使用?还是根本就不用
第二个问题我再看看
------其他解决方案--------------------
ibm那个帖子我看完了..
主要是这个地方,这是他的示例代码
if ( (key.readyOps() & SelectionKey.OP_ACCEPT) ==
SelectionKey.OP_ACCEPT) {
// Accept the new connection
ServerSocketChannel ssc =
(ServerSocketChannel) key.channel();
notifier.fireOnAccept();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
// 触发接受连接事件
Request request = new Request(sc);
notifier.fireOnAccepted(request);
// 注册读操作 , 以进行下一步的读操作
sc.register(selector, SelectionKey.OP_READ, request);
}
else if ( (key.readyOps() & SelectionKey.OP_READ) ==
SelectionKey.OP_READ ) {
// 提交读服务线程读取客户端数据
Reader.processRequest(key);
key.cancel();
}
else if ( (key.readyOps() & SelectionKey.OP_WRITE) ==
SelectionKey.OP_WRITE ) {
// 提交写服务线程向客户端发送回应数据
Writer.processRequest(key);
key.cancel();
}
}
他连接进来的时候确实只注册了OP_READ
sc.register(selector, SelectionKey.OP_READ, request);
但是处理里面又有OP_WRITE
key.readyOps() & SelectionKey.OP_WRITE== SelectionKey.OP_WRITE
其他地方也没有写是在哪个步骤注册了OP_WRITE
那我猜想也许是他需要写数据的时候才会注册OP_WRITE,
写完之后又cancel掉,直到下次需要写的时候再注册
是这样么
[其他解释]
不懂,帮顶
[其他解释]
再顶一下,实在没有人回了就结贴算了
[其他解释]
NIO在研究中,
LZ的担心数据丢失的问题可以不必要研究,那涉及到更底层网络传输了。
你应该用的TCP协议吧。
TCP协议是保证你接受的数据完整和有序的,什么断网什么的不用担心
[其他解释]
null