本来想写Socket和Sersocket的非阻塞形式的,结果发现水平不足,还是先复习下NIO。
Java NIO 由以下几个核心部分组成:
Channels
Buffers
Selectors
Channel 和 Buffer
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
这是在一个单线程中使用一个Selector处理3个Channel的图示:
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
Buffer的基本用法
1.写入数据到buffer
2.调用flip()方法
3.从buffer中读取数据
4.调用clear()或者compact()方法
为了理解Buffer的工作原理,需要熟悉它的三个属性:
capacity
position
limit
position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,capacity的含义总是一样的。
capacity
作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。
position
当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
//认识buffer capacity(总容量) position(指针) limit(实际大小)
//其他方法 mark()与reset()方法 标记和跳掉标记位置
public static void basebuffer() throws IOException{
RandomAccessFile aFile = new RandomAccessFile("d://niotest/base.txt", "rw");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buf =ByteBuffer.allocate(2);
int bytesRead = inChannel.read(buf);
while(bytesRead != -1){
buf.flip();//将buffer从读模式变为写模式 position变为0 limit变为position
while(buf.hasRemaining()){
System.out.println((char)buf.get());
}
buf.clear();
bytesRead = inChannel.read(buf);//再读缓冲池那么多
}
aFile.close();
}
//从buffer写到channel
public static void BufferTest(String data) throws IOException{
RandomAccessFile inFile =new RandomAccessFile("d://niotest/buffer.txt","rw");
Selector selector = Selector.open();
FileChannel inChannel = inFile.getChannel();
inChannel.write(ByteBuffer.wrap(new String(data).getBytes("utf-8")));
}
//分散 channel 将数据分散到多个buffer中
public static void scatter() throws IOException{
ByteBuffer head = ByteBuffer.allocate(6);
ByteBuffer body = ByteBuffer.allocate(20);
ByteBuffer [] bufferArray ={head,body};
RandomAccessFile aFile = new RandomAccessFile("d://niotest/base.txt", "rw");
FileChannel inChannel = aFile.getChannel();
long bytesRead = inChannel.read(bufferArray);
while(bytesRead>-1){
bufferArray[0].flip();
while(bufferArray[0].hasRemaining()){
System.out.println((char)bufferArray[0].get());
}
bufferArray[0].clear();
bufferArray[1].flip();
while(bufferArray[1].hasRemaining()){
System.out.println((char)bufferArray[1].get());
}
bufferArray[1].clear();
bytesRead = inChannel.read(bufferArray);
}
//通道传输 如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel传到另一个channel
public static void passStan() throws IOException{
RandomAccessFile inFile =new RandomAccessFile("d://niotest/base.txt","rw");
FileChannel inChannel = inFile.getChannel();
RandomAccessFile outFile = new RandomAccessFile("d://niotest/lalala.txt", "rw");
FileChannel outChannel = outFile.getChannel();
long position = 0;
long count = inChannel.size();
System.out.println("count:"+count);
outChannel.transferFrom(inChannel,position, count);
//在SoketChannel的实现中,SocketChannel只会传输此刻准备好的数据(可能不足count字节)。因此,SocketChannel可能不会将请求的所有数据(count个字节)全部传输到FileChannel中。
//transferFrom() 其他的传输到file中
//transferTo() file传输到其他中
}