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的含意总是一样的。

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();
}
发表回复