Netty学习——实战篇2 NIO 群聊系统(简单版) 备份

         需求:

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

        2、实现多人群聊

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

        4、客户端:通过channel可以无阻塞发送消息给其他所有用户,同时可以接受其他用户发送的消息。

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

        服务端代码:GroupChatServer.java

@Slf4j
public class GroupChatServer {//选择器private Selector selector;//监听器private ServerSocketChannel serverSocketChannel;//端口号private static final  int PORT = 8000;//构造方法,初始化成员变量public GroupChatServer(){try {//1 创建监听器serverSocketChannel = ServerSocketChannel.open();//2 创建选择器selector = Selector.open();//3 绑定端口号serverSocketChannel.socket().bind(new InetSocketAddress(PORT));//4 设置非阻塞模式serverSocketChannel.configureBlocking(false);//5 事件绑定serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);}catch (Exception e){e.printStackTrace();}}//监听public void listen(){log.info("监听的线程号是:{}",Thread.currentThread().getId());try {//循环等待客户端连接while(true){int count  = selector.select();if(count > 0){//表示有客户端连接//遍历得到selectorKeyIterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()){SelectionKey key = iterator.next();//判断事件监听类型if(key.isAcceptable()){/*//事件:连接SocketChannel socketChannel = serverSocketChannel.accept();//设置为非阻塞socketChannel.configureBlocking(false);//把socketchannel的读取事件类型注册到选择器上socketChannel.register(selector,SelectionKey.OP_READ);//提示用户上线log.info("用户,{}",socketChannel.getRemoteAddress(),"已上线");*/accept(serverSocketChannel);}if(key.isReadable()){readClientMessage(key);}iterator.remove();}}else{log.info("等待客户端连接");}}}catch (Exception e){e.printStackTrace();}finally {}}//客户端连接事件public void accept(ServerSocketChannel serverSocketChannel) throws Exception{//获取SocketChannelSocketChannel socketChannel = serverSocketChannel.accept();//设置SocketChannel 为非阻塞模式socketChannel.configureBlocking(false);//把读取事件绑定到选择器上socketChannel.register(selector,SelectionKey.OP_READ);//提示用户上线log.info("用户,{},已上线",socketChannel.getRemoteAddress());}//读取客户端信息public void readClientMessage(SelectionKey key){SocketChannel socketChannel = null;try {//根据key获取SocketChannelsocketChannel = (SocketChannel)key.channel();//创建ByteBufferByteBuffer buffer = ByteBuffer.allocate(1024);//channel 读取 bufferint count = socketChannel.read(buffer);//根据count的值做处理if(count > 0) {//把缓冲区的数据转换成字符串String msg = new String(buffer.array());//输出该消息log.info("来自客户端:{}, 的消息是:{}",socketChannel.getRemoteAddress(),msg);//向其他客户端转发消息(排除自己)sendMessageToOtherClients(msg,socketChannel);}}catch (Exception e){try {//提示离线log.info("{}",socketChannel.getRemoteAddress(),":已下线");//取消注册key.cancel();//关闭通道socketChannel.close();}catch (Exception e2){e2.printStackTrace();}}}public void sendMessageToOtherClients(String message,SocketChannel socketChannel) throws Exception{log.info("消息转发中。。。");log.info("服务器发送数据给客户端的线程是:{}",Thread.currentThread().getId());//遍历所有注册到selector的SocketChannel,排除自己for(SelectionKey key : selector.keys()){//通过key取出对应的SocketChannelChannel targetChannel =  key.channel();//排除自己if(targetChannel instanceof SocketChannel &&  targetChannel != socketChannel){SocketChannel dest = (SocketChannel) targetChannel;//把消息存储到ByteBufferByteBuffer buffer = ByteBuffer.wrap(message.getBytes());//把buffer的数据写入通道dest.write(buffer);}}}public static void main(String[] args) {GroupChatServer server = new GroupChatServer();server.listen();}}

