小小千想和您聊一聊

当前位置: 首页> 技术分享> Java编程之nio和bio

Java编程之nio和bio

  1,NIO vs BIO


  2,NIO的工作模式

  NIO有三大核心:

  1. Channel(通道)

  2. Buffer(缓冲区)

  3. Selector(选择器) NIO基于Channel和Buffer进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

  Channel提供了从文件,网络读取数据的通道,但是读或写数据,都必须经过Buffer,如下图所示:

  Selector用于监听多个通道的事件,从而达到一个线程监听多个客户端通道的效果。即多路复用技术。

  3,文件IO的核心API

  3.1 Buffer


  3.2 Channel

  4,文件IO-编程实战

  4.1 采用NIO写文件

@Test
public void writeTest() throws Exception {
//1.创建一个输出流
FileOutputStream fileOutputStream = new FileOutputStream("nio.txt");
//2.通过输出流创建一个通道
FileChannel channel = fileOutputStream.getChannel();
//3.创建一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//4.将字节数据写入到缓冲区中buffer.put("hello,nio".getBytes());
//5.注意,此时缓冲区的指针已经随着放入的数据而发生偏移
//所以,需要重置
buffer.flip();
//6.将缓冲区的数据写入通道,通道负责将数据写入到文件中channel.write(buffer);
//7.关闭资源fileOutputStream.close();
}


  4.2 采用NIO读文件

@Test
public void readTest() throws IOException {
//1.创建一个输入流
FileInputStream fileInputStream = new FileInputStream("nio.txt");
//2.通过输入流创建一个通道
FileChannel channel = fileInputStream.getChannel();
//3.创建一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//4.从通道中读取数据,并写入到缓冲区中channel.read(buffer);
//5.输出读取到的数据
System.out.println(new String(buffer.array()));
//6.关闭资源fileInputStream.close();
}


  4.3 完成文件的复制

@Test
public void copyTest() throws Exception{
//1.创建输入流和输出流
FileInputStream fileInputStream = new FileInputStream("nio.txt"); FileOutputStream fileOutputStream = new FileOutputStream("nio_copy.txt");
//2.通过流创建两个通道
FileChannel source = fileInputStream.getChannel(); FileChannel target = fileOutputStream.getChannel();
//3.通过通道实现快速拷贝
//target.transferFrom(source,0,source.size()); source.transferTo(0,source.size(),target);
//4.关闭资源
fileOutputStream.close(); fileInputStream.close();
}


  5,网络IO

  5.1 工作模式

  NIO的主要应用场景就是网络IO,网络IO的通道是非阻塞式的,基于事件驱动,适合于大量连接,但数据交换量不大的情况。 比如即时通讯。

  包括4个关键API

  1. Selector

  2. SelectionKey

  3. ServerSocketChannel

  4. SocketChannel

  5.2 Selector

  通过选择器可以检测多个注册的通道是否有事件发生,如果有事件发生,便捕获事件并进行处理。

  这样就可以达到一个线程处理多个客户端连接的效果

  5.3 SelectionKey

  代表Selector和网络通道的注册关系。

  1. OP_ACCEPT:表示有新的网络连接可以accept

  2. OP_CONNECT:表示连接已经建立

  3. OP_READ:表示读操作

  4. OP_WRITE:表示写操作

  5.4 ServerSocketChannel

  服务端监听新的客户端socket连接

  5.5 SocketChannel

  网络通道,负责具体的读写操作。 将缓存区的数据写入通道,或者从通道将数据读到缓冲区。

  6,编码实战

  6.1 目标

  实现客户端和服务端之间的网络通信

  6.2 客户端程序

/**
* @author huangguizhao
*/
public class NIOClient {

public static void main(String[] args) throws IOException {
//1.创建一个网络通道
SocketChannel channel = SocketChannel.open();
//2.设置该通道为非阻塞channel.configureBlocking(false);
//3.设置连接的服务端IP及端口
InetSocketAddress address = new InetSocketAddress("127.0.0.1",6666);
//4.连接服务器
//如果连接不上服务器,则尝试重复连接
if(!channel.connect(address)){//没连接成功
while (!channel.finishConnect()){
//等待连接,体现NIO的非阻塞优势
System.out.println("客户端在连接服务端的同时,还可以做其他的事");
}
}
//5.创建一个缓存区,用于存放发送的数据
ByteBuffer buffer = ByteBuffer.wrap("hello,nio server".getBytes());
//6.发送数据channel.write(buffer);

//7.避免服务端程序结束,设置阻塞System.in.read();
}
}

  6.3 服务端程序

/**
* @author huangguizhao
*/
public class NIOServer {

public static void main(String[] args) throws IOException {
//1.创建ServerSocketChannel对象
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.绑定监听的端口号
serverSocketChannel.bind(new InetSocketAddress(6666));
//3.设置为非阻塞式
serverSocketChannel.configureBlocking(false);
//4.创建一个Selector对象
Selector selector = Selector.open();
//5.把ServerSocketChannel对象注册给Selector对象
//同时关注连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//6.Selector开始监控while (true){
//每隔1秒钟查看是否可处理的事件if(selector.select(1000) == 0){
//非阻塞的体现
System.out.println("当前没有事件需要处理,服务器可以做点其他事");
continue;
}

//获取需要处理的事件,因为可能存在多个客户端的事件需要处理,所以返回的是集合Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
//开始循环处理事件
while (keyIterator.hasNext()){
//获取到事件
SelectionKey key = keyIterator.next();
//根据不同的事件,做不同的处理if(key.isAcceptable()){
System.out.println("有新的客户端连接	");
SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false);
//将当前的channel注册到Selector上,监听可读事件
socketChannel.register(selector,SelectionKey.OP_READ);
}

if(key.isReadable()){
SocketChannel socketChannel = (SocketChannel) key.channel();
//创建一个缓存区对象
ByteBuffer buffer = ByteBuffer.allocate(1024);
//将通道的数据读取到缓冲区socketChannel.read(buffer);
//输出结果
System.out.println(new String(buffer.array()));
}

//关键一步
//需要将当前的key移除,避免重复处理
keyIterator.remove();
}
}
}
}

上一篇:HTML5工具初识之网页编辑器

下一篇:java编程之冒泡排序(Bubble Sort)

QQ技术交流群

千锋Java开发官方①群
811099962

加入群聊