您的代码会遭受常见的大量 NIO 错误:
public class NIOServer implements Runnable {
private void runServer() throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(8080));
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
int readyChannels = selector.selectNow();
你正在选择不睡觉。如果没有就绪通道,该循环将导致 CPU 冒烟。使用超时,即使是很短的超时。
SelectionKey selectionKey = client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
除非您已经编写了一些内容并获得了一个简短的返回值,否则您不应该注册 OP_WRITE。
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
channel.configureBlocking(false);
通道已经处于非阻塞模式。当你接受它时,你就把它放在那里。除非它处于非阻塞模式,否则您无法选择它。消除。
ByteBuffer buffer = ByteBuffer.allocate(100);
buffer.clear();
缓冲区已经清空了。你刚刚创建了它。消除。
int bytesRead = channel.read(buffer);
while(bytesRead>0){
System.out.println("Read bytes: "+ bytesRead);
bytesRead=channel.read(buffer);
if(bytesRead==-1){
channel.close();
key.cancel();
关闭通道会取消密钥。你不需要两者。删除取消。
//key.cancel();
//channel.close();
消除。不要留下死代码来迷惑未来的读者。
具有 NIO 选择器的客户端:
public class NIOSelectorClient implements Runnable{
private Selector selector;
public void startClient() throws IOException {
SocketChannel socketChannel= openConnection();
selector = Selector.open();
socketChannel.register(selector,SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
往上看。
while(!Thread.interrupted()) {
int readyChannels = selector.selectNow();
往上看。
if(!currentKey.isValid()) {
continue;
}
非常好,但您需要在下面的所有其他测试之前进行此测试,例如currentKey.isValid() && currentKey.isReadable()
,因为先前的处理程序可能已关闭通道或取消密钥。这同样适用于服务器代码。
if(currentKey.isConnectable()) {
System.out.println("I'm connected to the server!");
handleConnectable(currentKey);
}
if(currentKey.isWritable()){
handleWritable(currentKey);
}
你从来不处理isReadable()
在客户端中。你不期待任何意见吗?
private void handleWritable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100);
Scanner scanner = new Scanner(System.in);
System.out.println("Enter message to server: ");
String output = scanner.nextLine();
在这里,您阻止了整个客户端,包括其所有内容SocketChannels
等待用户输入一些内容。这是非常糟糕的设计。
buffer.clear();
你不需要这个。您即将将缓冲区作为局部变量释放。你已经完成了。
channel.close();
写入一次后要关闭通道吗?为什么?
key.cancel();
关闭通道会取消密钥。你不需要两者。你不需要这个。消除。
private void handleConnectable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if(channel.isConnectionPending()) {
channel.finishConnect();
finishConnect()
可以返回false
,在这种情况下,您不应在此方法中执行任何进一步操作。
channel.configureBlocking(false);
该通道已处于阻塞模式。不然你不可能到这里。消除。
channel.register(selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
}
请参阅上面的 OP_WRITE。
private static SocketChannel openConnection() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
socketChannel.configureBlocking(false);
while(!socketChannel.finishConnect()) {
System.out.println("waiting connection....");
}
删除这个循环。这就是 OP_CONNECT 的用途。你养了一只狗,自己却在狂吠。如果您不想在连接完成之前离开这里,请在阻塞模式下进行。而不是仅仅让CPU吸烟。
这是非 NIO 客户端:
public class NIOClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while(socket.isConnected()) {
插座已连接。当你构建它时你就连接了它。它保持这样。isConnected()
不是对等断开的有效测试。