java nio selector怎么达到与Linux select一样的效果

前几天做了一个linux下的socket实验使用了select,可以让程序在不使用多线程的情况下,实现io的异步,也就是可以收数据,也可以发数据,但不阻塞。
于是我就想能不能移植到Java下来实现。结果我发现Java也有select类似的功能,但是我发现Java nio的Chanel无法对标准输入进行处理,要发数据的时候就会阻塞。
可能是我技术不够,我想问问大家Java nio到底能不能实现与linux select一样的效果?
package server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;

public class ServerSock {
public ServerSock() {
// TODO Auto-generated constructor stub
try {
ServerSocketChannel server= ServerSocketChannel.open();
server.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8899), 5);
System.out.println("服务器启动....");
Selector selector = Selector.open();
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);

        ByteBuffer buf = ByteBuffer.allocate(48);
        SocketChannel channel = null;
        while(true){
            int a=selector.select();

            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
            while(keyIter.hasNext()){
                SelectionKey key = keyIter.next();
                if (key.isAcceptable()) {
                    channel = server.accept();
                    System.out.println("接受: ");
                    channel.configureBlocking(false);
                    channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                }else if (key.isReadable()) {
                    System.out.println("读取:");
                    int bytesRead = channel.read(buf);
                    buf.flip();
                    String aString = new String(buf.array()).trim();
                    System.out.println(aString);
                    buf.clear();
                }else if (key.isWritable()) {
                    System.out.println("写入:");

                    Scanner scanner = new Scanner(System.in);
                    String b = scanner.nextLine();
                    buf.put(b.getBytes());
                    buf.flip();
                    channel.write(buf);
                    buf.clear();
                }
                keyIter.remove();
            }
        }

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
public static void main(String[] args) {
    new ServerSock();
}

}


3个回答

Java中Selector实现的是非阻塞IO,异步IO需要使用JDK7中的NIO2支持,方式是使用Future或Listener来进行异步处理。看代码中
```else if (key.isWritable()) {
System.out.println("写入:");

                Scanner scanner = new Scanner(System.in);
                String b = scanner.nextLine();
                buf.put(b.getBytes());
                buf.flip();
                channel.write(buf);
                buf.clear();```
这里使用到的Scanner和System.in是阻塞IO的方式。新建连接总是isWriable的,所以要先注册OP_READ,等读完完整的请求后再设置OP_WRITE, 然后发送数据,判断
是否是完整的请求可以采用一些带消息长度标识的长度字段。write时返回内容要从System.in中读取的话会阻塞整个IO处理线程,导致while循环卡住,所以最好是放到单独的线程判断要write的内容,不阻塞主的IOloop线程。还有不太清楚你描述的阻塞和不阻塞是什么样的效果
lq1115
lq1115 其实我想实现的是当我有键盘输入的时候就发送数据,有数据来的时候就接收数据。不阻塞的意思就是当没有数据来的时候,线程不会因为等待要等待数据来而阻塞,而是如果我有键盘输入的时候帮我发送数据
接近 3 年之前 回复

你标准输入输出有没有设置异步模式

lq1115
lq1115 我就是不懂怎么让标准输入输出设置成异步模式,而且标准输入输出也不知道能不能再selector中注册
接近 3 年之前 回复

java中JDK提供的Selector是API,在具体的实现中,根据不同的平台不同的操作系统版本会有不同的实现,如mac上使用kqueue,linux高版本使用epoll。

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问