        客户端 GroupChatClient.java

@Slf4j
public class GroupChatClient {//服务器IPprivate static final String HOST = "127.0.0.1";//服务器端口号private static final int PORT = 8000;//选择器private Selector selector;//SocketChannelprivate SocketChannel socketChannel;//用户名private String username;//构造器public GroupChatClient() throws Exception{//获取selectorselector = Selector.open();//连接到服务器socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));//设置为非阻塞socketChannel.configureBlocking(false);//将channel的读事件注册到selectorsocketChannel.register(selector, SelectionKey.OP_READ);//初始化usernameusername = socketChannel.getLocalAddress().toString().substring(1);log.info("{},客户端初始化完成",username);}//向服务器发送消息public void sendMessage(String message){message = username + " 说:" + message;try {socketChannel.write(ByteBuffer.wrap(message.getBytes()));//log.info("用户:{},说:{}",username,message);}catch (Exception e){e.printStackTrace();}}//读取从服务器获取的消息public void readMessage(){try {//获取socketChannel的通道数量int count = selector.select();//如果通道数量大于0,说明有可用的通道if(count > 0){//获取所有通道的迭代器Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//循环判断while(iterator.hasNext()){//获取keySelectionKey key = iterator.next();//如果事件类型是读取类型if(key.isReadable()){//获取相关通道SocketChannel channel = (SocketChannel) key.channel();//创建BytebufferByteBuffer buffer = ByteBuffer.allocate(1024);//读取bufferchannel.read(buffer);//把读取到缓冲区的数据转换成字符串,并输出String message = new String(buffer.array());log.info("{}",message);}}//删除当前的SelectionKey,方法重复注册iterator.remove();}}catch (Exception e){e.printStackTrace();}}public static void main(String[] args) throws Exception{//启动客户端GroupChatClient chatClient = new GroupChatClient();//启动一个线程,每隔3秒,从服务器读取数据new Thread(){public void run(){while(true){chatClient.readMessage();try {Thread.currentThread().sleep(3000);}catch (InterruptedException e){e.printStackTrace();}}}}.start();//发送数据到服务端Scanner scanner = new Scanner(System.in);while(scanner.hasNextLine()){String s = scanner.nextLine();chatClient.sendMessage(s);}}
}

        服务端运行结果:

        客户端1运行结果:

 

        客户端2运行结果:

 

