NIO核心组件
- Buffer 缓冲区
- Channel 通道
- Selector 选择器
1、Buffer缓冲区
Buffer工作原理
ByteBuffer的简单使用
通过下面这个简单代码可以深入理解上面三个参数在每次读或写之后的变化
public class BufferDemo {
public static void main(String[] args) {
// 初始化buffer容量为4字节
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
// 刚初始化时查看buffer三个重要指标的值
System.out.println("未写入数据前=============");
System.out.println("position==" + byteBuffer.position());
System.out.println("limit==" + byteBuffer.limit());
System.out.println("capacity==" + byteBuffer.capacity());
// 写入三字节数据
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
byteBuffer.put((byte) 3);
// 再次查看三个指标的值
System.out.println("写入数据后===========");
System.out.println("position==" + byteBuffer.position());
System.out.println("limit==" + byteBuffer.limit());
System.out.println("capacity==" + byteBuffer.capacity());
// 转换为读取模式
byteBuffer.flip();
System.out.println("转换为读取模式后===========");
System.out.println("position==" + byteBuffer.position());
System.out.println("limit==" + byteBuffer.limit());
System.out.println("capacity==" + byteBuffer.capacity());
// 读取两个数据
System.out.println("读取两个数据:");
System.out.println(byteBuffer.get());
System.out.println(byteBuffer.get());
System.out.println("读取两个数据后===========");
System.out.println("position==" + byteBuffer.position());
System.out.println("limit==" + byteBuffer.limit());
System.out.println("capacity==" + byteBuffer.capacity());
// 继续写入数据
byteBuffer.compact(); // 转换为写模式
System.out.println("转换为写入模式后===========");
System.out.println("position==" + byteBuffer.position());
System.out.println("limit==" + byteBuffer.limit());
System.out.println("capacity==" + byteBuffer.capacity());
byteBuffer.put((byte) 4);
byteBuffer.put((byte) 5);
byteBuffer.put((byte) 6);
System.out.println("写入三个数据后===========");
System.out.println("position==" + byteBuffer.position());
System.out.println("limit==" + byteBuffer.limit());
System.out.println("capacity==" + byteBuffer.capacity());
byteBuffer.flip();
System.out.println(byteBuffer.get());
}
}
ps:关于ByteBuffer
2、Channel通道
客户端SocketChannel
服务器端ServerSocketChannel
3、Selector选择器
Selector原理
NIO与BIO对比
NIO的使用案例
1、客户端:
public class NIOClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8001));
while (!socketChannel.finishConnect()) {
// 没有连接上,就一直等待
Thread.yield();
}
Scanner sc = new Scanner(System.in);
System.out.println("enter: ");
String msg = sc.nextLine();
ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
while (byteBuffer.hasRemaining()) {
socketChannel.write(byteBuffer);
}
System.out.println("received response from server: ");
ByteBuffer resBuffer = ByteBuffer.allocate(1024);
while (socketChannel.isOpen() && socketChannel.read(resBuffer) != -1) {
if (resBuffer.position() > 0) break;
}
// 读取响应
resBuffer.flip();
byte[] content = new byte[resBuffer.limit()];
resBuffer.get(content);
System.out.println(new String(content));
sc.close();
socketChannel.close();
}
}
2、服务器端
public class NIOServerV2 {
public static void main(String[] args) throws IOException {
// 创建网络服务端Channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 创建一个Selector,将channel注册上去
Selector selector = Selector.open();
SelectionKey selectKey = serverSocketChannel.register(selector, 0, serverSocketChannel);
// 注册serverSocketChannel感兴趣的事件:accept (serverSocketChannel只能支持accept操作)
selectKey.interestOps(SelectionKey.OP_ACCEPT);
// 开启服务器
serverSocketChannel.socket().bind(new InetSocketAddress(8001));
System.out.println("server started.......");
while (true) {
// 轮询事件,有就返回,没有就阻塞
selector.select();
// 获取事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
// 遍历事件集合
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
// 获取事件
SelectionKey key = iterator.next();
iterator.remove();
// 如果是Accept事件
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.attachment();
// 接受连接
SocketChannel clientSocketChannel = server.accept();
clientSocketChannel.configureBlocking(false);
// 将客户端channel注册到selector上
clientSocketChannel.register(selector, SelectionKey.OP_READ, clientSocketChannel);
System.out.println("accepted new connection from " + clientSocketChannel.getRemoteAddress());
}
// 如果是Read事件
if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.attachment();
ByteBuffer requestBuffer = ByteBuffer.allocate(1024);
while (socketChannel.isOpen() && socketChannel.read(requestBuffer) != -1) {
// 这里只要读到了数据,我们认为请求就结束了
if (requestBuffer.position() > 0) break;
}
// 如果没有数据,就不进行后面的操作
if (requestBuffer.position() == 0) continue;
// 开启读取模式
requestBuffer.flip();
byte[] content = new byte[requestBuffer.limit()];
requestBuffer.get(content);
System.out.println(new String(content));
System.out.println("received data from " + socketChannel.getRemoteAddress());
// TODO 业务操作 数据库、接口调用等
// 响应数据
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 11\r\n\r\n" +
"hello world";
ByteBuffer respBuffer = ByteBuffer.wrap(response.getBytes());
while (respBuffer.hasRemaining()) {
socketChannel.write(respBuffer);
}
}
}
selector.selectNow();
}
}
}