java——nio(学习笔记)
buffer
public static void method1(){
RandomAccessFile aFile = null;
try{
aFile = new RandomAccessFile("src/nio.txt","rw");
FileChannel fileChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
int bytesRead = fileChannel.read(buf);
System.out.println(bytesRead);
while(bytesRead != -1)
{
buf.flip();
while(buf.hasRemaining())
{
System.out.print((char)buf.get());
}
buf.compact();
bytesRead = fileChannel.read(buf);
}
}catch (IOException e){
e.printStackTrace();
}finally{
try{
if(aFile != null){
aFile.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
我们可以从代码中大体猜到
- buffer需要分配空间
- 从channel中write buffer中
- buffer调用flip方法转变为read模式
- 调用buffer到get方法进行读操作
- 调用compact方法或者clean方法进行对buffer对清除
以上图就是对buffer对写和读。
写模式中就随着数据的写入,position的位置会跟着改变。
如果转变到读模式,limit会跳转到position到位置,position会跳到最开始到地方。
如果进行compact到话,只是改变limit到位置到capacity的位置,而不对buffer里面的数据进行清除。
channel
serverSocket = new ServerSocket(8080);
int recvMsgSize = 0;
byte[] recvBuf = new byte[1024];
while(true){
Socket clntSocket = serverSocket.accept();
SocketAddress clientAddress = clntSocket.getRemoteSocketAddress();
System.out.println("Handling client at "+clientAddress);
in = clntSocket.getInputStream();
while((recvMsgSize=in.read(recvBuf))!=-1){
byte[] temp = new byte[recvMsgSize];
System.arraycopy(recvBuf, 0, temp, 0, recvMsgSize);
System.out.println(new String(temp));
}
}
使用bio的写法,就是创建serverSocket,并在循环中调用accept()方法,等待连接。这里的等待是阻塞的,即线程会被挂起,直到有连接才会对线程进行唤醒。
如果使用NIO的方法
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...
}
}
我们设置了configureBlocking为false的话,serverSocketChannel.accept() 马上会有返回值。
没有连接的话,则是null。所以我们也需要在循环中对这个方法进行调用,和bio不同的是,线程不会被挂起,但是资源会被浪费。
selector
Selector selector = null;
ServerSocketChannel ssc = null;
try{
selector = Selector.open();
ssc= ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(PORT));
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(TIMEOUT) == 0){
System.out.println("==");
continue;
}
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()){
SelectionKey key = iter.next();
if(key.isAcceptable()){
handleAccept(key);
}
if(key.isReadable()){
handleRead(key);
}
if(key.isWritable() && key.isValid()){
handleWrite(key);
}
if(key.isConnectable()){
System.out.println("isConnectable = true");
}
iter.remove();
}
}
}
这里的ServerSocketChannel注册进了selector中并且注册了对连接“感兴趣”,一个selector可以拥有多个channel。
但是selector的accept()该方法会阻塞等待,直到有一个或更多的信道准备好了I/O操作或等待超时。
select()方法将返回可进行I/O操作的信道数量。
在一个单独的线程中,通过调用select()方法就能检查多个信道是否准备好进行I/O操作。
如果经过一段时间后仍然没有信道准备好,select()方法就会返回0,并允许程序继续执行其他任务。
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
访问“已选择键集(selected key set)”中的就绪通道。
对这个键集进行遍历,并处理相映对io事件。