首先解释一下阻塞和非阻塞
对于传统的BIO,线程在同一时间只能等待数据的发送和接收,在此期间不能做其他的事
用图来表示
所以对于这种io,我们应该使用多线程的方式完成io通信
多线程虽然能完成同时接收和发送,但是本质上也是多个阻塞的单线程,多个线程中某个陷入阻塞,该线程还是不能做其他的事,造成资源的浪费。
NIO非阻塞模式,将每一个用于传输数据的通道都注册到选择器上,选择器监控通道的io状况(读,写,连接,接收数据),当某个通道上某个请求的事件完全就绪的时候,选择器才会将该任务分配给服务端的一个或多个线程,其他情况服务器的线程可以做其他事情。
nio非阻塞模式代码实现:
客户端
//客户端
@Test
public void TestClient(){
SocketChannel client=null;
ByteBuffer buffer=null;
try {
//获取socket连接
client = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
//切换非阻塞状态
client.configureBlocking(false);
//指定缓冲区
buffer = ByteBuffer.allocate(1024);
//从控制台读入数据,发送数据到服务端
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
buffer.put(scanner.next().getBytes());
//切换成读模式
buffer.flip();
client.write(buffer);
buffer.clear();
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (client!=null){
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端
@Test
public void TestServer() throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
//1.切换非阻塞模式
server.configureBlocking(false);
//2.绑定
server.bind(new InetSocketAddress("127.0.0.1",9898));
//3.获取选择器
Selector selector = Selector.open();
//4.将服务注册到选择器上,第二个参数式选择键selectionkey表示,有四个常量
// (1)selectionkey.OP_READ 读操作 (2)selectionkey.OP_WRITE 写操作 (3)selectionkey.OP_CONNECT 连接操作 (4)selectionkey.OP_ACCEPT 接收操作
server.register(selector, SelectionKey.OP_ACCEPT); //将读事件注册到选择器上
//5.轮询式的获取选择器上已经准备就绪的事件
while (selector.select()>0){
//6.获取当前选择器中所有注册的“选择键”
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
//7.判断什么事件准备就绪
if (key.isAcceptable()){
//8.如果是接收就绪,采取动作
SocketChannel accept = server.accept();
//9.切换非阻塞状态
accept.configureBlocking(false);
//10.将读事件注册到selector上
accept.register(selector,SelectionKey.OP_READ);
}else if (key.isReadable()){
//11.获取当前选择器上“读状态”的通道
SocketChannel channel =(SocketChannel) key.channel();
//12.读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len=0;
while ((len=channel.read(buffer))>0){
buffer.flip();
String s = new String(buffer.array(), 0, len);
System.out.println(s);
buffer.clear();
}
}
//执行完任务之后就要取消选择键 因为将acceptor也
iterator.remove();
}
}
}