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)是一种结构型设计模式,它提供了一个统一的接口&…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...
RabbitMQ 各类交换机
为什么要用交换机? 交换机用来路由消息。如果直发队列,这个消息就被处理消失了,那别的队列也需要这个消息怎么办?那就要用到交换机 交换机类型 1,fanout:广播 特点 广播所有消息:将消息…...
