在進行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();
}
}