在进行NIO编程的时候,发现客户端只往服务端发送了一条消息后,就下线了,但是服务端select方法的返回值总是1,出现了死循环,经过调试发现,Selectionkeys方法返回的总是进行读操作的SocketChannel,也就是说注册的SocketChannel一直有读的就绪事件,可是客户端已经关闭了。总结来说就是:客户端主动关闭连接,服务端会一直出现可读事件,read一直返回-1,在网上查找了大量的博客后,解决了这个问题。
解决的方案是服务端主动关闭通道SocketChannel,当关闭后,select就不会再继续关注该通道,这样select就不会总是返回1了
很明显,可以解决问题,但是如果我的客户端没有下线,而是想继续发消息怎么办?这就要依靠read的返回值来进行判断了,如果read的返回值是-1,则主动关闭通道
while ((read = socketChannel.read(buffer)) > 0) {
System.out.println("read:"+read);
buffer.flip();
System.out.println(socketChannel.getRemoteAddress()+":"+new String(buffer.array()));
}
if(read == -1) {
System.out.println("read:"+read);
socketChannel.close();
}
最后附上测试的服务端和客户端源码:
package selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
*
*/
public class Server {
public static void main(String[] args) throws IOException, InterruptedException {
//1.打开通道
ServerSocketChannel server = ServerSocketChannel.open();
//2.绑定端口
server.socket().bind(new InetSocketAddress(8888));
//3.得到选择器
Selector selector = Selector.open();
//4.设置非阻塞,并注册到选择器
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int select = selector.select();
System.out.println("select:"+select);
Thread.sleep(500);
if(select > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
if(next.isAcceptable()) {
System.out.println("accpet");
ServerSocketChannel serverSocketChannel = ((ServerSocketChannel) next.channel());
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
} else if(next.isReadable()) {
System.out.println("read");
SocketChannel socketChannel = ((SocketChannel) next.channel());
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
int read = 0;
while ((read = socketChannel.read(buffer)) > 0) {
System.out.println("read:"+read);
buffer.flip();
System.out.println(socketChannel.getRemoteAddress()+":"+new String(buffer.array()));
}
if(read == -1) {
System.out.println("read:"+read);
socketChannel.close();
}
buffer.clear();
}
iterator.remove();
System.out.println("开始下次select");
}
} else {
System.out.println("去干其他事情");
}
}
}
}
package selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
*
*/
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
//拿到通道
SocketChannel client = SocketChannel.open();
//2.设置非阻塞模式
client.configureBlocking(false);
//3.连接服务器
client.connect(new InetSocketAddress("127.0.0.1", 8888));
//
while (!client.finishConnect()) {
System.out.println("建立连接中...");
}
ByteBuffer buffer = ByteBuffer.allocate(100);
String msg = "this is one";
buffer.put(msg.getBytes());
buffer.flip();
client.write(buffer);
buffer.clear();
//测试客户端发两次数据
Thread.sleep(5000);
msg = "this is two";
buffer.put(msg.getBytes());
buffer.flip();
client.write(buffer);
buffer.clear();
Thread.sleep(10000);
client.close();
}
}