Non-blocking I/O 或 New I/O
jdk1.4发布 1.8稳定
高并发网络服务器支持岗
模型:对事务共性的抽象
编程模型:对编程共性的抽象
阻塞式I/O模型
弹性伸缩能力差
多线程耗资源
大量并发情况下会出现多个线程等待,致使服务器压力增大,甚至使服务器崩掉
Selector用于管理连接的客户端
基于非阻塞I/O实现
弹性伸缩能力强
单线程节省资源
Channel:通道
Buffer:缓冲区
Selector:选择器或多路复用器
双向性
非阻塞性
操作唯一性
文件类:FileChannel
UDP类:DatagramCHannel
TCP类:ServerSocketChannel/SocketChannel
作用:读写Channel中数据
本质:一块内存区域
Capacity:容量–表明数组最大容量字节数,如果超过容量必须将其清空才能继续填充数据
Position:位置–表示当前位置,插入数据后会向后移动到可插入数据单元,初始值为0,最大为下标减1
Limit:上限
Mark:标记
作用:I/O就绪选择
地位:NIO网络编程的基础
一个线程管理多个连接
四种就绪状态常量
有价值的属性
第一步:创建Selector
第二步:创建ServerSocketChannel,并绑定监听端口
第三步:将Channel设置为非阻塞模式
第四步:将Channel注册到Selector上,监听连接实际那
第五步:循环掉用Selector的select方法,检测就绪情况
第六步:掉用selectedKeys方法获取就绪channel集合
第七步:判断就绪事件种类,掉用业务处理方法
第八步:根据业务需要决定是否再次这个测监听时间,重复执行第三步操作
利用NIO编程只是,实现多人聊天室
麻烦:NOI类库和API繁杂
心累:可靠性能力补全,工作了和难度都非常大
有坑:Selector空轮询,导致CPU100% 主要体现在Linux系统
package com.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
/**
* NIO服务器端
*
* @author Wangjinghao
* @version v1.0.0
* @date 2019/6/11
*/
public class NioServer {
/* *
* @Author Wangjinghao
* @Description //TODO 启动
* @Date 16:21 2019/6/11
* @Param []
* @return void
**/
public void start() throws IOException {
/**
* 1.创建Selector
*/
Selector selector = Selector.open();
/**
* 2.通过ServerSocketChannel创建Channel通道
*/
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
/**
* 3.为channel通道绑定监听端口
*/
serverSocketChannel.bind(new InetSocketAddress(8000));
/**
* 4. **设置channel为非阻塞模式**
*/
serverSocketChannel.configureBlocking(false);
/**
* 5.将channel注册到selector上,监听连接事件
*/
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动成功!");
/**
* 6.循环等待接入的连接
*/
for (; ; ) {//while(true) c for;;
/**
* 获取可用channel数量
*/
int readyChannels = selector.select();
if (readyChannels == 0) continue;
/**
* 获取可用channel的集合
*/
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
/**
* selectionKey实例
*/
SelectionKey selectionKey = (SelectionKey) iterator.next();
/**
* 移除Set中的当前selectionKey
*/
iterator.remove();
/**
* 7.根据就绪转台,掉用对用方法处理业务逻辑
*/
/**
* 如果是 接入事件
*/
if (selectionKey.isAcceptable()) {
acceptHandler(serverSocketChannel, selector);
}
/**
* 如果是 可读事件
*/
if (selectionKey.isReadable()) {
readHandler(selectionKey, selector);
}
}
}
}
/**
* 接入事件处理器
*/
private void acceptHandler(ServerSocketChannel serverSocketChannel,
Selector selector) throws IOException {
/**
* 如果是 接入事件,创建socketChannel
*/
SocketChannel socketChannel = serverSocketChannel.accept();
/**
* 将sociketCahnnel设置为非阻塞工作模式
*/
socketChannel.configureBlocking(false);
/**
* 将channel注册到selector上,监听可读事件
*/
socketChannel.register(selector, SelectionKey.OP_READ);
/**
* 回复客户端提示信息
*/
socketChannel.write(Charset.forName("UTF-8")
.encode("你与聊天室里其他人都是不朋友关系,请注意隐私安全"));
}
/**
* 可读事件处理器
*/
private void readHandler(SelectionKey selectionKey,
Selector selector) throws IOException {
/**
* 要从selectionKey中获取调已经就绪的channel
*/
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
/**
* 创建按buffer
*/
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
/**
* 循环读取客户端请求信息
*/
String request = "";
while (socketChannel.read(byteBuffer) > 0) {
/**
* 切换buffer为读模式
*/
byteBuffer.flip();
/**
* 读取buffer中的内容
*/
request += Charset.forName("UTF-8").decode(byteBuffer);
}
/**
* 将channel再次注册到selector上,监听他的可读事件
*/
socketChannel.register(selector, SelectionKey.OP_READ);
/**
* 将客户端发送的请求信息,广播给其他客户端
*/
if (request.length() > 0) {
//广播给其他客户端
System.out.println("::" + request);
broadCast(selector,socketChannel,request);
}
}
/**
* 广播给其他客户端
*/
private void broadCast(Selector selector,SocketChannel sourceChannel,String request) {
/**
* 获取到所有已接入的客户端cahnnel
*/
Set<SelectionKey> selectionKeySet = selector.keys();
//
selectionKeySet.forEach(selectionKey->{
Channel tagetCahnnel= selectionKey.channel();
//剔除发消息的客户端
if (tagetCahnnel instanceof SocketChannel && tagetCahnnel!=sourceChannel){
try {
//将消息发送到客户端
((SocketChannel) tagetCahnnel).write(Charset.forName("UTF-8").encode(request));
} catch (IOException e) {
e.printStackTrace();
}
}
});
/**
* 循环向所有哦channel广播信息
*/
}
/* *
* @Author Wangjinghao
* @Description //TODO 主方法
* @Date 16:20 2019/6/11
* @Param [args]
* @return void
**/
public static void main(String[] args) throws IOException {
NioServer nioServer = new NioServer();
nioServer.start();
}
}
package com.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
/**
* 客户端线程类,准吗接收服务器响应信息
*
* @author Wangjinghao
* @version v1.0.0
* @date 2019/6/11
*/
public class NioClientHandler implements Runnable {
private Selector selector;
public NioClientHandler(Selector selector) {
this.selector = selector;
}
@Override
public void run() {
try {
for (; ; ) {
int readyChannels = selector.select();
if (readyChannels == 0) continue;
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
iterator.remove();
if (selectionKey.isReadable()) {
readHandler(selectionKey, selector);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void readHandler(SelectionKey selectionKey,
Selector selector) throws IOException {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
String response = "";
while (socketChannel.read(byteBuffer) > 0) {
byteBuffer.flip();
response += Charset.forName("UTF-8").decode(byteBuffer);
}
socketChannel.register(selector, SelectionKey.OP_READ);
//将服务器端信息打印到本地
if (response.length() > 0) {
System.out.println("" + response);
}
}
}
package com.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
/**
* NIO客户端
*
* @author Wangjinghao
* @version v1.0.0
* @date 2019/6/11
*/
public class NioClient {
/**
* 启动
*/
public void start(String nickname) throws IOException {
/**
* 连接服务器端
*/
SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",8000));
/**
* 接收服务器端响应
*/
//新开线程,专门负责来接收服务器端的响应数据
//selector ,socketCahnnel ,注册
Selector selector=Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
new Thread(new NioClientHandler(selector)).start();
/**
* 向服务器发送数据
*/
Scanner input=new Scanner(System.in);
while(input.hasNextLine()){
String request=input.nextLine();
if (request!=null && request.length()>0){
socketChannel.write(Charset.forName("UTF-8").encode(nickname+":"+request));
}
}
}
public static void main(String[] args) throws IOException {
// new NioClient().start();
}
}