当前位置:网站首页>NIO示例
NIO示例
2022-07-28 05:27:00 【yfyh2021】
NIO(Non Blocking IO)
同步非阻塞,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理,JDK1.4开始引入。
相较于传统BIO来说最直观的理解就是不阻塞了,我们直接来个简易NIO来看一下
package com.tuling.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class NioServer {
// 保存客户端连接
static List<SocketChannel> channelList = new ArrayList<>();
public static void main(String[] args) throws IOException {
// 创建NIO ServerSocketChannel,与BIO的serverSocket类似
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
// 设置ServerSocketChannel为非阻塞
serverSocket.configureBlocking(false);
System.out.println("服务启动成功");
while (true) {
// 非阻塞模式accept方法不会阻塞,否则会阻塞
// NIO的非阻塞是由操作系统内部实现的,底层调用了linux内核的accept函数
SocketChannel socketChannel = serverSocket.accept();
if (socketChannel != null) { // 如果有客户端进行连接
System.out.println("连接成功");
// 设置SocketChannel为非阻塞
socketChannel.configureBlocking(false);
// 保存客户端连接在List中
channelList.add(socketChannel);
}
// 遍历连接进行数据读取
Iterator<SocketChannel> iterator = channelList.iterator();
while (iterator.hasNext()) {
SocketChannel sc = iterator.next();
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
// 非阻塞模式read方法不会阻塞,否则会阻塞
int len = sc.read(byteBuffer);
// 如果有数据,把数据打印出来
if (len > 0) {
System.out.println("接收到消息:" + new String(byteBuffer.array()));
} else if (len == -1) { // 如果客户端断开,把socket从集合中去掉
iterator.remove();
System.out.println("客户端断开连接");
}
}
}
}
}打开两个telnet尝试链接

发现两个网络连接均链接成功, 而没有向BIO一样阻塞在accept方法处。
但是这种简易的方法有一个比较大的问题就是,我们是遍历chanel,如果大部分链接没有信息发送,但是链接没有断开,我们就有一大堆无用的链接需要遍历。
在这里引出我们NIO的多路复用器。
Selector(多路复用器):channel会注册到selector中,selector会监听channel中发生的事件,其本质是调用linux服务器中的Epoll函数
上代码
package com.tuling.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NioSelectorServer {
public static void main(String[] args) throws IOException {
// 创建NIO ServerSocketChannel
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
// 设置ServerSocketChannel为非阻塞
serverSocket.configureBlocking(false);
// 打开Selector处理Channel,即创建epoll
Selector selector = Selector.open();
// 把ServerSocketChannel注册到selector上,并且selector对客户端accept连接操作感兴趣
SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动成功");
while (true) {
// 阻塞等待需要处理的事件发生
selector.select();
// 获取selector中注册的全部事件的 SelectionKey 实例
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 遍历SelectionKey对事件进行处理
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 如果是OP_ACCEPT事件,则进行连接获取和事件注册
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 这里只注册了读事件,如果需要给客户端发送数据可以注册写事件
SelectionKey selKey = socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接成功");
} else if (key.isReadable()) { // 如果是OP_READ事件,则进行读取和打印
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);
int len = socketChannel.read(byteBuffer);
// 如果有数据,把数据打印出来
if (len > 0) {
System.out.println("接收到消息:" + new String(byteBuffer.array()));
} else if (len == -1) { // 如果客户端断开连接,关闭Socket
System.out.println("客户端断开连接");
socketChannel.close();
}
}
//从事件集合里删除本次处理的key,防止下次select重复处理
iterator.remove();
}
}
}
}
边栏推荐
- What's a good gift for your girlfriend on Chinese Valentine's day? Boys who can't give gifts, look!
- Problem solving for ACM freshmen in Jiangzhong on October 26
- 2022-07-17 达梦数据库安装
- Everything you don't know about time complexity is here
- 费马小定理
- OJ 1507 删数问题
- 刷题记录----反转链表(反转整个链表)
- 万字归纳总结并实现各大常用排序及性能对比
- OJ 1242 freshman's debut
- 关于Shader KeyWord的整理
猜你喜欢

Bug experience related to IAP jump of stm32

【详解如何一步步实现三子棋】

【动态规划--买卖股票的最佳时期系列2】
![[queue, simple application of stack ---- packaging machine]](/img/bc/617b1eb35558c4f948018f593a1de5.jpg)
[queue, simple application of stack ---- packaging machine]

刷题记录----二叉树的层序遍历

mysql-8.0.17-winx64(附加navicat)手动配置版安装

下雨场景效果(一)

Leetcode skimming diary sword finger offer II 050. sum of downward path nodes

QT batch operation control and set signal slot

2021-11-10
随机推荐
SSAO By Computer Shader(二)
Treasure plan TPC system development DAPP construction
订单交易简析
Explain the installation of MSDN 2015 and precautions
气传导蓝牙耳机哪个好、气传导蓝牙耳机排行榜
数组解法秘籍
OJ 1284 counting problem
气传导耳机哪个好、性价比最高的气传导耳机推荐
OJ 1507 deletion problem
水瓶效果制作
Development of clip arbitrage / brick carrying arbitrage system
Leetcode 刷题日记 剑指 Offer II 047. 二叉树剪枝
C语言的编译和预处理
开放式耳机推荐哪款最好、性价比最高的开放式耳机
[pta---- output full arrangement]
【实现简易版扫雷小游戏】
[c语言]--一步一步实现扫雷小游戏
OJ 1131 美丽数
用c语言实现三子棋小游戏
Leetcode 刷题日记 剑指 Offer II 050. 向下的路径节点之和