java NIO群聊系统

demo要求:

1)编写一个NIO群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)

2)实现多人群聊

3)服务器端:可以监测用户上线,离线,并实现消息转发功能。

4)客户端:通过channel可以无阻塞发送消息给其他所有用户(客户端),同时可以接受其他用户发送的消息(由服务器转发得到)

5)目的:进一步理解NIO非阻塞网络编程机制。

以下代码:

服务器端实现的代码:

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,多开几个客户端实现。实现如下截图:

若有问题请留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/760390.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 爬虫 地理空间DEM 制作中国地形

一.配置Python 爬虫 环境 from selenium import webdriver import time # from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keys # from selenium.webdriver.comm…

Langchain-chatchat+ChatGlm3-6b部署

我的环境 升级了下配置&#xff0c;加载知识库成功 内存&#xff1a;16GB 32B 显卡&#xff1a;GTX1060-6G RTX4080 Laptop-12G 1. 基础环境准备 1.1. 安装anaconda&#xff0c;创建环境python版本3.11 conda create -n chatglm3 python3.11 conda activate chatglm3 1.…

如何在CentOS搭建docker compose ui可视化工具并无公网IP远程管理容器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

蓝桥杯 2023 省A 更小的数

主要思路&#xff1a; 输入一个长度为n的字符串&#xff0c;用二维数组dp[i][j]来记录子串[i, j]是否需要反转一次才能满足条件。使用动态规划自底向上地填充dp数组。根据问题的要求&#xff0c;需要考虑字符串的子串中字符的大小关系来判断是否需要反转。最后统计满足条件的子…

静态扭矩,是用公称扭矩还是最小动态扭矩作为对比?——​suntorque智能扭矩系统​

在机械工程和动力学领域&#xff0c;扭矩是一个至关重要的概念。当我们讨论到扭矩时&#xff0c;常常会遇到静态扭矩和动态扭矩的说法。特别是在实际应用中&#xff0c;工程师们经常需要对比和选择使用哪种扭矩作为参考。那么&#xff0c;静态扭矩&#xff0c;是用公称扭矩还是…

【C/C++】详解 assert() 断言(什么是assert? assert有什么作用?)

目录 一、前言 二、什么是 assert ? 三、assert 的用法 四、assert 案例解析 五、assert 断言的使用原则 六、共勉 一、前言 在编写程序过程中&#xff0c;尤其是调试代码时&#xff0c;往往需要一个提醒代码漏洞/Bug的小助手&#xff0c;以便于程序员及时修改和完善代码…

109 项目整合 spring-quartz 启动自动执行定时任务

前言 项目中使用了 quartz 来支持定时任务的相关基础支撑, 但是 最近添加了一个 资源消耗比较高的定时任务, 发布到测试环境之后, 发现服务突然 起不起来了[资源比较有限] 然后 查看了一下日志, 这个定时任务怎么在执行?, 不是 配置的是 凌晨两点么, 然后 仔细一看 几乎配置…

python课后习题一

题目&#xff1a; 1. 2. 解题过程&#xff1a; 1. """计算年数和天数""" minute int(input("请输入分钟数&#xff1a;")) hours minute // 60 days hours // 24 years days // 365 last_days days % 365 print(f"{minut…

【IEDM2023】背势垒电荷运动诱导GaN HEMT随时间的非稳态击穿

分享一篇2023年IEDM上GaN HEMT&#xff08;高电子迁移率晶体管&#xff09;的研究论文&#xff0c;标题为“Charge Movement in Back Barrier Induced Time-Dependent On-State Breakdown of GaN HEMT”。论文讨论了在GaN HEMT中&#xff0c;由于背栅&#xff08;Back Barrier&…

BigDecimal类的使用,用于精确计算任意精度的数字

BigDecimal类 BigDecimal 是 Java 中用于精确表示任意精度的十进制数的类。在很多情况下,使用基本数据类型(如 double 或 float)进行浮点数计算可能会导致精度丢失或舍入错误。BigDecimal 提供了一种更精确的解决方案,可以处理需要高精度计算的场景,比如财务应用或科学计算…

