在以前搭建网络连接中,使用的BIO,在用户连接后专门创建一个线程和用户对接当用户没有使用时就会阻塞线程这也就是BIO为什么叫阻塞式通讯,而在今天高并发的情况下BIO会占用大量资源,阻塞线程会消耗系统资源形成一种浪费。所以NIO就是为了解决这个问题。
在传统的BIO模式中会使用线程来连接客户端,在一定程度上避免线程频繁的创建与销毁减少系统资源的使用,但是这仍然存在很大的问题。当用户离线时,线程不会直接注销,而是会阻塞或者空转,一直占用着线程资源,随着用户连接数越来越多所需要的线程资源就会越来越多降低整体新能,当有上万用户时所需要的资源就会十分夸张。但是NIO很好的避免了这个问题,下面来介绍NIO的组件。
NIO在Java中由三个组件构成分别为:缓存(ByteBuffer),管道(Channel)和选择器(Selector)。最为关键的选择器,选择器通过注册客户端发送过来的管道,之后直接进入休眠,等到有请求发送在开始读取连接管道中存在的数据处理好后再用管道原路返回。
接着是管道和缓存的使用,下面有一个服务端利用管道和缓存来实现NIO连接的例子:
int port=8080;
ServerSocketChannel server=ServerSocketChannel.open();
server.configureBlocking(false);
server.bind(new InetSocketAddress(port));
ByteBuffer buffer=ByteBuffer.allocate(1024);
while (true) {
Thread.sleep(1000);
SocketChannel socketChannel = server.accept();
if (socketChannel != null) {
System.out.println("连接成功。。。");
Thread.sleep(1000);
int num=socketChannel.read(buffer);
if(num>0) {
buffer.flip();
byte[] bytes=new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println("client:"+new String(bytes));
}
buffer.clear();
String msg = "连接成功。。。";
buffer.put(msg.getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
}
这里先创建一个端口用于接收在这个端口下创建的客户端,接着使用一个缓存空间接收管道中的数据,这里没有使用选择器而是采用轮询的方式效率会不如选择器。
客户端则需要创建管道和缓存空间来将需要的数据传给服务器或从服务器那里读取数据,实现如下:
String ip="127.0.0.1";
int port=8080;
SocketChannel channel=SocketChannel.open();
channel.configureBlocking(false);//禁止阻塞
channel.connect(new InetSocketAddress(ip,port));
ByteBuffer buffer=ByteBuffer.allocate(1024);
while (!channel.finishConnect()){
System.out.println("客户端正在连接服务器。。。");
}
Scanner scanner=new Scanner(System.in);
String message=scanner.nextLine();
buffer.put(message.getBytes());
buffer.flip();//切换为读模式
Thread.sleep(100);
channel.write(buffer);
buffer.clear();
int readNum=channel.read(buffer);
if(readNum>0){//只有大于0才读到数据,-1时断开连接
buffer.flip();
byte[] bytes=new byte[buffer.remaining()];
buffer.get(bytes);//将读到的数据存入自定义数组中
String msg=new String(bytes);
System.out.println("client:"+msg);
}
这里需要用while循环来连接至客户端防止一次没连接上,利用缓存将字符串转为字节流,再将字节流输入管道中,服务器通过轮询来获取到数据并利用管道返回结果。