NIO核心的三大组件
- Channels
- Buffers
- Selectors
Channel
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
socketChannel 的非阻塞模式
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
while(! socketChannel.finishConnect() ){
//wait,&nbs***bsp;do something else...
} ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel != null){
//do something with socketChannel...
}
} ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept() 方***立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null datagramChannel UDP在数据传送方面没有任何保证
接收数据
ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); channel.receive(buf);发送数据
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80)); Buffer
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
- MappedByteBuffer (内存映射文件)
- 写入数据到buffer
- 调用 flip 方法从写模式切换到读模式
- 从buffer中读取数据
- 调用clear 或者 compact方法 清除数据 , clear是清除所有数据,compact是清除已读数据,未读的数据会移动到buffer起始位置
allocate 分配缓冲区
rewind 将position置为0 ,重新读取数据
mark 标记一个指定的position
reset 将position 重置回到mark标记的位置
Selector
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方***一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件
Selector selector = Selector.open() 创建选择器
向selector 注册通道
channel.configureBlocking(false); SelectionKey key = channel.register(selector,Selectionkey.OP_READ);与 selector 一起使用,channel必须是非阻塞的,这意味着 FileChannel 不能和 selector 一起使用,
第二个参数 是 interest 集合,标识监听的感兴趣的事件
- SelectionKey.OP_CONNECT 连接就绪
- SelectionKey.OP_ACCEPT 接收就绪
- SelectionKey.OP_READ 读就绪
- SelectionKey.OP_WRITE 写就绪
SelectionKey
注册完之后就会返回一个selectionKey对象,
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT; boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT; boolean isInterestedInRead = interestSet & SelectionKey.OP_READ; boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;ready集合
selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();附加对象
selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();监听就绪事件
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。 分散和聚集 (Scatter / Gather)
分散就是一个channel 写到 多个buffer
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray); 聚集就是多个buffer 写入同一个 channel
通道之间数据传输
transferFrom
transferTo
管道 Pipe
向管道写数据,需要用到sinkChannel
Pipe.SinkChannel sinkChannel = pipe.sink();
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
sinkChannel.write(buf);
} 从管道读取数据,需要用到SourceChannelPipe.SourceChannel sourceChannel = pipe.source(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = sourceChannel.read(buf);

京公网安备 11010502036488号