我开始以为 attach 是分读的附件和写的附件的,但写了测试代码发现并不是。 服务端代码:
package NonBlocking;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NonBlockingServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8888));
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //首先注册 ACCEPT 事件
int result = 0; int i = 1;
while(true) { //遍历获得就绪事件
result = selector.select();
System.out.println(String.format("selector %dth loop, ready event number is %d", i++, result));
if (result == 0) {
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){ //就绪事件可能不止一个
SelectionKey sk=iterator.next();
if(sk.isAcceptable()){ //如果是 ACCEPT,那么与之关联的 channel 肯定是个 ServerSocketChannel
System.out.println("服务端有 ACCEPT 事件就绪");
ServerSocketChannel ss = (ServerSocketChannel)sk.channel();
SocketChannel socketChannel = ss.accept();
socketChannel.configureBlocking(false); //也切换非阻塞
socketChannel.register(selector, SelectionKey.OP_READ); //注册 read 事件
}
else if(sk.isReadable()){ //如果是 READ,那么与之关联的 channel 肯定是个 SocketChannel
System.out.println("服务端有 READ 事件就绪");
SocketChannel socketChannel = (SocketChannel)sk.channel();
ByteBuffer buf=ByteBuffer.allocate(1024);
int len=0;
StringBuilder sb = new StringBuilder();
while((len=socketChannel.read(buf))>0){
buf.flip();
String s = new String(buf.array(),0,len);
sb.append(s);
buf.clear();
}
//服务端开始响应消息
ByteBuffer readAtta = (ByteBuffer)sk.attachment();
if (readAtta != null) {
System.out.println("lasttime readAtta string is: "+new String(readAtta.array()));
} else {
System.out.println("lasttime readAtta is null ");
}
sk.attach(ByteBuffer.wrap(sb.toString().getBytes()));
sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
String sendStr = "您的消息'"+sb.toString()+"'我已经收到了";
System.out.println("接下来 attach 的是:"+sendStr);
sk.attach(ByteBuffer.wrap(sendStr.getBytes()));
}
else if(sk.isWritable()){
System.out.println("服务端有 WRITE 事件就绪");
SocketChannel socketChannel = (SocketChannel)sk.channel();
ByteBuffer writeAtta = (ByteBuffer) sk.attachment();
if (writeAtta != null) {
System.out.println("lasttime writeAtta string is: "+new String(writeAtta.array()));
} else {
System.out.println("lasttime writeAtta is null ");
}
sk.interestOps(sk.interestOps() & ~SelectionKey.OP_WRITE);
}
iterator.remove();
System.out.println("after remove key");
}
}
}
}
客户端代码(这个不重要,就放链接里了): https://paste.ubuntu.com/p/58P39BQQTm/ 使用方法:客户端在控制台每输入一句话,再去服务端看执行效果:
selector 1th loop, ready event number is 1
服务端有 ACCEPT 事件就绪
after remove key
selector 2th loop, ready event number is 1
服务端有 READ 事件就绪
lasttime readAtta is null
接下来 attach 的是:您的消息'你好'我已经收到了
after remove key
selector 3th loop, ready event number is 1
服务端有 WRITE 事件就绪
lasttime writeAtta string is: 您的消息'你好'我已经收到了
after remove key
selector 4th loop, ready event number is 1
服务端有 READ 事件就绪
lasttime readAtta string is: 您的消息'你好'我已经收到了
接下来 attach 的是:您的消息'他好吗'我已经收到了
after remove key
selector 5th loop, ready event number is 1
服务端有 WRITE 事件就绪
lasttime writeAtta string is: 您的消息'他好吗'我已经收到了
after remove key
sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
和sk.interestOps(sk.interestOps() & ~SelectionKey.OP_WRITE);
是正确的注册和反注册 write 事件的姿势?然后我进行服务端代码修改:
sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
换成socketChannel.register(selector, SelectionKey.OP_WRITE)
sk.interestOps(sk.interestOps() & ~SelectionKey.OP_WRITE);
换成socketChannel.register(selector, SelectionKey.OP_READ)
再看服务端效果则是这样了:
selector 1th loop, ready event number is 1
服务端有 ACCEPT 事件就绪
after remove key
selector 2th loop, ready event number is 1
服务端有 READ 事件就绪
lasttime readAtta is null
接下来 attach 的是:您的消息'你好'我已经收到了
after remove key
selector 3th loop, ready event number is 1
服务端有 WRITE 事件就绪
lasttime writeAtta string is: 您的消息'你好'我已经收到了
after remove key
selector 4th loop, ready event number is 1
服务端有 READ 事件就绪
lasttime readAtta is null
接下来 attach 的是:您的消息'他好吗'我已经收到了
after remove key
selector 5th loop, ready event number is 1
服务端有 WRITE 事件就绪
lasttime writeAtta string is: 您的消息'他好吗'我已经收到了
after remove key
可以发现:
socketChannel.register(selector, SelectionKey.OP_READ);
后,下一次再执行 attachment,附件就丢了,变成 null 了。socketChannel.register(selector, SelectionKey.OP_WRITE);
后,下一次再执行 attachment,附件却不会丢。 这是为什么呢?这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.