BIO网络模型
高并发场景下的BIO模型:每个客户端请求会建立一个阻塞线程
BIO模型的缺陷:
①阻塞式IO模型
②弹性伸缩能力差(每次都会起单独的线程)
③多线程消耗资源
NIO网络模型:
NIO模型优势:
①非阻塞式IO模型
②弹性伸缩能力强
③单线程节省资源
NIO网络编程详解:
Channel通道:双向传输,非阻塞,唯一通过buffer操作channel
使用方法:
Buffer缓冲器 从channel中读取数据,内存中一块属性
Capacity:容量 最大可储存的字节数,超过最大容量清空后才能继续写入数据
Position:容量位置,最大为容量-1
Limit:上限
Mark:标记,恢复读写位置
Selector选择器:检测多个IO通道是否准备就绪,NIO的基础核心,一个线程管理多个线程
Selectionkey:四种就绪状态常量,连接就绪,接受就绪,读就绪,写就绪。通过selectionkey集合获得当前channel
实战:利用NIO编程知识,实现多人聊天室功能:
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服务器端
*/
public class Nioserver {
/**
* 启动方法
*/
public void start() throws IOException {
//1.创建selector
Selector selector = Selector.open();
//2.通过serversocketchannel创建channerl通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//3.为channel通道绑定监听端口
serverSocketChannel.bind(new InetSocketAddress(9000));
//4.设置channel为非阻塞模式
serverSocketChannel.configureBlocking(false);
//5.将channel注册到selector上,监听连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动成功");
//6.循环等待新连接的事件
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(); // 去除实例后后移除
//7.根据就绪状态,调用处理逻辑
/**
* 接入事件
*/
if (selectionKey.isAcceptable()) {
acceptHandler(serverSocketChannel, selector);
}
/**
* 可读事件
*/
if (selectionKey.isReadable()) {
readHandler(selectionKey, selector);
}
}
}
}
/**
* 接入事件处理
*/
public void acceptHandler(ServerSocketChannel serverSocketChannel,
Selector selector) throws IOException {
// 创建socketchannel
SocketChannel socketChannel = serverSocketChannel.accept();
//将socketchannel设置为非阻塞模式
socketChannel.configureBlocking(false);
//将channel注册到selector上,监听事件
socketChannel.register(selector, SelectionKey.OP_READ);
//回复客户端提示消息
socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室其他人都不是朋友关系,请注意隐私安全"));
}
/**
* 可读事件处理
*/
public 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) > 1) {
//切换成读模式
byteBuffer.flip();
//读取
request += Charset.forName("utf-8").decode(byteBuffer);
}
//将channel再次注册到selectionkey上,再次监听他的可读事件
socketChannel.register(selector, SelectionKey.OP_READ);
//将客户端发送的信息广播给其他客户端 : 聊天室功能
if (request.length() > 0) {
System.out.println(request);
broadCast(selector,socketChannel,request);
}
}
private void broadCast(Selector selector, SocketChannel socketChannel, String request){
// 获得所有已接入的客户端的channel
Set<SelectionKey> selectionKeys = selector.keys();
//向所有channel广播信息
selectionKeys.forEach(selectionKey -> {
Channel targetChannel = selectionKey.channel();
if(targetChannel instanceof SocketChannel && targetChannel!=socketChannel){
try {
((SocketChannel) targetChannel).write(Charset.forName("utf-8").encode(request));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
/**
* 主方法
*
* @param args
*/
public static void main(String[] args) throws IOException {
Nioserver nioserver = new Nioserver();
nioserver.start();
}
}
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;
/**
* 客户端
*/
public class NioClient {
public void start(String name) throws IOException {
/**
* 连接服务器端
*/
SocketChannel socketChannel = SocketChannel.open(
new InetSocketAddress("127.0.0.1", 9000));
/**
* 接受服务器端响应
*/
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
new Thread(new NioClientHandler(selector)).start();
/**
* 向服务器端发送请求
*/
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String request = scanner.nextLine();
if (request != null && request.length() > 0) {
socketChannel.write(Charset.forName("utf-8").encode(name+": "+request));
}
}
}
public static void main(String[] args) throws IOException {
// new NioClient().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;
public class NioClientHandler implements Runnable {
public NioClientHandler(Selector selector) {
this.selector = selector;
}
private 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();
}
}
public void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
// 要从selectionkey中获取已经就绪的channel
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//创建buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//循环读取服务器端响应
String response = "";
while (socketChannel.read(byteBuffer) > 1) {
//切换成读模式
byteBuffer.flip();
//读取
response += Charset.forName("utf-8").decode(byteBuffer);
}
//将channel再次注册到selectionkey上,再次监听他的可读事件
socketChannel.register(selector, SelectionKey.OP_READ);
//将服务器端响应打印出来
if (response.length() > 0) {
System.out.println(response);
}
}
}
package com.NIO;
import java.io.IOException;
public class AClient {
public static void main(String[] args) throws IOException {
new NioClient().start("Aclient");
}
}
package com.NIO;
import java.io.IOException;
public class BClient {
public static void main(String[] args) throws IOException {
new NioClient().start("Bclient");
}
}
package com.NIO;
import java.io.IOException;
public class CClient {
public static void main(String[] args) throws IOException {
new NioClient().start("Cclient");
}
}
多人聊天室功能实现: