View on GitHub

个人笔记

SongPinru 的小仓库

Buffer

  • IntBuffer
  • FloatBuffer
  • CharBuffer
  • DoubleBuffer
  • ShortBuffer
  • LongBuffer
  • ByteBuffer

指针

  • position:当前指针
  • limit:可用的末尾指针(超过部分不可访问)
  • capacity:容量,不可变
  • mark:标记,可以使用reset回到该位置

方法:

  • put:增加数据,position后移,增加后不可超过limit
  • flip:翻转指针,limit=position,position=0
  • get:读取数据,position后移,读取不可超过limit
  • rewind:position重置为0
  • mark:标记,配合reset回到该位置
  • clear:position置0,limit=capacity

ByteBuffer:

  • slice:切片

Channel

  • FileChannel
  • SocketChannel
  • ServerSocketChannel
  • DatagramChannel

获取:

  1. 使用getChannel()方法
  • FileInputStream/FileOutputStream
  • RandomAccessFile
  • Socket
  • ServerSocket
  • DatagramSocket
  1. open()方法

  2. Files.newByteChannel()

FileChannel

零拷贝

FileChannel inChannel = FileChannel.open(Paths.get("/path/to/src"));
FileChannel outChannel = FileChannel.open(Paths.get("/path/to/destination"));
inChannel.transferTo(0,inChannel.size(),outChannel);

常用类:

  • Files,针对文件的所有操作
  • Path,Paths
  • StandardOpenOption: 操作描述符
  • FileSystems

方法:

  • read:
    • 分散读取(有多个buffer)
    • ==read之后一定要flip==
    • read前不clear,数据会重复
  • write:
    • ==write前一定要flip==
    • 聚集写入(有多个buffer)
  • size:这个channel的size,即文件大小
  • position:当前Channel的偏移(offset)
  • force:强制刷新到磁盘
  • transferTo:把数据导入另一个Channel,可以实现零拷贝,mmap+write
    • direct(零拷贝) -> mapped -> jvm内存
    • direct零拷贝最大写入2G
    • transferTo一次最大写入2G,如果使用内存映射,此处内存映射限制是8M,while循环,直到2G拷完)
    • 如果是socket,最大写入8M(SelChImpl的子类,执行一次会自动跳出),也就是说除了file都是2G
    • 返回值为本次写入的字节数(scoket最大8M,file最大2G)
  • transferFrom:从另一个channel导入
    • 不能实现零拷贝
    • 如果是file2file,使用内存映射(8M)
    • 如果不是file2file,使用堆内内存
    • 没有读取限制,可以一次读完
    • 如果是scoket,这里的position是当前的,读取过的不算
  • map:内存文件映射,网上有用来做零拷贝,但是最大限制2G,可以用来做修改文件
  • truncate:截断,保留相应大小的前面部分,后面丢弃
  • lock:给文件加锁,阻塞
  • tryLock:给文件加锁,非阻塞,不成功返回null

Charset:

Charset charset = Charset.forName("UTF-8");
CharBuffer allocate = CharBuffer.allocate(1024);
ByteBuffer encode = charset.encode(allocate);
CharBuffer decode = charset.decode(encode);

Selector

Selector是操作系统帮我们管理socket的文件描述符的管理器,把channel注册上去就可以交给系统管理了

selector只接受非阻塞的channel

select方法阻塞实际上是底层SelectionKey阻塞,如果select方法执行时没有的key,是不会触发唤醒的。

使用wakeup唤醒

  • keys:注册的key的集合
  • selectedKeys:有事件的key的集合,也就是keys的子集
    • 这里的keys显示的是已经触发的,如果没有手动删除的话,会包含之前触发的key(就算删了新触发的也会写进去),但是不会重复触发
  • interestOps:注册的事件选项
  • readyOps:已触发的事件选项

SocketChannel

SocketChannel client = SocketChannel.open();
client.bind(null);//绑定本地端口,null为自动绑定,
client.connect(InetSocketAddress.createUnresolved("localhost",8080));
client.configureBlocking(false);//非阻塞
Selector selector = Selector.open();
SelectionKey selectionKey = client.register(selector, SelectionKey.OP_CONNECT);
//selectionKey.attach()//附加对象
//selectionKey.attachment()//获取附加对象
selectionKey.interestOps(SelectionKey.OP_ACCEPT);//等于注册
selectionKey.interestOpsAnd(SelectionKey.OP_READ);//条件做AND
selectionKey.interestOpsOr(SelectionKey.OP_WRITE);//OR

selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
selectionKey.isValid();

selectionKey.readyOps();//就绪的ops集合,即注册的ops

ServerSocketChannel

ServerSocketChannel server = ServerSocketChannel.open();
server.bind(InetSocketAddress.createUnresolved("localhost",8080));
server.configureBlocking(false);//非阻塞
// Selector selector = Selector.open();
server.register(selector,SelectionKey.OP_ACCEPT);


SocketChannel socketChannel = server.accept();//获取通道
socketChannel.configureBlocking(false);//非阻塞

常用类

  • StandardProtocolFamily:网络协议(ipv4 ipv6)

DatagramChannel

DatagramChannel datagramChannel = DatagramChannel
        .open(StandardProtocolFamily.INET)
        .bind(InetSocketAddress.createUnresolved("localhost", 8080));
datagramChannel.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
datagramChannel.receive(byteBuffer);
datagramChannel.send(byteBuffer,InetSocketAddress.createUnresolved("localhost",8080));
datagramChannel.register(selector,SelectionKey.OP_ACCEPT);

Pipe

Pipe是线程间的管道,用来异步通信

Pipe pipe = Pipe.open();
Pipe.SourceChannel source = pipe.source();
Pipe.SinkChannel sink = pipe.sink();
source.keyFor(selector);
source.register(selector,SelectionKey.OP_ACCEPT);
source.configureBlocking(false);
sink.keyFor(selector);
ByteBuffer buffer = ByteBuffer.allocate(1024);
source.read(buffer);
sink.write(buffer);