        客户端3运行结果:

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

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

相关文章

分类算法(数据挖掘)

目录 1. 逻辑回归&#xff08;Logistic Regression&#xff09; 2. 支持向量机&#xff08;Support Vector Machine, SVM&#xff09; 3. 决策树&#xff08;Decision Tree&#xff09; 4. 随机森林&#xff08;Random Forest&#xff09; 5. K近邻&#xff08;K-Nearest …

Vue3(三):生命周期、路由、自定义hooks

这里终于明白了为什么一直有这个语法报错&#xff0c;就是在提示你哪里错的地方上方注释一行/*eslint-disable*/&#xff0c;之前一直警告这个错误感谢老师&#xff01; 一、vue2和vue3生命周期 还有一个问题就是父组件和子组件哪个先挂载完毕呢&#xff1f;答案是子组件先挂…

Qt---控件的基本属性

文章目录 enabled(控件可用状态)geometry(位置和尺寸)简单恶搞程序 windowIcon(顶层 widget 窗口图标)使用 qrc 机制 windowOpacity(窗口的不透明值)cursor(当鼠标悬停空间上的形状)自定义鼠标图标 toolTip(鼠标悬停时的提示)focusPolicy(控件获取焦点的策略)styleSheet(通过CS…

【Python实践应用】使用Python加载栅格数据

下面的代码实现的是加载伊宁市NDVI数据&#xff0c;首先进行相关的python包的导入&#xff0c;然后定义和读取我们需要加载的数据&#xff0c;这里我们使用的NDVI数据是将伊宁23年的NDVI数据合并成为了一张栅格图像&#xff0c;每个波段表示一年的 NDVI&#xff0c;我们这里显示…

MySQL学习笔记3——条件查询和聚合函数

条件查询和聚合函数 一、条件查询语句二、聚合函数1、SUM&#xff08;&#xff09;2、AVG()、MAX()、MIN()3、COUNT&#xff08;&#xff09; 一、条件查询语句 WHERE 和 HAVING 的区别&#xff1a; WHERE是直接对表中的字段进行限定&#xff0c;来筛选结果&#xff1b;HAVIN…

最新版IntelliJ IDEA 2024.1安装和配置教程 详细图文解说版安装教程

IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版 文章目录 IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版前言 第一步&#xff1a; IntelliJ IDEA 2024.1安装教程第 0 步&…

Java快速入门系列-7(测试与调试)

第七章:测试与调试 第7章:测试与调试7.1 单元测试(JUnit)7.1.1 为什么要进行单元测试7.1.2 JUnit基础7.1.3 断言7.1.4 测试套件7.2 集成测试与系统测试7.2.1 集成测试7.2.2 系统测试7.3 调试技巧与工具7.3.1 断点7.3.2 单步执行7.3.3 变量检查7.3.4 条件断点7.3.5 日志记录…

Playwright已经是目前最好的测试自动化工具了吗?

作者观点&#xff1a;很长时间以来&#xff0c;Selenium是QA工程师寻求测试自动化解决方案的首选测试框架。它能够测试任何浏览器&#xff08;这在IE浏览器的统治时期尤其重要&#xff09;和任何平台。然而&#xff0c;现在看来&#xff0c;那个时代已经过去了。 今天&#xf…

【嵌入式】SD NAND:小身躯蕴含大能量的新型嵌入式存储解决方案

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

如何在Linux通过docker搭建Plik文件系统并实现无公网IP管理内网文件

文章目录 1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 本文介绍如何使用Linux docker方式快速安装Plik并且结合Cpolar内网穿透工具实现远程访问&#xff0c;实现随时随地在任意设备上传或者…

JNI用法

提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一 背景 之前搞过jni&#xff0c;之前是强哥指导搞的&#xff0c;现在感觉又忘了。 今天照着帖子再搞一次。参考帖子&#xff1a;https://blog.csdn.net/y…

前端 接口返回来的照片太大 加载慢如何解决

现象 解决 1. 添加图片懒加载 背景图懒加载 对背景图懒加载做的解释 和图片懒加载不同&#xff0c;背景图懒加载需要使用 v-lazy:background-image&#xff0c;值设置为背景图片的地址&#xff0c;需要注意的是必须声明容器高度。 <div v-for"img in imageList&quo…

交叉熵损失函数介绍

交叉熵是信息论中的一个重要概念&#xff0c;它的大小表示两个概率分布之间的差异&#xff0c;可以通过最小化交叉熵来得到目标概率分布的近似分布。 为了理解交叉熵&#xff0c;首先要了解下面这几个概念。 自信息 信息论的基本想法是&#xff0c;一个不太可能的事件发生了…

openwrt局域网配置多个IP

在局域网配置过程中&#xff0c;若是DHCP服务器关闭&#xff0c;又忘记了配置的ip&#xff0c;将很难访问到路由器重新进行配置。这种情况可以在路由器出厂时做一个备用ip去避免。 1.配置 以下是备用ip的配置方法&#xff0c;以SKYLAB的SKW99 WIFI模组为例进行说明&#xff1…

如何在树莓派安装Nginx并实现固定公网域名访问本地静态站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

LeetCode 59.螺旋矩阵II

LeetCode 59.螺旋矩阵II 1、题目 力扣题目链接&#xff1a;59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1…

你想来微软苏黎世混合现实研究中心学习和实习吗?

Microsoft Mixed Reality & AI Lab - Zurich 苏黎世混合现实研发中心简介 微软苏黎世混合现实与人工智能实验室概况 专注于利用计算机视觉绘制和理解环境&#xff0c;识别和跟踪相关物体&#xff0c;并帮助用户执行任务&#xff0c;构建混合现实的未来。该实验室还在探索混…

API接口淘宝开放平台item_get-获得淘宝商品详情API接口根据商品ID查询商品标题价格描述等详情数据

要使用淘宝开放平台的item_get API接口根据商品ID查询商品标题、价格、描述等详情数据&#xff0c;你需要先注册一个KEY账号&#xff0c;然后获取到api_key和api_secret。接下来&#xff0c;你可以使用Python的requests库来调用API接口。 以下是一个示例代码&#xff1a; # c…

网络管理实验二、SNMP服务与常用的网管命令

1 常用的网管命令 1.1 网络状态监视命令 包括以下命令&#xff1a;Ipconfig、ping、nslookup、dig、host ipconfig 作用&#xff1a;用来显示本机所有网卡的基本信息&#xff08;IP、掩码、网关、工作状态&#xff09;&#xff1b;用法&#xff1a;ipconfig展示&#xff1a;…