JAVA中的BIO、NIO、EPOLL

前几天听了清华周老师讲的nio的课,感觉讲得很清楚,所以记录一下,一方面自己理解一次,加深印象,另一方面,防止后面忘记,时长回顾;以下是自己的理解内容,表达能力有限,仅供个人参考:

一、BIO

BIO:同其名字,阻塞IO,accept、read、connect、write操作都会阻塞;

                            

 

 BIO代码示例:


public class NIOExample {

final static int PORT = 8088;

final static ThreadPoolExecutor executor =
new ThreadPoolExecutor(3,3,3L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(100));


public static void main(String[] args) {
final ConcurrentLinkedQueue<SocketChannel> clients = new ConcurrentLinkedQueue();
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(PORT));
//配置非阻塞
       serverSocketChannel.configureBlocking(false);
InetSocketAddress local = (InetSocketAddress)serverSocketChannel.getLocalAddress();
System.out.println(local.getAddress());
//一个线程负责处理有效连接上的数据
      new Thread(new Runnable(){
public void run() {
connectionHandle(clients);
}
}).start();
//一个线程负责接收连接
      while(true){
//非阻塞,没有客户端连接进来,返回-1,BIO会一直阻塞
        SocketChannel client = serverSocketChannel.accept();
if(client != null){
client.configureBlocking(false);
clients.add(client);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

private static void connectionHandle(ConcurrentLinkedQueue<SocketChannel> clients) {
while(true){
final SocketChannel client = clients.poll();
final ByteBuffer byteBuffer = ByteBuffer.allocate(512);
if(client != null){

executor.execute(new Runnable() {
public void run() {
try {
InetSocketAddress remoteAddress = (InetSocketAddress)client.getRemoteAddress();
System.out.println("收到消息,对方端口号:"+remoteAddress.getPort());
client.read(byteBuffer);
byteBuffer.flip();
System.out.println("收到消息:"+new String(byteBuffer.array()));
} catch (IOException e) {
e.printStackTrace();
}

}
});
}
}
}
}

三、epoll

在系统内核中select、poll、epoll都是多路复用器,java中对多路复用器的封装是在Selector中;

     

selector代码示例:

public class SelectorExample {

    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //selector多路复用器必须是非阻塞的
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",8088));
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        Set<SelectionKey> selectionKeys = selector.selectedKeys();

        Iterator<SelectionKey> iterator = selectionKeys.iterator();
        ByteBuffer readBuff = ByteBuffer.allocate(1024);
        ByteBuffer writeBuff = ByteBuffer.allocate(1024);
        while(iterator.hasNext()){
            try {
                SelectionKey selectionKey = iterator.next();
                if(selectionKey.isAcceptable()){
                    //接收到连接。做什么事
                    // 创建新的连接,并且把连接注册到selector上,而且,
                    // 声明这个channel只对读操作感兴趣。
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }else if(selectionKey.isReadable()){
                    //客户端有数据发送过来,读数据
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    readBuff.clear();
                    socketChannel.read(readBuff);

                    readBuff.flip();
                    System.out.println("received : " + new String(readBuff.array()));
                    selectionKey.interestOps(SelectionKey.OP_WRITE);
                }else if(selectionKey.isWritable()){
                    writeBuff.rewind();
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.write(writeBuff);
                    selectionKey.interestOps(SelectionKey.OP_READ);
                }
            } finally {
                iterator.remove();
            }
        }

    }
}

 

epoll的总结:

1.Server向selector注册事件

2.selector调用系统内核,系统内核调用epoll_create方法为绑定的端口分配一块内存

3.当用channel连接进来时,会记录channel的元数据信息,调用内核方法epoll_ctl方法,将信道元数据保存到内存中,

4.当信道有数据传送过来时,系统内核将传送的数据保存到channel对应的queue中

5.selector调用系统内核epoll_wait方法,对queue中存放的数据进行读取

posted @ 2020-09-26 21:44  月下沧澜  阅读(74)  评论(0)    收藏  举报