1. 目录结构
1. NioServer
代码语言:javascript
复制package com.xu.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;
/**
* @author jiafupeng
* @desc
* @create 2021/3/7 19:11
* @update 2021/3/7 19:11
**/
public class NioServer {
/**
* 启动
*/
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(;;){
// 阻塞 - 获取可用channel数量
int readyChannels = selector.select();
if(readyChannels == 0){continue;}
// 获取可用channel的集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
// selectionKey实例
SelectionKey selectionKey = iterator.next();
// 移除
iterator.remove();
// 7. 根据就绪状态,调用对应方法处理业务逻辑
// 如果是 接入事件
if(selectionKey.isAcceptable()){
acceptHandler(selectionKey, selector);
}
// 如果是 可读事件
if(selectionKey.isReadable()){
readHandler(selectionKey, selector);
}
}
}
}
/**
* 接入事件处理器
*/
private void acceptHandler(SelectionKey selectionKey,
Selector selector)
throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
// 要是接入事件,创建socketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
// 将socketChannel设置为非阻塞工作模式
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){
broadCast(selector, socketChannel, request);
}
}
private void broadCast(Selector selector,
SocketChannel sourceChannel, String request){
Set<SelectionKey> selectionKeySet = selector.keys();
selectionKeySet.stream().forEach(selectionKey -> {
Channel targetChannel = selectionKey.channel();
// 剔除发消息的客户端
if (targetChannel instanceof SocketChannel
&& targetChannel != sourceChannel) {
try {
// 将信息发送到targetChannel客户端
((SocketChannel) targetChannel).write(
Charset.forName("UTF-8").encode(request));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public static void main(String[] args) throws IOException {
NioServer nioServer = new NioServer();
nioServer.start();
}
}
2. NioClient
代码语言:javascript
复制package com.xu.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;
/**
* @author jiafupeng
* @desc
* @create 2021/3/7 19:42
* @update 2021/3/7 19:42
**/
public class NioClient {
private String nickname;
public NioClient(String nickname) {
this.nickname = nickname;
}
public void start() throws IOException {
// 连接服务器端
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));
// 接受服务端响应
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(nickname " : " request));
}
}
}
}
3. NioClientHandler
代码语言:javascript
复制package com.xu.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 jiafupeng
* @desc
* @create 2021/3/8 10:14
* @update 2021/3/8 10:14
**/
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) {
// 切换buffer为读模式
byteBuffer.flip();
// 读取buffer中的内容
response = Charset.forName("UTF-8").decode(byteBuffer);
}
// 将channel再次注册到selector上,监听他的可读事件
socketChannel.register(selector, SelectionKey.OP_READ);
// 将服务器端响应信息打印到本地
if (response.length() > 0) {
System.out.println(response);
}
}
}
4. XXClient
代码语言:javascript
复制package com.xu.nio;
import java.io.IOException;
/**
* @author jiafupeng
* @desc
* @create 2021/3/8 10:59
* @update 2021/3/8 10:59
**/
public class XXClient {
public static void main(String[] args) throws IOException {
new NioClient("jiafupeng").start();
}
}
5. YYClient
代码语言:javascript
复制package com.xu.nio;
import java.io.IOException;
/**
* @author jiafupeng
* @desc
* @create 2021/3/8 10:59
* @update 2021/3/8 10:59
**/
public class YYClient {
public static void main(String[] args) throws IOException {
new NioClient("wangdongxu").start();
}
}
结果展示