NIO:Channel,Buffer、Selector编程
程序员文章站
2022-05-04 10:07:41
...
NIO简介
NIO示例代码
Buffer
public class BufferDemo1 {
public static void main(String[] args) {
// 创建一个ByteBuffer对象
// ByteBuffer本身是一个抽象类,不允许直接创建对象
// ByteBuffer中的构造方法都是默认的,那么不对外提供
// 需要传入一个参数表示容量
// ByteBuffer底层是基于数组来实现的
ByteBuffer buffer = ByteBuffer.allocate(10);
// int capacity = buffer.capacity();
// System.out.println(capacity);
// 缓冲区刚创建的时候,position指向第0位
// System.out.println(buffer.position());
// 添加元素
buffer.put("abc".getBytes());
buffer.put("def".getBytes());
// System.out.println(buffer.position());
// 需要将position挪到指定位置
// 归零
// buffer.position(0);
// 获取元素
// 返回值表示单个字节
// byte b = buffer.get();
// System.out.println(b);
// System.out.println(buffer.get());
// 如果需要遍历添入的元素,需要先将limit挪到position位置上
// 然后将position归零
// 这个操作称之为翻转缓冲区
// buffer.limit(buffer.position());
// buffer.position(0);
// 等价于
buffer.flip();
// while (buffer.position() < buffer.limit()) {
// 等价于
while (buffer.hasRemaining()) {
System.out.println(buffer.get());
}
}
}
public class BufferDemo2 {
public static void main(String[] args) {
// 这种方式适合于数据已知的场景
// ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes());
// System.out.println(buffer.position());
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put("hello".getBytes());
buffer.flip();
// 获取buffer底层的字节数组
byte[] data = buffer.array();
System.out.println(
new String(data, 0, buffer.limit()));
}
}
Channel
public class Client {
public static void main(String[] args) throws Exception {
// 开启客户端通道
SocketChannel sc = SocketChannel.open();
// 如果需要非阻塞,需要手动设置
sc.configureBlocking(false);
// 发起连接
// 如果设置为非阻塞,那么无论连接是否建立,都会继续往下运行
sc.connect(new InetSocketAddress("localhost", 8090));
// 判断是否连接上
while (!sc.isConnected())
// 如果没有连接上,应该试图再次连接
// finishConnect方法底层会自动计数
// 如果多次连接为连接上,则会认为这个连接是无法建立的
// 此时会报错
sc.finishConnect();
// 写数据
// 数据需要封装成ByteBuffer
sc.write(ByteBuffer.wrap("hello server".getBytes()));
// 关流
sc.close();
}
}
public class Server {
public static void main(String[] args) throws Exception {
// 开启服务器端通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 绑定监听端口
ssc.bind(new InetSocketAddress(8090));
// 设置为非阻塞
ssc.configureBlocking(false);
// 接收连接
// 无论是否接收到了连接都会继续往下执行
SocketChannel sc = ssc.accept();
// 保证能接到连接
while (sc == null)
sc = ssc.accept();
// 读数据
// 准备一个缓冲区用于存储数据
ByteBuffer dst = ByteBuffer.allocate(1024);
sc.read(dst);
System.out.println(new String(dst.array(), 0, dst.position()));
// 关流
ssc.close();
}
}
Selector
public class Client {
public static void main(String[] args) throws Exception {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8090));
sc.write(ByteBuffer.wrap("hello server".getBytes()));
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array(), 0, buffer.position()));
sc.close();
}
}
public class Server {
public static void main(String[] args) throws Exception {
// 开启服务器端的通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 绑定监听端口
ssc.bind(new InetSocketAddress(8090));
// 将通道设置为非阻塞
ssc.configureBlocking(false);
// 开启选择器
Selector selector = Selector.open();
// 将通道注册到选择器上
ssc.register(selector, SelectionKey.OP_ACCEPT);
// 用死循环表示服务器启动一直运行
while (true) {
// 进行选择,将有操作的连接选择出来
selector.select();
// 获取这次选择之后会产生的事件
Set<SelectionKey> keys = selector.selectedKeys();
// 遍历Set集合,根据不同的事件进行不同的处理
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
// 拿到事件之后,需要对事件类型进行判断
// 可接受事件
if (key.isAcceptable()) {
// 通过这个事件获取到对应的通道
ServerSocketChannel sscx =
(ServerSocketChannel) key.channel();
// 接收连接
SocketChannel sc = sscx.accept();
System.out.println("连接成功~~~");
// 将sc设置为非阻塞
sc.configureBlocking(false);
// 注册读或者写事件
// 既需要注册读有需要注册写
// 每一次register都会将之前的事件覆盖
// sc.register(selector, SelectionKey.OP_WRITE + SelectionKey.OP_READ);
sc.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
}
// 可读事件
if (key.isReadable()) {
// 从事件中获取通道
SocketChannel sc = (SocketChannel) key.channel();
// 准备一个缓冲区用于存储数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取数据
sc.read(buffer);
// 解析数据
System.out.println(
new String(buffer.array(), 0, buffer.position()));
// 需要将可读事件从通道上移除
// 获取这个通道上所有的事件,抠掉read事件
// sc.register(selector, key.interestOps() - SelectionKey.OP_READ);
sc.register(selector, key.interestOps() ^ SelectionKey.OP_READ);
}
// 可写事件
if (key.isWritable()) {
// 从事件中获取通道
SocketChannel sc = (SocketChannel) key.channel();
// 写出数据
sc.write(ByteBuffer.wrap("hello client".getBytes()));
// 需要将可写事件从通道上移除
sc.register(selector, key.interestOps() ^ SelectionKey.OP_WRITE);
}
// 一大类事件处理完成之后,需要将事件移除
// 这个remove只是从Set集合中移除,表示这一类事件处理完了
// 不是从通道上移除
it.remove();
}
}
}
}
上一篇: 微信站前端开发注意点?
下一篇: 推荐10篇关于format函数的课程