NIO支持缓冲区和通道,效率非常高,非常好用,代码演示如下
1.NIO的HelloWorld
package cn.zzu.wcj.nio;
import static org.junit.Assert.*;
import java.nio.ByteBuffer;
import org.junit.Test;
/*
* 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
*
* 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
* ByteBuffer
* CharBuffer
* ShortBuffer
* IntBuffer
* LongBuffer
* FloatBuffer
* DoubleBuffer
*
* 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区
*
* 二、缓冲区存取数据的两个核心方法:
* put() : 存入数据到缓冲区中
* get() : 获取缓冲区中的数据
*
* 三、缓冲区中的四个核心属性:
* capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
* limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
* position : 位置,表示缓冲区中正在操作数据的位置。
*
* mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
*
* 0 <= mark <= position <= limit <= capacity
*
* 四、直接缓冲区与非直接缓冲区:
* 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
* 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
*/
public class BufferTest {
@Test
public void test1() {
String str="abcde" ;
//1.分配一个指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024) ;
System.out.println("-----------------allocate()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//2.利用put()方法存入数据
buf.put(str.getBytes()) ;
System.out.println("-----------------put()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//3.切换到读取模式
buf.flip() ;
System.out.println("-----------------flip()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//4.利用get()读取缓冲区中的数据
byte[] dst=new byte[buf.limit()] ;
buf.get(dst,buf.position(),buf.limit()) ;
System.out.println("-----------------get()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//5.rewind()可重复读
buf.rewind() ;
System.out.println("-----------------rewind()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//6.clear():清空缓冲区,但是缓冲区中的数据依然存在,但是处于‘被遗忘’状态
buf.clear() ;
System.out.println("-----------------clear()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
System.out.println((char)buf.get(0));
}
@Test
public void test2(){
String str="abcde" ;
ByteBuffer buf=ByteBuffer.allocate(1024) ;
byte[] source=str.getBytes() ;
buf.put(source, 0,2) ;
System.out.println("position="+buf.position());
buf.mark() ;
System.out.println("-------------mark()@2--------------");
buf.put(source, 2, 2) ;
System.out.println("position="+buf.position());
System.out.println("----------------reset()--------------------");
buf.reset() ;
System.out.println("position="+buf.position());
// System.out.println("len="+source.length);
if(buf.hasRemaining()){
System.out.println(buf.remaining());
}
}
@Test
public void test3(){
ByteBuffer buf=ByteBuffer.allocateDirect(1024) ;
assertSame(true, buf.isDirect());
}
}
2.创建Channel的几种方式
package cn.zzu.wcj.nio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.Test;
/*
* 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
*
* 二、通道的主要实现类
* java.nio.channels.Channel 接口:
* |--FileChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* 三、获取通道
* 1. Java 针对支持通道的类提供了 getChannel() 方法
* 本地 IO:
* FileInputStream/FileOutputStream
* RandomAccessFile
*
* 网络IO:
* Socket
* ServerSocket
* DatagramSocket
*
* 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
* 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
*
* 四、通道之间的数据传输
* transferFrom()
* transferTo()
*
* 五、分散(Scatter)与聚集(Gather)
* 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
* 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
*
* 六、字符集:Charset
* 编码:字符串 -> 字节数组
* 解码:字节数组 -> 字符串
*
*/
public class ChannelTest {
@Test
public void testEncDec() throws Exception{
Charset charset = Charset.forName("GBK") ;
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder() ;
CharBuffer charBuf=CharBuffer.allocate(1024) ;
charBuf.put("HelloWorld,世界你好!");
charBuf.flip() ;
ByteBuffer byteBuffer = encoder.encode(charBuf);
for(int x=0;x
System.out.print(byteBuffer.get()+"、");
}
System.out.println();
byteBuffer.flip() ;
CharBuffer charBuffer = decoder.decode(byteBuffer);
System.out.println(charBuffer.toString());
System.out.println("-----------------------------");
// Charset charset2 = Charset.forName("UTF-8") ;
Charset charset2 = Charset.forName("GBK") ;
byteBuffer.flip() ;
CharBuffer charBuffer2 = charset2.decode(byteBuffer) ;
System.out.println(charBuffer2.toString());
}
@Test
public void testCharset(){
Map charsets = Charset.availableCharsets() ;
Set> set = charsets.entrySet() ;
for(Entry e : set ){
System.out.println(e.getKey()+"="+e.getValue());
}
}
@Test
public void testScatterAndGather()throws Exception{
RandomAccessFile raf=new RandomAccessFile("1.txt", "rw") ;
FileChannel inChannel = raf.getChannel() ;
ByteBuffer buf=ByteBuffer.allocate(100) ;
ByteBuffer buf2=ByteBuffer.allocate(1024) ;
ByteBuffer bufs[]={buf,buf2} ;
inChannel.read(bufs) ;
for(ByteBuffer byteBuf : bufs){
byteBuf.flip() ; //切换到读取模式
}
System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
System.out.println("------------------------------------------------");
System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
RandomAccessFile raf2=new RandomAccessFile("2.txt", "rw") ;
FileChannel outChannel = raf2.getChannel() ;
outChannel.write(bufs) ;
outChannel.close();
inChannel.close();
raf.close();
raf2.close();
}
@Test
public void testChannel() throws Exception{
long start=System.currentTimeMillis() ;
FileInputStream fis=null ;
FileOutputStream fos=null ;
fis=new FileInputStream("1.jpg");
fos=new FileOutputStream("2.jpg") ;
//1.获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//2.准备缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024) ;
//3.读写
while(inChannel.read(buf) != -1){ //读
buf.flip() ; //切换到读取模式
outChannel.write(buf) ; //写
buf.clear() ; //清空缓冲区,准备再次读取
}
//4.关闭流
outChannel.close();
inChannel.close();
fos.close();
fis.close();
long end=System.currentTimeMillis() ;
System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒");
}
@Test
public void testDirectChannel()throws Exception{
long start=System.currentTimeMillis() ;
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ;
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE ) ;
//内存映射文件
MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()) ;
MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()) ;
//直接对缓冲区中的数据进行读写操作
byte[] temp=new byte[1024] ;
inMappedBuf.get(temp) ;
outMappedBuf.put(temp) ;
inChannel.close();
outChannel.close();
long end=System.currentTimeMillis() ;
System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒");
}
@Test
public void testTransform()throws Exception{
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ;
FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"), StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE ) ;
//inChannel.transferTo(0, inChannel.size(), outChannel) ;
outChannel.transferFrom(inChannel, 0, inChannel.size()) ;
inChannel.close();
outChannel.close();
}
}
3.阻塞式NIO
package cn.zzu.wcj.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import org.junit.Test;
/*
* 一、使用 NIO 完成网络通信的三个核心:
*
* 1. 通道(Channel):负责连接
*
* java.nio.channels.Channel 接口:
* |--SelectableChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* |--Pipe.SinkChannel
* |--Pipe.SourceChannel
*
* 2. 缓冲区(Buffer):负责数据的存取
*
* 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
*
*/
public class BlockingNIOTest {
@Test
public void testClient() throws Exception {
//1.创建通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
//2.准备缓冲区
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ;
ByteBuffer buf=ByteBuffer.allocate(1024) ;
//3.读取本地文件,发送到客户端
while(inChannel.read(buf) != -1){
buf.flip() ;
socketChannel.write(buf) ;
buf.clear() ;
}
//4.关闭通道
inChannel.close();
socketChannel.close();
}
@Test
public void testServer() throws Exception{
//1.创建通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open() ;
//2.绑定端口号
serverSocketChannel.bind(new InetSocketAddress(8989)) ;
//3.准备Channel和Buffer
FileChannel outChannel=FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE) ;
ByteBuffer buf=ByteBuffer.allocate(1024) ;
//4.接收客户端请求
SocketChannel socketChannel = serverSocketChannel.accept() ;
while(socketChannel.read(buf) != -1){
buf.flip() ;
outChannel.write(buf) ;
buf.clear() ;
}
//5.关闭流
socketChannel.close();
outChannel.close();
serverSocketChannel.close();
}
}
package cn.zzu.wcj.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import org.junit.Test;
public class BlockingNIOTestPlus {
@Test
public void testClent() throws Exception{
SocketChannel client = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999)) ;
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ;
ByteBuffer buf=ByteBuffer.allocate(1024) ;
while(inChannel.read(buf) != -1){
buf.flip() ;
client.write(buf) ;
buf.clear() ;
}
client.shutdownOutput() ; //结束输出
//接收服务器反馈
while(client.read(buf) != -1){
buf.flip() ;
System.out.println(new String(buf.array(),0,buf.limit()));
buf.clear() ;
}
inChannel.close() ;
client.close() ;
}
@Test
public void testServer() throws Exception{
ServerSocketChannel server = ServerSocketChannel.open() ;
server.bind(new InetSocketAddress(9999)) ;
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"),
StandardOpenOption.WRITE,
StandardOpenOption.CREATE ) ;
ByteBuffer buf=ByteBuffer.allocate(1024) ;
SocketChannel client = server.accept();
while(client.read(buf)!=-1){
buf.flip() ;
outChannel.write(buf) ;
buf.clear() ;
}
//发送反馈给客户端
buf.put("乖儿子,爸爸接收到黄图啦!!!".getBytes()) ;
buf.flip() ;
client.write(buf) ;
client.close();
outChannel.close();
server.close();
}
}
4.非阻塞式NIO
package cn.zzu.wcj.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import org.junit.Test;
/*
* 一、使用 NIO 完成网络通信的三个核心:
*
* 1. 通道(Channel):负责连接
*
* java.nio.channels.Channel 接口:
* |--SelectableChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* |--Pipe.SinkChannel
* |--Pipe.SourceChannel
*
* 2. 缓冲区(Buffer):负责数据的存取
*
* 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
*
*/
public class TestNonBlockingChannel {
@Test
public void testClient() throws Exception{
//创建客户端通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",6666)) ;
//配置为非阻塞式NIO
sChannel.configureBlocking(false) ;
//准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024) ;
Scanner sc=new Scanner(System.in) ;
while(sc.hasNext()){
buffer.put((new Date().toString()+" : "+sc.next()).getBytes()) ;
buffer.flip() ; //切换成读模式
//向服务器端发送消息
sChannel.write(buffer) ;
buffer.clear() ;
}
//关闭通道
sChannel.close();
}
@Test
public void testServer() throws Exception{
//1.创建服务器端通道
ServerSocketChannel ssChannel = ServerSocketChannel.open() ;
//2.绑定端口号
ssChannel.bind(new InetSocketAddress(6666)) ;
//3.设置非阻塞模式
ssChannel.configureBlocking(false) ;
//4.获取选择器
Selector selector = Selector.open() ;
//5.将选择器注册到通道上
ssChannel.register(selector,SelectionKey.OP_ACCEPT) ;
//6.以轮训的方式获取选择器上已经准备就绪的事件
while(selector.select() > 0){
//7.接收全部选择键
Iterator iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
//8.接收选择键
SelectionKey selectionKey = iterator.next();
//9.根据键值判断具体是什么事件
if(selectionKey.isAcceptable()){
//10.接收就绪,获取客户端连接
SocketChannel sChannel = ssChannel.accept() ;
//11.切换到非阻塞模式
sChannel.configureBlocking(false) ;
//12.将通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ) ;
}else if(selectionKey.isReadable()){
//13.获取读状态的通道
SocketChannel sChannel=(SocketChannel) selectionKey.channel() ;
ByteBuffer dst=ByteBuffer.allocate(1024) ;
//14.读取数据
Integer length=0 ;
while( (length=sChannel.read(dst))> 0){
dst.flip() ;
System.out.println(new String(dst.array(),0,length));
dst.clear() ;
}
}
//15.取消选择键
iterator.remove();
}
}
}
}
package cn.zzu.wcj.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import org.junit.Test;
public class TestNonBlockingNIOPlus {
@Test
public void testSend() throws Exception{
DatagramChannel dc=DatagramChannel.open() ;
dc.configureBlocking(false) ;
ByteBuffer buf=ByteBuffer.allocate(1024) ;
Scanner sc=new Scanner(System.in) ;
while(sc.hasNext()){
String msg=sc.next() ;
buf.put((new Date().toString()+" : "+msg).getBytes()) ;
buf.flip() ;
dc.send(buf, new InetSocketAddress("127.0.0.1",9999)) ;
buf.clear() ;
}
dc.close();
}
@Test
public void testReceive() throws Exception{
DatagramChannel dc = DatagramChannel.open() ;
dc.configureBlocking(false) ;
dc.bind(new InetSocketAddress(9999)) ;
Selector selector = Selector.open() ;
dc.register(selector, SelectionKey.OP_READ) ;
while(selector.select()>0){
Iterator iterator = selector.selectedKeys().iterator() ;
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
ByteBuffer buf=ByteBuffer.allocate(1024) ;
dc.receive(buf) ;
buf.flip() ;
System.out.println(new String(buf.array(),0,buf.limit()));
buf.clear() ;
}
}
iterator.remove();
}
}
}
5.管道
package cn.zzu.wcj.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;
import java.nio.channels.Pipe.SinkChannel;
import java.nio.channels.Pipe.SourceChannel;
import org.junit.Test;
public class PipeTest {
@Test
public void testPipe() throws Exception{
//1.获取管道
Pipe pipe = Pipe.open() ;
SinkChannel sinkChannel = pipe.sink();
//2.准备缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024) ;
buf.put("单向管道发送数据".getBytes()) ;
buf.flip() ;
//4.发送数据
sinkChannel.write(buf) ;
buf.clear() ;
//5.接收数据
SourceChannel sourceChannel = pipe.source() ;
sourceChannel.read(buf) ;
buf.flip() ;
System.out.println(new String(buf.array(),0,buf.limit()));
}
}