我首先使用伪代码来描述问题,然后我会将整个代码粘贴到以下内容中,后者可以在本地运行。
1.selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
2.while(true){
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
3. if(!key.isAcceptable()){
continue;
}
4. ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
...
}
}
在步骤4中发生异常,然后我在步骤3中进行检查,但是它无法通过可接受的检查并进入死循环。 偶尔,它可以正常接收和响应,我没有做任何改变,这对我来说太奇怪了。 在这里我粘贴代码,希望有人可以帮助我。感谢。
package io.Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import io.util.IOUtil;
public class NioServer extends Thread{
private int port;
private Selector selector;
private ServerSocketChannel serverChannel;
public NioServer(int port){
this.port = port;
}
@Override
public void run() {
try{
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch(IOException e){
IOUtil.close(serverChannel);
}
System.out.println("server start:" + port);
while(true){
try {
selector.select();
} catch (ClosedSelectorException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if(!key.isValid()){
key.cancel();
IOUtil.close(key.channel());
IOUtil.close(key.selector());
System.out.println(IOUtil.now() + "clear a invalid key.");
continue;
}
// i put a check here,if is not Acceptable,then continue, but it's a dead loop
if(!key.isAcceptable()){
System.out.println("not Acceptable");
continue;
}
try {
//Exception here: SocketChannelImpl cannot be cast to ServerSocketChannel
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
if(channel == null){
continue;
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
// if (key.isReadable()){
// System.out.println("not read");
// }
ByteBuffer buffer = ByteBuffer.allocate(1024);
if (channel.read(buffer) > 0) {
buffer.flip();
byte[] byteArray = new byte[buffer.remaining()];
buffer.get(byteArray);
String expression = new String(byteArray, "UTF-8");
System.out.println(IOUtil.now() + "receive request:" + expression);
String result = null;
response(channel, result);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void shutdown(){
IOUtil.close(selector);
IOUtil.close(serverChannel);
}
private void response(SocketChannel channel, String response) throws IOException {
response = "hello response";
System.out.println(IOUtil.now() + "send response:"+ response);
byte[] bytes = response.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
channel.write(buffer);
}
public static void main(String[] args) {
new NioServer(IOUtil.DEFAULT_PORT).start();
}
}
package io.Nio;
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.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import io.util.IOUtil;
public class NioClient extends Thread{
private volatile CountDownLatch connectLatch;
private String ip;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private NioClient(String ip, int port) {
this.ip = ip;
this.port = port;
connectLatch = new CountDownLatch(1);
}
public static NioClient open(String ip, int port){
NioClient client = new NioClient(ip,port);
client.start();
return client;
}
@Override
public void run(){
try{
long begin = System.currentTimeMillis();
System.out.println(IOUtil.now() + "start client");
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
yield();
}
System.out.println(IOUtil.now() + "cost time:" + (System.currentTimeMillis() - begin) + "ms");
connectLatch.countDown();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while(true){
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = it.next();
if(!key.isValid() || !key.isReadable()){
continue;
}
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
if(channel.read(buffer) > 0){
buffer.flip();
byte[] byteArray = new byte[buffer.remaining()];
buffer.get(byteArray);
String response = new String(byteArray,"UTF-8");
System.out.println(IOUtil.now() + "receive response:" + response);
}
}
}
}catch(IOException e){
e.printStackTrace();
}
}
public void request(String request) {
try {
connectLatch.await();
} catch (InterruptedException e) {
System.out.println(IOUtil.now() + "interrupted" + e.getMessage());
}
try {
byte[] bytes = request.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
socketChannel.register(selector, SelectionKey.OP_READ);
//TODO
System.out.println(IOUtil.now() + "send request:" + request);
socketChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(final String[] args) throws InterruptedException {
NioClient client = NioClient.open(IOUtil.DEFAULT_HOST, IOUtil.DEFAULT_PORT);
client.request("hello");
// while(true){
// sleep(500);
// String request = IOUtil.buileRequest(1991);
// client.request(request);
// }
}
}
package io.util;
import java.io.Closeable;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class IOUtil {
public static final String DEFAULT_HOST = "127.0.0.1";
public static final int DEFAULT_PORT = 8080;
public static final String operators[] = {"+", "-", "*", "/"};
public static final int CLIENNT_NUM = 10;
public static final boolean CLIENT_CLOSEABLE = true;
public static String now(){
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS ").format(new Date());
}
public static String buileRequest(int seed){
Random random = new Random(seed);
return random.nextInt(10) + IOUtil.operators[random.nextInt(4)] + (random.nextInt(10) + 1);
}
public static void close(Closeable io) {
if (io != null) {
try {
io.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
答案 0 :(得分:0)
您描述的异常可能发生的唯一方法是,如果您在非ServerSocketChannel
的通道上尝试进行类型转换,如果其键已准备好但不可接受,则可能会发生这种情况。很明显,当你遇到这个例外时你没有第2步,所以你处理了一个“可读”的频道,好像它是一个“可接受的”频道。
因此,您发布的代码实际上并未表现出该问题,但它确实存在大量其他问题。您不需要因为某个键无效而关闭选择器,如果它无效则已取消,因此您无需取消它,无论如何关闭该通道都会取消该键。为什么您对OP_READ已接受并注册的频道上的OP_READ / isReadable()
感兴趣?你为什么试着从你刚刚接受的频道中读取,而不是等待OP_READ?
扔掉它,好好看看Java NIO教程。
我想在客户端评论一段特别的废话:
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
yield();
}
这里,您在非阻塞模式下执行相当于阻塞模式的连接。它可以全部替换为:
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(ip,port));
socketChannel.configureBlocking(false);
然后:
socketChannel.register(selector, SelectionKey.OP_CONNECT);
您正在注册已经发生的事件。真奇怪。您永远不会从已连接的套接字中获取OP_CONNECT。删除。