一.NIO中的缓冲区Buffer的内部原理
缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如果我们使用get()方法从缓冲区获取数据或者使用put()方法把数据写入缓冲区,都会引起缓冲区状态的变化。
在缓冲区中,最重要的属性有下面三个:position,limit,capacity。其中postion主要是定位接下来要操作元素的角标,limit定位最后一个元素的位置,往往元素是由position和limit共同决定,调用flip()方法的时候就是把当前position赋值给limit,并把position设置为0。具体细节在此不赘述。
二.实现服务端和客户端通信(主要用到选择器selector绑定事件)
服务端代码如下:
package cn.test.nio; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; import java.io.IOException; public class NIOServer { private Selector selector; private ByteBuffer buff = ByteBuffer.allocate(1024); public void init(int port) throws IOException{ selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); //通道对应的ServerSocket绑定到对应的端口上 ServerSocket serverSocket = serverSocketChannel.socket(); serverSocket.bind(new InetSocketAddress(port)); //把通道绑定到通道管理器上,服务端接受客户端的连接事件时候触发 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException { System.out.println("服务端启动成功。。"); //循环监听选择器上是否有对应事件 while (true) { selector.select(); Set<SelectionKey> selectedKyes = selector.selectedKeys(); Iterator<SelectionKey> it = selectedKyes.iterator(); while(it.hasNext()){ SelectionKey key = (SelectionKey)it.next(); it.remove(); /** * 当遇到客户端连接事件后,开启读事件。当捕捉到读事件后,把信息写回客户端 */ if((key.readyOps()&SelectionKey.OP_ACCEPT)==SelectionKey.OP_ACCEPT){ ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.register(this.selector, SelectionKey.OP_READ); }else if((key.readyOps()&SelectionKey.OP_READ)==SelectionKey.OP_READ){//把信息写回客户端 SocketChannel channel = (SocketChannel) key.channel(); while(true){ /** * 重置buff,postion=0;limit=capacity */ buff.clear(); int r = channel.read(buff); if(r<=0){ break; } /**1.把limit设置为当前position,把position位置置0 * 2.把数据写入通道前,对数据进行定位,取postion->limit之间的数据 */ buff.flip(); channel.write(buff); } byte[] data = buff.array(); System.out.println("服务端接受并返回信息:::"+new String(data).trim()); } } } } public static void main(String[] args) throws IOException { NIOServer n = new NIOServer(); n.init(8002); n.listen(); } }
客户端代码如下:
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class NIOClient { private Selector selector; public void init(String ip,int port) throws IOException{ selector = Selector.open(); SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); channel.connect(new InetSocketAddress(ip, port)); channel.register(selector,SelectionKey.OP_CONNECT); } public void listen() throws IOException { //循环监听选择器上是否有对应事件 while (true) { selector.select(); Set<SelectionKey> selectedKyes = selector.selectedKeys(); Iterator<SelectionKey> it = selectedKyes.iterator(); while(it.hasNext()){ SelectionKey key = it.next(); it.remove(); //开启连接服务端的兴趣事件 if((key.readyOps()&SelectionKey.OP_CONNECT)==SelectionKey.OP_CONNECT){ SocketChannel channel = (SocketChannel) key.channel(); if(channel.isConnectionPending()){ channel.finishConnect(); } channel.configureBlocking(false);
//注册写事件 channel.register(this.selector, SelectionKey.OP_WRITE); }else if((key.readyOps()&SelectionKey.OP_WRITE)==SelectionKey.OP_WRITE){ SocketChannel channel = (SocketChannel) key.channel(); String msg = new String("Hello! nice to meet u"); channel.write(ByteBuffer.wrap(msg.getBytes()));
//注册读事件 channel.register(this.selector, SelectionKey.OP_READ); System.out.println("客户端发送信息::::"+msg); }else if((key.readyOps()&SelectionKey.OP_READ)==SelectionKey.OP_READ){ SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buff = ByteBuffer.allocate(1024); while(true){ buff.clear(); int r = channel.read(buff); if(r<=0){ break; } } byte[] data = buff.array(); System.out.println("客户端收到信息::"+new String(data).trim()); } } } } public static void main(String[] args) throws IOException { NIOClient n = new NIOClient(); n.init("localhost", 8002); n.listen(); } }