当前位置: 首页 > news >正文

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要求&#xff1a; 1&#xff09;编写一个NIO群聊系统&#xff0c;实现服务器端和客户端之间的数据简单通讯&#xff08;非阻塞&#xff09; 2&#xff09;实现多人群聊 3&#xff09;服务器端&#xff1a;可以监测用户上线&#xff0c;离线&#xff0c;并实现消息转发功…...

ZCC5429 异步升压芯片

一、产品综述 ZCC5429 芯片是一款自动调频、最高 600KHz 工作频率、高效率、宽输入电压范围的电流模式异步升压&#xff08;BOOST&#xff09;芯片&#xff0c;且可调输入限流功能。用户可灵活地通过外部补偿建立动态环路&#xff0c;获得在所有条件下最优瞬态性能。 ZCC5429…...

复试专业前沿问题问答合集10-1——区块链与加密货币

复试专业前沿问题问答合集10-1——区块链与加密货币 区块链与加密货币安全以及6G通信的基础知识问答: 区块链以及加密货币相关的基础安全知识 包括区块链如何确保交易安全、共识机制的作用、加密货币钱包的保护措施、智能合约的工作原理以及如何防范潜在的网络攻击。这些知…...

redis【面试题】

目录 Java全技术栈面试题合集地址Redis篇1.Redis 的数据类型&#xff1f;2.Redis 是单进程单线程的&#xff1f;3.一个字符串类型的值能存储最大容量是多少&#xff1f;4.Redis 的持久化机制是什么&#xff1f;各自的优缺点&#xff1f;5.redis 过期键的删除策略&#xff1f;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 文件&#xff08;即使用 gzip 压缩的…...

Python环境下基于1D-CNN的轴承故障诊断及TSNE特征可视化

1D CNN 处理一维信号具有显著优势&#xff0c;已在很多领域得到初步应用&#xff1a; 心电图监测&#xff1a;将1DCNN应用于心脏病监测&#xff0c;其方法是针对每一个心脏病人的&#xff0c;即对于每个心律失常患者使用该患者特有的训练数据&#xff0c;专门训练出一个紧凑的…...

进程的调度,原则,算法

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

瑞_23种设计模式_状态模式

文章目录 1 状态模式&#xff08;State Pattern&#xff09;1.1 介绍1.2 概述1.3 状态模式的结构1.4 状态模式的优缺点1.5 状态模式的使用场景 2 案例一2.1 需求2.2 代码实现&#xff08;未使用状态模式&#xff09;2.3 代码实现&#xff08;状态模式&#xff09; 3 案例二3.1 …...

system Verilog:clocking中定义信号为input和output的区别

在SystemVerilog中&#xff0c;clocking块用于定义时钟块&#xff0c;这通常用于描述时钟边缘和同步的输入/输出行为&#xff0c;特别是在测试平台和硬件接口描述中。 在下述两个代码示例中&#xff0c;主要区别在于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“]

效果图 解决方式 重新运行项目即可&#xff1b;...

pytorch升级打怪(八)

保存模型和加载已有模型 保存并加载模型保存加载 保存并加载模型 在本节中&#xff0c;我们将研究如何通过保存、加载和运行模型预测来保持模型状态。 import torch import torchvision.models as models保存 PyTorch模型将学习的参数存储在内部状态字典中&#xff0c;称为s…...

全智能深度演进,一键成片让视频创作颠覆式提效

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

uniapp(vue3) H5页面连接打印机并打印

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

Android视角看鸿蒙第八课(module.json5中的各字段含义之abilities)下

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

设计模式 适配器模式

1.背景 适配器模式&#xff0c;这个模式也很简单&#xff0c;你笔记本上的那个拖在外面的黑盒子就是个适配器&#xff0c;一般你在中国能用&#xff0c;在日本也能用&#xff0c;虽然两个国家的的电源电压不同&#xff0c;中国是 220V&#xff0c;日本是 110V&#xff0c;但是这…...

前端面试题详解

前端面试 1.app如何实现登陆成功&#xff0c;卸载app重新安装再进入获取上一次已经登陆的信息&#xff1f; 要实现前端APP在登录成功后&#xff0c;即使卸载并重新安装也能获取上一次已经登录的信息&#xff0c;通常涉及以下几个关键步骤&#xff1a; 1. 使用持久化存储 在APP…...

抖音,剪映,TikTok,竖屏短视频转场pr模板视频素材

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

python网络相册设计与实现flask-django-nodejs-php

此系统设计主要采用的是python语言来进行开发&#xff0c;采用django框架技术&#xff0c;框架分为三层&#xff0c;分别是控制层Controller&#xff0c;业务处理层Service&#xff0c;持久层dao&#xff0c;能够采用多层次管理开发&#xff0c;对于各个模块设计制作有一定的安…...

设计模式: 外观模式

文章目录 一、什么是外观模式二、外观模式结构1、外观模式的主要角色包括&#xff1a;2、外观模式通常适用于以下情况&#xff1a; 三、优点 一、什么是外观模式 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供了一个统一的接口&…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...