关于java:JAVA-NIO

java的网络编程中,会波及到2种IO模型,一个就是BIO,即阻塞IO。一个是NIO,即非阻塞IO。 在默认的状况下,IO是阻塞IO,比方tomcat容器就是就是BIO。而redis, netty等反对高并发服务,都是基于NIO。

NIO和BIO有3点区别:
1:BIO是基于流(stream)的,比方:FileStream, SockectStream。而NIO是基于缓冲区buffer的,比方LongBuffer, ByteBuffer。
2:BIO是阻塞的,NIO是非阻塞的。
3:NIO有选择器(Selector),而BIO没有。

NIO有三大组件:
Channel:通道, Buffer:缓冲区, Selector:选择器。

Channel:介绍:
在NIO中,应用Channel进行通信,能够向Channel写入音讯,也能够从Channel读取音讯。
Channel能够分为如下类型:
ServerSocketChannel:服务端的网络Channel。能够绑定监听端口,能够承受客户端的网络连接。
SocketChannel:示意网络连接Channel,能够从该Channel读取数据,也能够向Channel写入数据
DatagramChannel:能通过UDP读写网络中的数据
FileChannel 从文件中读写数据

Buffer介绍:
在NIO中,从channel读出数据或者向channel写入数据,都是通过buffer操作,这样能够提高效率。
依照类型不同,Buffer也分为不同类型:比方:ByteBuffer, LongBuffer, IntBuffer, CharBuffer, FloatBuffer等。
应用Buffer读写数据个别遵循以下四个步骤:
1. 写入数据到Buffer
2. 调用 flip() 办法
3. 从Buffer中读取数据
4. 调用clear()办法或者 compact()办法

当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,须要通过flip()办法将Buffer从写模式切换到读模式。在读模式下,能够读取之前写入到buffer的所有数据。一旦读完了所有的数据,就须要清空缓冲区,让它能够再次被写入。有两种形式能清空缓冲区:调用clear()或compact()办法。clear()办法会清空整个缓冲区。compact()办法只会革除曾经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的前面。    
Buffer 有三个重要属性:capacity,position,limit。position和limit的含意取决于Buffer处在读模式还是写模式。不论Buffer处在什么模式,capacity的含意总是一样的。
![image](/img/bVI7kq)

capacity: 作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,须要将其清空(通过读数据或者革除数据)能力持续写数据往里写数据。
position: 当你写数据到Buffer中时,position示意以后的地位。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前挪动到下一个可插入数据的Buffer单元。position最大可为capacity – 1。当读取数据时,也是从某个特定地位读。当将Buffer从写模式切换到读模式,position会被重置为0。 当从Buffer的position处读取数据时,position向前挪动到下一个可读的地位。
limit:在写模式下,Buffer的limit示意你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity。当切换Buffer到读模式时, limit示意你最多能读到多少数据。因而,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)。

Selector介绍:
Selector(选择器)是Java NIO中可能检测一到多个NIO通道,并可能通晓通道是否为诸如读写事件做好筹备的组件。这样,一个独自的线程能够治理多个channel,从而治理多个网络连接。
Selector反对的Channel的事件类型有:SelectionKey.OP_CONNECT, SelectionKey.OP_ACCEPT, SelectionKey.OP_READ, SelectionKey.OP_WRITE。


服务端示例代码:
public void startNioServer() throws Exception {
    ServerSocketChannel serverChannel = ServerSocketChannel.open(); //关上通道
 serverChannel.configureBlocking(false); // 非阻塞
 ServerSocket serverSocket = serverChannel.socket();
 serverSocket.bind(new InetSocketAddress(8090)); // 绑定端口
 System.out.println("bind port: 8090");
 Selector selector = Selector.open(); // 选择器
 serverChannel.register(selector, SelectionKey.OP_ACCEPT); //注册到选择器
 while (selector.select() > 0) { //判断事件筹备好的通道数量
 Iterator<SelectionKey> it = selector.selectedKeys().iterator();
 while (it.hasNext()) {
            SelectionKey key = it.next();
 if (key.isAcceptable()) { //客户端连贯的事件
 ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
 SocketChannel socketChannel = serverSocketChannel.accept(); //新建设的客户端连贯
 socketChannel.configureBlocking(false); //设置为非阻塞
 socketChannel.register(selector, SelectionKey.OP_READ); //注册可读事件
 }
            if (key.isReadable()) {
                SocketChannel socketChannel = (SocketChannel) key.channel();
 ByteBuffer buffer = ByteBuffer.allocate(1024); //调配缓冲区,在堆内存调配
 int len = 0;
 while ((len = socketChannel.read(buffer)) > 0) {
                    buffer.flip();
 System.out.println("NIO SERVER RECEIVE: " + new String(buffer.array(), 0, len));
 buffer.clear();
 }
                socketChannel.close();
 }
            it.remove();
 }
    }
    serverChannel.close();
}

客户端示例代码:

public void startNioClient(int idx) throws Exception {
    SocketChannel socketChannel = SocketChannel.open(); //关上通道
 socketChannel.configureBlocking(false); // 非阻塞
 socketChannel.connect(new InetSocketAddress(8090)); // 连贯近程服务
 while (!socketChannel.finishConnect()) { // 期待连贯实现
 }
    String msg = "client-" + idx + " send message.";
 ByteBuffer buffer = ByteBuffer.allocate(1024); //调配缓冲区,默认是在堆上调配
 buffer.put(msg.getBytes()); //写入数据
 buffer.flip(); //反转为读模式
 socketChannel.write(buffer); // 向通道写入信息
 socketChannel.close();
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理