Netty网络框架学习笔记-2(NIO实现简单一个群聊_2022-01-26)
实现的功能有:
- 监听所有客户端的上线与离线
- 多人群聊, 客户端发送消息给所有的客户端 (通过服务端转发)
- 或者单独私聊发送信息!
1.0 服务端的实现
/**
* @Author: ZhiHao
* @Date: 2022/1/26 17:44
* @Description: 群聊服务端实现
* @Versions 1.0
**/
@Slf4j
public class GroupChatNIOService {
private static final ConcurrentMap<Integer, SocketChannel> CHANNEL_CONCURRENT_MAP = new ConcurrentHashMap<>();
private static final AtomicInteger atomicInteger = new AtomicInteger(1);
public static void main(String[] args) throws Exception {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
for (; ; ) {
log.info("GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生");
int select = selector.select();
// 监听到的事件大于0, 进入操作
if (select > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
SelectableChannel channel = selectionKey.channel();
if (selectionKey.isAcceptable()) {
ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) channel;
SocketChannel socketChannel = serverSocketChannel1.accept();
socketChannel.configureBlocking(false);
int andIncrement = atomicInteger.getAndIncrement();
socketChannel.register(selector, SelectionKey.OP_READ, andIncrement);
log.info("GroupChatNIOService-{}号,客户端上线了群聊!", andIncrement);
CHANNEL_CONCURRENT_MAP.put(andIncrement, socketChannel);
}
if (selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel) channel;
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);
StringBuilder builder = new StringBuilder(1024);
try {
// 这里可以使用前面学习笔记中的扩容机制 (但是也有不少缺点不够完善)
while (socketChannel.read(byteBuffer) > 0) {
byteBuffer.flip();
builder.append(new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()));
byteBuffer.clear();
}
} catch (IOException e) {
Integer andIncrement = (Integer) selectionKey.attachment();
CHANNEL_CONCURRENT_MAP.remove(andIncrement);
log.info("GroupChatNIOService-{}号,客户端离线了群聊!, 进行取消注册与关闭通道", andIncrement);
selectionKey.cancel(); // 取消注册
channel.close(); // 关闭通道
keyIterator.remove(); // 防止重复操作
continue;
}
String str = builder.toString();
log.info("GroupChatNIOService-服务端接收到客户端的信息:{}", str);
JSONObject jsonObject = JSONUtil.parseObj(str);
// 进行转发信息到其他客户端
forward((Integer) selectionKey.attachment(), jsonObject);
}
keyIterator.remove(); // 防止重复操作, 一直循环0事件
}
}
}
}
private static void forward(Integer attachment, JSONObject jsonObject) throws IOException {
Integer clientNum = Convert.convertQuietly(Integer.class, jsonObject.get("clientNum"));
String data = (String) jsonObject.get("data");
// 私聊操作
if (Objects.nonNull(clientNum)) {
SocketChannel socketChannel = CHANNEL_CONCURRENT_MAP.get(clientNum);
if (Objects.isNull(socketChannel)){
log.info("GroupChatNIOService-对应客户端还没有上线, 请等待上线!");
return;
}
socketChannel.write(ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)));
return;
}
// 群聊操作
Set<Map.Entry<Integer, SocketChannel>> entries = CHANNEL_CONCURRENT_MAP.entrySet();
for (Map.Entry<Integer, SocketChannel> entry : entries) {
if (entry.getKey().equals(attachment)) {
continue; // 排除自己
}
SocketChannel socketChannel = entry.getValue();
socketChannel.write(ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)));
}
}
}
2.0 客户端1号实现
/**
* @Author: ZhiHao
* @Date: 2022/1/26 17:44
* @Description: 群聊服务端实现
* @Versions 1.0
**/
@Slf4j
public class GroupChatNIOClient1 {
private static ExecutorService executors = Executors.newSingleThreadExecutor();
public static void main(String[] args) throws Exception {
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
for (; ; ) {
log.info("GroupChatNIOClient1-阻塞等待selector中的所有通道其中的事件发生");
int select = selector.select();
if (select > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
if (selectionKey.isConnectable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
while (!channel.finishConnect()) {
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);
StringBuilder builder = new StringBuilder(1024);
while (channel.read(byteBuffer) > 0) {
byteBuffer.flip();
builder.append(new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()));
byteBuffer.clear();
}
log.info("GroupChatNIOClient1号, 接收到消息:{}", builder.toString());
}
//写数据回服务端
executors.execute(()->{
try {
waitWrite((SocketChannel) selectionKey.channel());
} catch (IOException e) {
e.printStackTrace();
}
});
keyIterator.remove(); // 防止重复操作, 一直循环0事件
}
}
}
}
private static void waitWrite(SocketChannel channel) throws IOException {
Scanner scan = new Scanner(System.in);
log.info("GroupChatNIOClient1号, 求输入需要发送的信息, 格式 1-xxxxx");
System.out.print("输入数据:");
String str = null; // 接收数据
while (StrUtil.isNotBlank((str = scan.next()))) {
break;
}
String[] split = str.split("-");
JSONObject jsonObject = new JSONObject();
if (split.length > 1) {
jsonObject.putOnce("clientNum", split[0]);
jsonObject.putOnce("data", split[1]);
} else {
jsonObject.putOnce("data", split[0]);
}
log.info("GroupChatNIOClient1号, 输入需要发送的信息为:{}", jsonObject.toString());
channel.write(ByteBuffer.wrap(jsonObject.toString().getBytes(StandardCharsets.UTF_8)));
}
}
2.0 客户端2、3号实现
代码完全与客户端一号一样
3.0 结果:
20:29:30.909 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:29:34.581 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-1号,客户端上线了群聊!
20:29:34.583 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:29:37.135 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-2号,客户端上线了群聊!
20:29:37.135 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:29:39.694 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-3号,客户端上线了群聊!
20:29:39.694 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:30:26.093 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-服务端接收到客户端的信息:{"data":"二号你是个大帅B","clientNum":"2"}
20:30:26.159 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:30:43.786 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-服务端接收到客户端的信息:{"data":"你也是个帅B","clientNum":"1"}
20:30:43.786 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:31:25.273 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-服务端接收到客户端的信息:{"data":"三号我就是喜欢群发,"}
20:31:25.273 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:33:32.265 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-3号,客户端离线了群聊!, 进行取消注册与关闭通道
20:33:32.266 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
20:33:44.630 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-2号,客户端离线了群聊!, 进行取消注册与关闭通道
20:33:44.630 [main] INFO com.zhihao.nio.GroupChatNIOService - GroupChatNIOService-阻塞等待selector中的所有通道其中的事件发生
---------------------------------------------------------------------
20:29:34.583 [main] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1-阻塞等待selector中的所有通道其中的事件发生
20:29:34.589 [main] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1-阻塞等待selector中的所有通道其中的事件发生
20:29:34.592 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1号, 求输入需要发送的信息, 格式 1-xxxxx
输入数据:2-二号你是个大帅B
20:30:26.091 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1号, 输入需要发送的信息为:{"data":"二号你是个大帅B","clientNum":"2"}
20:30:43.787 [main] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1号, 接收到消息:你也是个帅B
20:30:43.787 [main] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1-阻塞等待selector中的所有通道其中的事件发生
20:30:43.787 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1号, 求输入需要发送的信息, 格式 1-xxxxx
输入数据:20:31:25.273 [main] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1号, 接收到消息:三号我就是喜欢群发,
20:31:25.274 [main] INFO com.zhihao.nio.GroupChatNIOClient1 - GroupChatNIOClient1-阻塞等待selector中的所有通道其中的事件发生
------------------------------------------------------------------------
20:29:37.138 [main] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2-阻塞等待selector中的所有通道其中的事件发生
20:29:37.142 [main] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2-阻塞等待selector中的所有通道其中的事件发生
20:29:37.147 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2号, 求输入需要发送的信息, 格式 1-xxxxx
输入数据:20:30:26.159 [main] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2号, 接收到消息:二号你是个大帅B
20:30:26.161 [main] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2-阻塞等待selector中的所有通道其中的事件发生
1-你也是个帅B
20:30:43.785 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2号, 输入需要发送的信息为:{"data":"你也是个帅B","clientNum":"1"}
20:30:43.785 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2号, 求输入需要发送的信息, 格式 1-xxxxx
输入数据:20:31:25.274 [main] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2号, 接收到消息:三号我就是喜欢群发,
20:31:25.274 [main] INFO com.zhihao.nio.GroupChatNIOClient2 - GroupChatNIOClient2-阻塞等待selector中的所有通道其中的事件发生
---------------------------------------------------------------------------
20:29:39.696 [main] INFO com.zhihao.nio.GroupChatNIOClient3 - GroupChatNIOClient3-阻塞等待selector中的所有通道其中的事件发生
20:29:39.702 [main] INFO com.zhihao.nio.GroupChatNIOClient3 - GroupChatNIOClient3-阻塞等待selector中的所有通道其中的事件发生
20:29:39.706 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient3 - GroupChatNIOClient3号, 求输入需要发送的信息, 格式 1-xxxxx
输入数据:三号我就是喜欢群发, 1、2号给我接收
20:31:25.270 [pool-1-thread-1] INFO com.zhihao.nio.GroupChatNIOClient3 - GroupChatNIOClient3号, 输入需要发送的信息为:{"data":"三号我就是喜欢群发,"}
到此完毕, 加深熟悉了NIO网络编程的知识!
1