1、NIO的介绍 1.1、NIO概念
NIO(New IO)同步非阻塞IO模型,采用了事件驱动的思想来实现一个复用器,来解决大并发的问题
NIO中,将读、写、可连接、可接受等操作在BIO中是阻塞处理,将操作设置为非阻塞,并将这些操作视为事件,当关注某个事件时,将其注册到复用器Selector(本质上使用底层操作系统提供的IO复用器:select、poll,epoll)。由系统来监听事件是否准备就绪, 当socket有读或者可写操作时,系统会通知相应的用户程序来处理,将流读取到缓冲区或者写入到系统中
一个复用器上是可以注册多个用户的连接,有效的用户请求才会来通知用户程序,用户程序才进行介入处理,
1.2、NIO处理过程
1.3、几个概念的介绍
1.3.1、复用器(Selector)
也叫做多路选择器,作用是检查一个或者多个NIO的channel(Channel),可以实现单线程管理多个channel,也可以管理多个网络请求
1.3.2、channel:
1.3.2、channel:
通道,用户IO操作的连接,对原有的IO补充,不能直接访问数据需要和缓冲区BUffer结合使用,通道主要有
1.3.3、SocketChannel:
主要是用户连接服务端,一般在客户端实现
1.3.4、ServerSocketChannel:
监听新进来的TCP连接,对于每一个新用户连接创建一个SocketChannel实例,一般在服务端实现
1.3.5、Buffer:缓冲区
IO流中的数据需要经过缓冲区交给channel
2、NIO编码
2.1、服务端编程流程:
2.1.1、实例ServerSocketChannel实例【ServerSocketChannel.open()】
2.1.2、服务端绑定端口【bind】
2.1.3、将serversocketchannel设置为非阻塞【serverSocketChannel.configureBlocking(false)】
2.1.4、实例话selector实例【Selector.open()】
2.1.5、将serversocketchannel注册到selector上,并关注可接受事件【serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT)】
2.1.6、selector阻塞等待至少一个事件发生【selector.select()】
2.1.7、有感兴趣事件发生,遍历感兴趣事件集合
2.1.8、如果是可接受事件、通过serversocketchannel调用accept获取socketchannel
2.1.9、将socketchannel设置为非阻塞
2.1.10、将socketchannel注册到selector复用上,并关注可读、可写事件
2.1.11、循环等待感兴趣事件集合,即跳转至第6步
2.1.12、如果当前是可读事件、通过socketchannel调用read来进行读数据操作
2.1.13、关闭资源
服务端代码:public class Server {
public static void main(String[] args) {
ServerSocketChannel serverSocketChannel = null;
try {
//创建ServerSocketchannel实例
serverSocketChannel = ServerSocketChannel.open();
//绑定端口
serverSocketChannel.bind(new InetSocketAddress(7777));
System.out.println("服务端启动啦");
//设置ServerSocketchannel为非阻塞 true:表示则塞 false:非阻塞
serverSocketChannel.configureBlocking(false);
//创建复用器selector实例
Selector selector = Selector.open();
//将serverSocketChannel注册到selector中,并关注的是可接受事件
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT | SelectionKey.OP_WRITE);
//selector阻塞等待事件发生
while (selector.select() >= 0) {
//获取已发生事件集合
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//删除已完成事件
iterator.remove();
if (selectionKey.isValid() && selectionKey.isAcceptable()) {
System.out.println("有接收事件发生");
//有可接受事件发生
//SelectableChannel 所有通道的父类,ServerSocketChannel、SocketChannel
ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) selectionKey.channel();
//通过调用accept获取SocketChannel实例
SocketChannel socketChannel = serverSocketChannel1.accept();
System.out.println("有新用户连接啦"+socketChannel.getRemoteAddress());
//设置为非阻塞
socketChannel.configureBlocking(false);
//将socketChannel注册到selector复用器上,并关注读事件
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isValid() && selectionKey.isReadable()) {
System.out.println("有可读事件发生");
//有可读事件发生
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//读取数据
//创建一个缓存实例
ByteBuffer buffer = ByteBuffer.allocate(100);
//将数据从channel写入缓存
int read = socketChannel.read(buffer);
//读写模式切换
buffer.flip();
//创建有效大小的byte数组
byte[] bytes = new byte[read];
buffer.get(bytes);
String msg = new String(bytes);
System.out.println("数据为:"+msg);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
System.out.println("服务端关闭啦");
if (serverSocketChannel != null) {
try {
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.2、客户端编码流程:
2.2.1、创建socketchannel实例
2.2.2、将socketchannel设置非阻塞
2.2.3、创建Selector复用器实例
2.2.4、将socketchannel实例注册到Selector实例中,并关注可连接事件
2.2.5、socketchannel主动调用connect连接服务端,会立即返回结果
2.2.6、如果结果为false:当前连接还未完成,通过selector.select由系统监听事件发生,
2.2.7、进行读写事件
2.2.8、关闭资源
客户端代码:public class Client {
public static void main(String[] args) {
SocketChannel socketChannel = null;
try {
//创建socketChannel实例
socketChannel = SocketChannel.open();
//将socketChannel设置为非阻塞
socketChannel.configureBlocking(false);
//创建Selector复用器实例
Selector selector = Selector.open();
//将socketChannel注册到Selector实例中,并关注可连接事件
socketChannel.register(selector,SelectionKey.OP_CONNECT);
//主动连接服务端,该方法不会阻塞,会立即返回结果,要么成功,要么未连接成功,则交由os等待连接过程
if (!socketChannel.connect(new InetSocketAddress("127.0.0.1", 7777))) {
//返回为false,表示连接还未连接成功
System.out.println("connect还未完成!");
//等待系统返回就绪事件
selector.select();
//进行完成连接操作
socketChannel.finishConnect();
}
System.out.println("客户端连接服务端成功");
//进行读写事件
byte[] bytes = "Hello Tulun".getBytes();
//创建buffer
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
//往Buffer写数据
buffer.put(bytes);
//读写模式切换
buffer.flip();
//往socketchannel通道中写数据
socketChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (socketChannel != null) {
System.out.println("客户端关闭啦");
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}



