持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
遇到困难时不要抱怨,既然改变不了过去,那么就努力改变未来
NIO 小案例实战客户端与服务端进行通讯
案例背景
我们进行了NIO的相关组件学习,但是都是知道了组件的API以及作用,但是并不知道如何将组件放到一起进行使用,所以我们通过一个客户端和服务端通信的案例进行学习,并加深三大组件的使用
开发步骤
- 创建选择器
- 创建服务端 ServerSocketChannel
- 创建服务端的监听端口
- 设置服务端的 ServerSocketChannel 是非阻塞的
- 将服务端的 ServerSocketChannel 注册到 Selector 选择器上面,并将事件设置成连接事件
- 监听就绪事件
- 根据相应的就绪事件进行逻辑处理【连接事件、可读事件、可写事件等等】
代码实现
package com.zxp.groupChat;
import io.netty.util.CharsetUtil;
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;
/**
* @Version V1.0.0
* @Since 1.0
* @Date 2022/6/27
* @Description 聊天记录服务端
*/
public class ChatServer {
public static void main(String[] args) throws Exception {
// 1. 创建选择器
Selector selector = Selector.open();
// 2. 创建服务端 channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 3. 创建服务端的监听端口
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 6666));
// 4.设置serversocketchannel 是非阻塞的
serverSocketChannel.configureBlocking(false);
// 5. 将serversocketchannel注册到selector选择器上面,并将事件设置成连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 监听就绪事件
while (true) {
System.out.println("等待......");
// 休眠1秒 无论是否有读写事件发生 selector每隔1秒被唤醒
int selected = selector.select(1000);
if (selected > 0) { // 证明有事件已经准备就绪
// 返回已经就绪的事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
if (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 获取socketChannel
if (key.isAcceptable()) { // 连接事件就绪,将其感兴趣的事件设置成已读事件
// 处理接入的新请求
handleAccept(selector, key);
}
if (key.isReadable()) { // 已读事件就绪
// 处理通道的读请求
handleRead(key);
}
iterator.remove();// 将处理完的数据进行了移除
}
}
}
}
/**
* 处理客户端读操作请求
* @param key
*/
private static void handleRead(SelectionKey key) {
SocketChannel socketChannel = (SocketChannel) key.channel();
// 申请一个buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将通道的数据读入到buffer中
try {
socketChannel.read(buffer);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("客户端发来消息: " + new String(buffer.array(),
CharsetUtil.UTF_8));
}
/**
* 处理连接操作
* @param selector
* @param key
*/
private static void handleAccept(Selector selector, SelectionKey key) {
// 通过 ServerSocketChannel 监听过来连接的客户端事件
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
//通过调用 accept 方法,返回一个具体的客户端连接管道
try {
SocketChannel socketChannel = serverChannel.accept();
System.out.println("客户端 " +
socketChannel.getRemoteAddress() + "已上线......");
// 将channel 注册到selector 上面,而且需要设置成是非阻塞的
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果展示
由于我们是在windows平台下进行的测试所以我们就用了命令行工具 cmd ,通过Telnet工具进行的测试操作,但是我们进行指令操作的时候有可能提示这个工具不存在,那么我们就需要进行将这个工具开放出来【windows 默认是关闭的】
开启Telnet工具
1. 打开控制面板
2. 打开程序
3. 启用或关闭 Windows 功能
在图中将其√选中,之后确定保存就可以了
连接服务端
连接服务端指令:telnet IP地址 端口号
telnet 127.0.0.1 6666
没连接之前隔1s会一直打印 等待......
连接成功后服务端会有客户端提示已上线的操作显示
此时客户端会一直是黑色的,你需要按下Ctrl+] 这个键子,之后通过send指令进行消息发送
此时在cmd控制台通过telnet 的send指令向服务端发送hello操作
发送结果
此时客户端已经和服务端进行了连接成功并进行了消息发送操作
总结
到此我们已经将NIO的三大组件channel、buffer、selector 进行了结合实现了服务端与客户端的连接操作,并且通过客户端也给服务端进行了消息发送,服务端进行了消息打印处理,后续我们还会做一个群聊的通讯案例进行NIO 基础知识的学习