版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27092581/article/details/78440261
一、什么是阻塞和非阻塞?
传统的 IO 流都是阻塞式的。也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,
该线程在此期间不 能执行其他任务。因此,在完成网络通信进行 IO 操作时,由于线程会 阻塞,所以服务器端必须为每个客户端都
提供一个独立的线程进行处理, 当服务器端需要处理大量客户端时,性能急剧下降。
Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时,若没有数 据可用时,该线程可以进行其他任务。
线程通常将非阻塞 IO 的空闲时 间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入 和输出通道。
因此,NIO 可以让服务器端使用一个或有限几个线程来同 时处理连接到服务器端的所有客户端。
二、NIO中阻塞和非阻塞用法
1、NIO中的阻塞式IO用法
这里通过socket网络建立client和server实现图片上传为例:
客户端代码:
public void client()throws Exception
{
//建立网络通道,连接指定服务器端口
SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1", 10011));
//创建缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//创建客户端本地文件通道
FileChannel fileChannel=FileChannel.open(Paths.get("sb.jpg"), StandardOpenOption.READ);
//读取本地文件,发送到服务器
while(fileChannel.read(byteBuffer)!=-1)
{
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
}
//告诉服务端我发送完了
socketChannel.shutdownOutput();
//接受端反馈
int len=0;
while((len=socketChannel.read(byteBuffer))!=-1)
{
byteBuffer.flip();
//直接打印
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
//关闭资源
fileChannel.close();
socketChannel.close();
}
服务端代码:
public void Server()throws Exception
{
//获取服务器通道
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
//建立指向服务器的通道
FileChannel fileChannel=FileChannel.open(Paths.get("xxx.jpg"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//绑定连接 指定端口
serverSocketChannel.bind(new InetSocketAddress(10011));
//可以理解为连接客户端的通道
SocketChannel socketChannel=serverSocketChannel.accept();
//建立一个缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
while(socketChannel.read(buf)!=-1)
{
buf.flip();
fileChannel.write(buf);
buf.clear();
}
//发送反馈给客户端
buf.put("服务端接受数据成功!".getBytes());
//切换可读
buf.flip();
socketChannel.write(buf);
//关闭通道
socketChannel.close();
fileChannel.close();
serverSocketChannel.close();
}
2、NIO中的非阻塞式IO的使用
要用NIO中的非阻塞式IO就需要了解NIO中的Selector,中文意思“选择器”。
我们需要知道,通过Selector可以使一个线程管理多个Channel。
客户端:
public void client()throws Exception
{
SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1", 10010));
//切换非阻塞式IO
socketChannel.configureBlocking(false);
//分配缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
buf.put(new Date().toString().getBytes());
buf.flip();
socketChannel.write(buf);
buf.clear();
socketChannel.close();
}
服务端:
public void Server()throws Exception
{
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
//切换为非阻塞
serverSocketChannel.configureBlocking(false);
//绑定端口
serverSocketChannel.bind(new InetSocketAddress(10010));
//获取选择器
Selector selector =Selector.open();
//将通道注册到选择器上 第二个参数用于监控通道的什么状态
//指定为了接收监听事件
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT );
//通过选择器轮询获取选择器上已经准备就绪的事件
while(selector.select()>0)
{
//获取当前选择器中所有注册的选择键(已就绪监听键)
Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
//遍历
while(iterator.hasNext())
{
//获取准备就绪的事件
SelectionKey key=iterator.next();
//判断是什么事件准备就绪
//1、接受就绪 那就获取客户端的连接
if(key.isAcceptable())
{
//建立连接
SocketChannel socketChannel=serverSocketChannel.accept();
//设置为非阻塞
socketChannel.configureBlocking(false);
//注册读监听
socketChannel.register(selector, SelectionKey.OP_READ);
}
//读就绪
else if(key.isReadable())
{
SocketChannel sChannel=(SocketChannel)key.channel();
//读取数据
ByteBuffer buf=ByteBuffer.allocate(1024);
int len=0;
while((len=sChannel.read(buf))!=-1)
{
buf.flip();
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
}
//取消选择键
iterator.remove();
}
}
//关闭资源
}