本文共 3923 字,大约阅读时间需要 13 分钟。
在Nio编程中,Channel、Buffer和Selector是核心组件,它们共同构成了Nio的非阻塞I/O模型。
Channel 是数据在Nio中流动的核心载体。与传统的输入输出流不同,Channel 是双向的数据通道,可以同时进行读写操作。例如,Client可以通过Channel与Server进行数据交互。
在数据传输过程中,数据需要暂时存储。Buffer 作为缓冲区,用于临时存储从Channel读取或写入的数据。常见的Buffer实现是 ByteBuffer。
Selector 的作用是管理多个Channel。当一个Channel发生读写事件时,Selector会通知线程进行处理。这种机制避免了传统线程模型中单个线程处理多个客户端连接的问题,提高了效率。
ByteBuffer 是Nio中最常用的缓冲对象。它的使用需要遵循特定的步骤和模式。
ByteBuffer有三个重要属性:
channel.read(buffer);buffer.flip();buffer.get();buffer.clear(); 或 buffer.compact();读取数据:
buffer.get();buffer.get(Byte[] byte);buffer.rewind();buffer.make(); + buffer.reset();写入数据:
buffer.put();channel.write(buffer);在实际开发中,经常需要将字符串与ByteBuffer之间进行转换。
// 字符串转换为ByteBufferByteBuffer buffer = ByteBuffer.allocate(16);buffer.put("hello".getBytes());buffer.flip();// 读取数据System.out.println(buffer.get()); // 输出"h"// ByteBuffer转换为字符串String str = StandardCharsets.UTF_8.decode(buffer).toString(); ###黏包半包问题处理
当网络数据传输过程中出现黏包或丢包时,可以通过ByteBuffer进行重新组合。
public class ByteBufferContestTest { private static ByteBuffer messageBuffer; public static void main(String[] args) { messageBuffer = ByteBuffer.allocate(20); ByteBuffer buffer = ByteBuffer.allocate(32); buffer.put("Hello,World\nI'm hushang\nHo".getBytes()); split2(buffer); buffer.put("w are you?\n".getBytes()); split2(buffer); } private static void split2(ByteBuffer buffer) { buffer.flip(); for (int i = 0; i < buffer.limit(); i++) { if (buffer.get(i) == '\n') { int length = i + 1 - buffer.position(); ByteBuffer target = ByteBuffer.allocate(length); for (int j = 0; j < length; j++) { target.put(buffer.get(j)); } target.flip(); System.out.println(StandardCharsets.UTF_8.decode(target).toString()); } } buffer.compact(); }} FileChannel可以通过FileInputStream、FileOutputStream或RandomAccessFile获取。
// 读取文件ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);// 写入文件buffer.flip();channel.write(buffer);
实现文件传输可以使用transferTo()方法。
long size = channel1.size();long left = size;while (left > 0) { left -= channel1.transferTo(left, left, channel2);} Path和Files类用于处理文件路径和文件操作。
Path source = Paths.get("D:/aaa/bb...");System.out.println(path.normalize()); // 创建目录Files.createDirectory(Paths.get("E:/1bbb/测试创建文件夹"));// 拷贝文件Files.copy(Path source, Path target);// 移动文件Files.move(Path source, Path target, StandardCopyOption.ATOMIC_MOVE);// 删除文件Files.delete(Path path); 传统的Socket编程是基于阻塞模式的。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));ListsocketChannelList = new ArrayList<>();while (true) { SocketChannel socketChannel = serverSocketChannel.accept(); socketChannelList.add(socketChannel); System.out.println("客户端成功连接服务器!!!");}
Nio提供了非阻塞编程模式,避免了单线程处理多个连接的问题。
Selector selector = Selector.open();selector.register(channel, new ByteBuffer());while (!selector.isClosed()) { try { int n = selector.select(); for (int i = 0; i < n; i++) { SelectionKey key = selector.selectedKeys()[i]; if (key.isReadable()) { ByteBuffer buffer = (ByteBuffer) key.attachment(); buffer.flip(); int bytesRead = channel.read(buffer); if (bytesRead > 0) { System.out.println("读取了" + bytesRead + "字节数据"); } } } } catch (IOException e) { e.printStackTrace(); }} 转载地址:http://opcfk.baihongyu.com/