力扣---括号生成---回溯---dfs/二进制

暴力--二进制 采用与&#xff1a;力扣---子集---回溯&#xff08;子集型回溯&#xff09;---递归-CSDN博客 中二进制求解一样的思路&#xff0c;即遍历0~-1&#xff08;从二进制去考虑&#xff09;&#xff0c;如果这个数的第 i 位为0&#xff0c;则括号的第 i 位为‘&#xff…

Excel第27享:基于if函数嵌套的多结果唯一性匹配

1、需求描述 如下图所示&#xff0c;现在有E列、F列、G列三列数据&#xff0c;在D列中填充“最终对应编号”&#xff0c;匹配原则是&#xff1a;E列、F列、G列三列数据中&#xff0c;哪个有数据就填充哪个数据&#xff0c;如果都没有&#xff0c;就显示空值即可。 2、解决思路…

redis 常见的异常

目录 一、缓存穿透 1、概念 解决方案 &#xff08;1&#xff09;布隆过滤器 (2)、缓存空对象 二、缓存雪崩 1、概念 解决方案 &#xff08;1&#xff09;redis高可用 &#xff08;2&#xff09;限流降级 &#xff08;3&#xff09;数据预热 一、缓存穿透 1、概念 缓…

【Greenhills】GHS-MULTI IDE-Ubuntu纯命令系统部署license文件

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 记录在Ubuntu纯命令系统中部署license文件的步骤。 2、 问题场景 客户服务器为Linux纯命令行的环境&#xff0c;客户也无其他服务器可以部署&#xff0c;需在纯命令行上尝试安装。 3、软硬件环境 1&#xff09…

Windows系统部署eXtplorer文件管理器结合内网穿透构建私人云存储服务器

文章目录 1. 前言2. eXtplorer网站搭建2.1 eXtplorer下载和安装2.2 eXtplorer网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1. 前言 通过互联网传输文件&#xff0c;是互联网最重要的应用之一&#xff0c;无论是…

8.测试教程-自动化测试selenium-3

文章目录 1.unittest框架解析2.批量执行脚本2.1构建测试套件2.2用例的执行顺序2.3忽略用例执行 3.unittest断言4.HTML报告生成5.异常捕捉与错误截图6.数据驱动 大家好&#xff0c;我是晓星航。今天为大家带来的是 自动化测试selenium第三节 相关的讲解&#xff01;&#x1f600…

ISIS邻居以及路由计算

ISIS报文细节&#xff0c;ISIS报文分为三个部分&#xff1a; 1、通用报文头 Intradomain Routeing Pro Dis&#xff1a;ISIS&#xff1a;域内路由协议鉴别符&#xff1a;0x83代表ISIS协议 Length Indicator&#xff1a;27 头部长度&#xff08;包含了通用头部和专用头部&…

【原创】三十分钟实时数据可视化网站前后端教程 Scrapy + Django + React 保姆级教程向

这个本来是想做视频的&#xff0c;所以是以讲稿的形式写的。最后没做视频&#xff0c;但是觉得这篇文还是值得记录一下。真的要多记录&#xff0c;不然一些不常用的东西即使做过几个月又有点陌生了。 文章目录 爬虫 SCRAPYxpath 后端 DJANGO前端 REACT Hello大家好这里是小鱼&a…

【spring】@Conditional注解学习

Conditional介绍 Conditional注解用于按照设定的条件进行判断&#xff0c;从而决定是否将某个bean注册到Spring容器中。 Conditional注解是在Spring 4.0版本中引入的&#xff0c;它提供了一种更加灵活的方式来控制bean的创建和注册。在此之前&#xff0c;开发者通常使用Profi…

Automated and Cooperative Vehicle Merging at Highway On-Ramps

论文解读——Automated and Cooperative Vehicle Merging at Highway On-Ramps 参考资料来源&#xff1a;《Automated and Cooperative Vehicle Merging at Highway On-Ramps》一文 1.问题描述 该文章想解决如FIG.1中的匝道合流问题&#xff0c;传统的匝道合理需要等待main r…