更新时间:2023-01-18 09:07:02
您的代码经常遇到大量NIO错误:
Your code suffers from the usual raft of NIO mistakes:
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.使用超时,甚至是短暂的超时.
You are selecting without a sleep. If there are no ready channels this loop will smoke the CPU. Use a timeout, even a short one.
SelectionKey selectionKey = client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
除非已经写过东西并获得简短的返回值,否则不应该注册OP_WRITE.
You should not register for OP_WRITE unless you've already written something and got a short return value.
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
channel.configureBlocking(false);
该频道已处于非阻塞模式.接受时将其放到那里.除非它处于非阻止模式,否则您无法选择它.删除.
The channel is already in non-blocking mode. You put it there when you accepted it. You couldn't have selected on it unless it was in non-blocking mode. Remove.
ByteBuffer buffer = ByteBuffer.allocate(100);
buffer.clear();
缓冲区已清除.您刚刚创建了它.删除.
The buffer is already clear. You just created it. Remove.
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();
关闭频道会取消键.两者都不需要.取消取消.
Closing the channel cancels the key. You don't need both. Remove the cancel.
//key.cancel();
//channel.close();
删除.不要留下无效的代码来迷惑未来的读者.
Remove. Don't leave dead code lying around to confuse future readers.
具有NIO选择器的客户端:
Client with NIO Selector:
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()
,因为先前的处理程序可能已关闭通道或取消了密钥.服务器代码中也是如此.
Very good but you need this test before every other one below, e.g. currentKey.isValid() && currentKey.isReadable()
, because a prior handler may have closed the channel or cancelled the key. Same applies in the server code.
if(currentKey.isConnectable()) {
System.out.println("I'm connected to the server!");
handleConnectable(currentKey);
}
if(currentKey.isWritable()){
handleWritable(currentKey);
}
您永远不会在客户端中处理isReadable()
.您不希望有任何输入吗?
You never handle isReadable()
in the client. Don't you expect any input?
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
),等待用户输入一些输入.这是非常糟糕的设计.
Here you are blocking the entire client including all its SocketChannels
waiting for the user to enter some input. This is very poor design.
buffer.clear();
您不需要这个.您将要释放缓冲区作为局部变量.您已经完成了.
You don't need this. You're about to release the buffer as a local variable. You're done with it.
channel.close();
一次写入后您要关闭通道吗?为什么?
You're closing the channel after one write? Why?
key.cancel();
关闭频道会取消键.两者都不需要.您不需要这个.删除.
Closing the channel cancels the key. You don't need both. You don't need this. Remove.
private void handleConnectable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if(channel.isConnectionPending()) {
channel.finishConnect();
finishConnect()
可以返回false
,在这种情况下,您不应该对此方法做进一步的操作.
finishConnect()
can return false
, in which case you should do nothing further in this method.
channel.configureBlocking(false);
该频道已处于阻止模式.否则,您将无法到达这里.删除.
The channel is already in blocking mode. Otherwise you couldn't have got here. Remove.
channel.register(selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
}
有关OP_WRITE,请参见上文.
See above re 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.
Remove this loop. That's what OP_CONNECT is for. You are keeping a dog and barking yourself. If you want not to proceed out of here until the connection is complete, do it in blocking mode. Instead of just smoking the CPU.
这是非NIO的条件:
And this is the non-NIO cliet:
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()
不是对等断开连接的有效测试.
The socket is connected. You connected it when you constructed it. It stays that way. isConnected()
is not a valid test for peer disconnection.