NIO模型的代码演示如下:
//NIO服务端
public class Test0226_NIOServer {
public static void main(String[] args) throws IOException {
//创建ServerSocketChannel实例
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
//绑定端口号
serverSocketChannel.bind(new InetSocketAddress(6667));
System.out.println("服务端启动了!");
//创建selector实例
Selector selector=Selector.open();
//将ServerSocketChannel设置为非阻塞的
serverSocketChannel.configureBlocking(false);
//将ServerSocketChannel实例注册到选择器上,并对accept事件进行监听
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
//选择器进行阻塞,直到有事件发生才返回
while (selector.select()>0){
//对所有监听事件的集合进行遍历
Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key=iterator.next();
iterator.remove();
//处理accept事件
if(key.isValid() && key.isAcceptable()){
ServerSocketChannel ssc=(ServerSocketChannel)key.channel();
//接收新用户的连接
SocketChannel socketChannel=ssc.accept();
//将新用户的连接设置为非阻塞
socketChannel.configureBlocking(false);
//将socketChannel实例注册到选择器上
socketChannel.register(selector,SelectionKey.OP_READ);
key.cancel();
}
//处理读事件
if(key.isValid() && key.isReadable()){
SocketChannel socketChannel= (SocketChannel)key.channel();
//创建Buffer实例
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//将数据读到buffer实例中
socketChannel.read(byteBuffer);
//翻转
byteBuffer.flip();
byte[] bytes=new byte[byteBuffer.remaining()];
//将缓存数据读到buffer中
byteBuffer.get(bytes);
String nsg=new String(bytes);
System.out.println("客户端发送的消息为:"+nsg);
key.cancel();
//关闭用户连接
socketChannel.close();
}
}
}
System.out.println("服务端结束!");
//关闭操作
selector.close();
serverSocketChannel.close();
}
}
//NIO客户端
public class Test0226_NIOClient {
public static void main(String[] args) throws IOException {
//创建socketChannel实例
SocketChannel socketChannel=SocketChannel.open();
//创建选择器实例
Selector selector=Selector.open();
//设置socketChannel实例非阻塞
socketChannel.configureBlocking(false);
//connect本身是阻塞的,将socketChannel实例设置为非阻塞,则该connect无论是否连接成功都立即返回
if(!socketChannel.connect(new InetSocketAddress("127.0.0.1",6667))){
//当前连接服务器还未连接上则注册到选择器上进行监听
//将socketChannel实例注册到选择器上
socketChannel.register(selector,SelectionKey.OP_CONNECT);
}
int num;
while ((num=selector.select(1000))>0){
System.out.println("select返回:"+num);
//获取注册到选择器上的事件
Set<SelectionKey> selectionKeys=selector.selectedKeys();
Iterator<SelectionKey> iterator=selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey key=iterator.next();
iterator.remove();
//可连接事件完成
if(key.isValid() && key.isConnectable()){
SocketChannel Channel=(SocketChannel)key.channel();
key.cancel();
if(!Channel.finishConnect()){
//连接未完成,则直接断开该用户的连接
System.exit(1);
}
//连接完成,则创建该用户的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
byteBuffer.put("hello world".getBytes());
byteBuffer.flip();
//发送消息
socketChannel.write(byteBuffer);
System.out.println("客户端发送消息完成!");
}
}
}
//关闭操作
selector.close();
socketChannel.close();
System.out.println("客户端关闭操作完成!");
}
}
NIO模型与BIO模型之间的区别是什么?
NIO(同步非阻塞模型) | BIO(同步阻塞模型) |
1、通信处理方式:非阻塞的 2、面向缓冲区 3、数据读取到缓冲区,在需要的时候可以前后移动缓冲区中的数据 4、NIO可以利用一个线程或者少量的线程处理多个用户的请求 | 1、通信处理方式:阻塞的 2、面向流 3、每次从流中读取一个或者多个字节,直至将所有的字节读取完,并没有将数据读取到任何的缓冲区,因而不能前后移动数据 4、BIO是一个请求产生一个线程 |
5、适用场景:高并发、高访问量、短请求 | 5、适用场景:访问量少、长请求(比如下载一个大文件等场景) |
NIO模型进行缓冲区读取数据与IO流进行读取数据有何区别?
1、在Java IO中读取数据和写入数据是面向流的,这表示当我们从流中读取数据的时候,写入数据时也将其写入流,流的含义在于没有缓存,就好像我们站在流水线的前面,所有的数据沿着流水线到达我们面前,我们只能读取当前的数据。如果需要获得某个数据的前一项或者后一项那就必须自己缓存数据,不能够直接从流中获取到。
2、而在Java NIO中数据的读写是面向缓冲区的,读取时可以先将整块的数据读取到缓冲区中,在写入的时候可以将整个缓冲区中的数据一起写入。这就好像将流水线传输变成了卡车传送,面向缓冲区的IO则使我们能够看到数据的上下文,也就是说在缓冲区中获取某项数据的前一项数据或者后一项数据的时候十分方便。