java NIO群聊系统
demo要求:
1)编写一个NIO群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)
2)实现多人群聊
3)服务器端:可以监测用户上线,离线,并实现消息转发功能。
4)客户端:通过channel可以无阻塞发送消息给其他所有用户(客户端),同时可以接受其他用户发送的消息(由服务器转发得到)
5)目的:进一步理解NIO非阻塞网络编程机制。
从Netty的Reactor模式看demo是单Reactor单线程模式。其实Netty是在NIO1.0的基础上封装了复杂的调用操作,解决了JDK1.6的NIO臭名昭著的Epoll Bug,方便程序员进行网络编程,提升效率。
以下代码:
服务器端实现的代码:
package com.tfq.netty.nio.groupchat;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;/*** @author: fqtang* @date: 2024/03/19/11:22* @description: 服务器端*/
public class GroupChatServer {//定义属性private ServerSocketChannel listenChannel;private Selector selector;private static final int PORT = 6667;//构造器public GroupChatServer() {try {//得到选择器this.selector = Selector.open();//获取监听通道this.listenChannel = ServerSocketChannel.open();//绑定端口this.listenChannel.socket().bind(new InetSocketAddress(PORT));//设置通道为非阻塞this.listenChannel.configureBlocking(false);//将该listenChannel注册到selectorthis.listenChannel.register(selector, SelectionKey.OP_ACCEPT);} catch(IOException e) {e.printStackTrace();}}/*** 监听*/public void listen(){try {//循环处理while(true) {int count = selector.select();if(count > 0) {//有事件处理//遍历得到selectionKey集合Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()) {//取出selectionkeySelectionKey key = iterator.next();//监听到acceptif(key.isAcceptable()) {SocketChannel sc = listenChannel.accept();sc.configureBlocking(false);//将sc注册到Selectorsc.register(selector, SelectionKey.OP_READ);System.out.println(sc.getRemoteAddress() + " 上线");}if(key.isReadable()) {//通道发送read事件,即通道是可读的状态//处理读readDate(key);}//当前的Key删除,防止重复处理iterator.remove();}}}} catch(IOException e) {e.printStackTrace();} finally {try {listenChannel.close();} catch(IOException e) {throw new RuntimeException(e);}}}/*** 读取客户端的消息*/private void readDate(SelectionKey key) {//定义一个SocketChannelSocketChannel channel = null;try {//得到channelchannel = (SocketChannel) key.channel();//创建bufferByteBuffer buffer = ByteBuffer.allocate(1024);int count = channel.read(buffer);if(count > 0) {//把缓冲区的数据转成字符串String msg = new String(buffer.array());System.out.println("from 客户端发送的消息:" + msg);//向其他的客户端转发消息sendMsgToOtherClients(channel, msg);}} catch(Exception e) {try {System.out.println(channel.getRemoteAddress() + "已离线了");//取消注册key.cancel();//关闭通道channel.close();} catch(IOException ex) {throw new RuntimeException(ex);}}}/*** 转发消息给其他通道,排除自己* @param selfChannel* @param msg*/private void sendMsgToOtherClients(SocketChannel selfChannel,String msg) throws IOException {System.out.println("服务器转发消息中.....");//遍历 所有注册到selector 上的SocketChannel,并排除seflChannelfor(SelectionKey key: selector.keys()){//通过key 取出对应的SocketChannelChannel targetChannel = key.channel();//排除自己if(targetChannel instanceof SocketChannel && targetChannel != selfChannel ){//转型SocketChannel dest = (SocketChannel) targetChannel;//将数据存储到buffer。写入bufferByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());//将buffer 的数据写入通道dest.write(byteBuffer);}}}public static void main(String[] args) {GroupChatServer groupChatServer = new GroupChatServer();groupChatServer.listen();}}
客户端代码如下:
package com.tfq.netty.nio.groupchat;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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;/*** @author: fqtang* @date: 2024/03/19/14:14* @description: 描述*/
public class GroupChatClient {//定义属性private final String HOST = "127.0.0.1";private final int PORT = 6667;private SocketChannel sc;private Selector selector;private String userName;public GroupChatClient() throws IOException {selector = Selector.open();//连接服务器sc = sc.open(new InetSocketAddress(HOST, PORT));//设置非阻塞sc.configureBlocking(false);//将channel注册到selectorsc.register(selector, SelectionKey.OP_READ);//得到usernameuserName = sc.getLocalAddress().toString().substring(1);System.out.println(userName + " is ok.....");}/*** 向服务器发送消息** @param info*/public void sendMsg(String info) {info = userName + " 说: " + info;try {sc.write(ByteBuffer.wrap(info.getBytes()));} catch(IOException e) {e.printStackTrace();}}/*** 读取服务器端的数据*/public void readMsg() {try {int readChannel = selector.select();if(readChannel > 0) {//有可用通道Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()) {SelectionKey key = iterator.next();if(key.isReadable()) {//得到相关通道SocketChannel socketChannel = (SocketChannel) key.channel();//得到一个BufferByteBuffer buffer = ByteBuffer.allocate(1024);//读取socketChannel.read(buffer);System.out.println("读取数据:" + new String(buffer.array()));}}//删除当前的selectionKey,防止重复操作,若不清空,其他客户端收到不到最新消息数据iterator.remove();}} catch(IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {GroupChatClient groupChatClient = new GroupChatClient();//启动一个线程,每隔3秒读取发送的数据new Thread() {public void run() {while(true){groupChatClient.readMsg();try {Thread.sleep(3000);} catch(InterruptedException e) {e.printStackTrace();}}}}.start();//发送数据给服务器端Scanner scanner = new Scanner(System.in);while(scanner.hasNextLine()){String s = scanner.nextLine();groupChatClient.sendMsg(s);}}}
通过idea运行GroupChatClient.java,多开几个客户端实现。实现如下截图:
总结:服务器端用一个线程通过多路复用器搞定所有的IO操作(包括连接、读、写等),编码简单,清晰明了,但是如果客户端连接数量较多,将无法支撑。要使用单Reactor多线程解决。
若有问题请留言。
相关文章:

java NIO群聊系统
demo要求: 1)编写一个NIO群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞) 2)实现多人群聊 3)服务器端:可以监测用户上线,离线,并实现消息转发功…...
ZCC5429 异步升压芯片
一、产品综述 ZCC5429 芯片是一款自动调频、最高 600KHz 工作频率、高效率、宽输入电压范围的电流模式异步升压(BOOST)芯片,且可调输入限流功能。用户可灵活地通过外部补偿建立动态环路,获得在所有条件下最优瞬态性能。 ZCC5429…...
复试专业前沿问题问答合集10-1——区块链与加密货币
复试专业前沿问题问答合集10-1——区块链与加密货币 区块链与加密货币安全以及6G通信的基础知识问答: 区块链以及加密货币相关的基础安全知识 包括区块链如何确保交易安全、共识机制的作用、加密货币钱包的保护措施、智能合约的工作原理以及如何防范潜在的网络攻击。这些知…...
redis【面试题】
目录 Java全技术栈面试题合集地址Redis篇1.Redis 的数据类型?2.Redis 是单进程单线程的?3.一个字符串类型的值能存储最大容量是多少?4.Redis 的持久化机制是什么?各自的优缺点?5.redis 过期键的删除策略?6.…...
linux下使用 tar 来压缩和解压 tar.gz 和 tar.xz 文件
linux下使用 tar 来压缩和解压 tar.gz 和 tar.xz 文件 文章目录 linux下使用 tar 来压缩和解压 tar.gz 和 tar.xz 文件1. 压缩 tar.gz 文件2. 解压 tar.gz 文件3. 压缩 tar.xz 文件4. 解压 tar.xz 文件 1. 压缩 tar.gz 文件 要创建 .tar.gz 文件(即使用 gzip 压缩的…...

Python环境下基于1D-CNN的轴承故障诊断及TSNE特征可视化
1D CNN 处理一维信号具有显著优势,已在很多领域得到初步应用: 心电图监测:将1DCNN应用于心脏病监测,其方法是针对每一个心脏病人的,即对于每个心律失常患者使用该患者特有的训练数据,专门训练出一个紧凑的…...

进程的调度,原则,算法
进程调度 进行上下问切换的时候根据什么原则来切换进程呢 进程的什么周期什么时候进行调度 调度原则 评价指标,作为调度算法的参考 调度算法的实现的标准 响应时间越小越好,平均响应时间的波动越小越好,稳定 (不能忽大忽小&am…...

瑞_23种设计模式_状态模式
文章目录 1 状态模式(State Pattern)1.1 介绍1.2 概述1.3 状态模式的结构1.4 状态模式的优缺点1.5 状态模式的使用场景 2 案例一2.1 需求2.2 代码实现(未使用状态模式)2.3 代码实现(状态模式) 3 案例二3.1 …...
system Verilog:clocking中定义信号为input和output的区别
在SystemVerilog中,clocking块用于定义时钟块,这通常用于描述时钟边缘和同步的输入/输出行为,特别是在测试平台和硬件接口描述中。 在下述两个代码示例中,主要区别在于a被定义为一个input还是output。 当a被定义为input时&#x…...

JAVA_Tomcat
Tomcat 使用教程 1.下载: http://tomcat.apache.org/ 2.安装: 解压压缩包(安装目录不要有中文) 3.卸载: 删除目录即可 4.启动: 运行./bin/startup.sh1.黑窗口一闪而过: 没有配置好JDK环境变量2.启动报错(查看日志文件): 端口占用 5.关闭: 1.强制关闭: 点击窗口关闭按钮2.正常…...

uniapp运行项目到微信小程序报错——未找到[“sitemapLocation“]
效果图 解决方式 重新运行项目即可;...
pytorch升级打怪(八)
保存模型和加载已有模型 保存并加载模型保存加载 保存并加载模型 在本节中,我们将研究如何通过保存、加载和运行模型预测来保持模型状态。 import torch import torchvision.models as models保存 PyTorch模型将学习的参数存储在内部状态字典中,称为s…...

全智能深度演进,一键成片让视频创作颠覆式提效
全智能一键成片,让内容创作的「边际成本」逼近于零。 大模型和AIGC技术的发展,可以用“日新月异”来形容,其迭代速度史无前例,涌现出的各类垂直应用模型,也使得音视频行业的应用场景更加广泛和多样化。 然而ÿ…...

uniapp(vue3) H5页面连接打印机并打印
一、找到对应厂商打印机的驱动并在windows上面安装。查看是否安装完成可以在:控制面板->查看设备和打印机,找到对应打印机驱动是否安装完成 二、打印机USB连接电脑 三、运行代码调用浏览器打印,主要使用的是window.print()功能。下面使用…...

Android视角看鸿蒙第八课(module.json5中的各字段含义之abilities)下
Android视角看鸿蒙第八课(module.json5中的各字段含义之abilities)下 导读 上篇文章开始学习abilities下的各字段含义,因为篇幅原因只学习了name、srcEntry、description、icon和label字段的含义和用法, 这篇文章继续学习和了解其他字段。 …...

设计模式 适配器模式
1.背景 适配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,虽然两个国家的的电源电压不同,中国是 220V,日本是 110V,但是这…...
前端面试题详解
前端面试 1.app如何实现登陆成功,卸载app重新安装再进入获取上一次已经登陆的信息? 要实现前端APP在登录成功后,即使卸载并重新安装也能获取上一次已经登录的信息,通常涉及以下几个关键步骤: 1. 使用持久化存储 在APP…...

抖音,剪映,TikTok,竖屏短视频转场pr模板视频素材
120个叠加效果视频转场过渡素材,抖音,剪映,TikTok,短视频转场pr模板项目工程文件。 效果:VHS、光效、胶片、霓虹灯闪光、X射线、信号、老电影等。 适用软件:Adobe Premiere Pro 2018 12.0或更高版本。 视频素材与大多数应用程序兼容ÿ…...

python网络相册设计与实现flask-django-nodejs-php
此系统设计主要采用的是python语言来进行开发,采用django框架技术,框架分为三层,分别是控制层Controller,业务处理层Service,持久层dao,能够采用多层次管理开发,对于各个模块设计制作有一定的安…...
设计模式: 外观模式
文章目录 一、什么是外观模式二、外观模式结构1、外观模式的主要角色包括:2、外观模式通常适用于以下情况: 三、优点 一、什么是外观模式 外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口&…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...