netty学习(1):多个客户端与服务器通信

news2024/11/15 23:26:13

1. 基于前面一节netty学习(1):1个客户端与服务器通信

只需要把服务器的handler改造一下即可,通过ChannelGroup 找到所有的客户端channel,发送消息即可。

package server;

import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * 自定义处理Handler
 */
public class NettyServiceHandler extends SimpleChannelInboundHandler<String> {
    // 创建一个ChannelGroup,其是一个线程安全的集合,其中存放着与当前服务器相连接的所有Active状态的Channel
    // GlobalEventExecutor是一个单例、单线程的EventExecutor,是为了保证对当前group中的所有Channel的处理
    // 线程是同一个线程
    private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    // 只要有客户端Channel与服务端连接成功就会执行这个方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 获取到当前与服务器连接成功的channel
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + "---上线");
        group.writeAndFlush(channel.remoteAddress() + "---上线\n");
        // 将当前channel添加到group中
        group.add(channel);

//        NettyService.remoteAddressMap.put(channel.remoteAddress().toString(), ctx);
    }

    // 只要有客户端Channel断开与服务端的连接就会执行这个方法
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 获取到当前要断开连接的Channel
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + "------下线");
        group.writeAndFlush(channel.remoteAddress() + "下线,当前在线人数:" + group.size() + "\n");

        // group中存放的都是Active状态的Channel,一旦某Channel的状态不再是Active,
        // group会自动将其从集合中踢出,所以,下面的语句不用写
        // remove()方法的应用场景是,将一个Active状态的channel移出group时使用
        // group.remove(channel);
    }

    /**
     * channelRead,这个方法当有数据读写的时候,会触发,可以读取客户端的消息
     * 只要有客户端Channel给当前的服务端发送了消息,那么就会触发该方法的执行
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel channel = ctx.channel();
        System.out.println("netty客户端" + channel.remoteAddress() + "发送过来的消息:" + msg);
//        channel.writeAndFlush("自己发的消息:" + msg + "\n");

        group.forEach(ch -> { // JDK8 提供的lambda表达式
            if (ch != channel) {
                ch.writeAndFlush(channel.remoteAddress() + ":" + msg + "\n");
            } else {
                channel.writeAndFlush("自己发的消息:" + msg + "\n");
            }
        });
//        String[] split = msg.split(":");
//        if (split.length >= 2) { //指定客户端时发送给指定客户
//            System.out.println("发送给客户:" + split[0]);
//            if (NettyService.userIdMap.get(split[0]) != null) {
//                NettyService.userIdMap.get(split[0]).writeAndFlush(channel.remoteAddress() + ":" + split[1] + "\n");
//            } else {
//                NettyService.userIdMap.put(split[0], ctx);  //第一次发送消息时注册客户端的channel
//            }
//        } else { //否则发送给所有的客户端
//            System.out.println("广播");
//            // 遍历channelGroup,从而区分“我”和“别人”发出的消息,如果消息是自己发出的就显示“我”
//            group.forEach(ch -> { // JDK8 提供的lambda表达式
//                if (ch != channel) {
//                    ch.writeAndFlush(channel.remoteAddress() + ":" + msg + "\n");
//                } else {
//                    channel.writeAndFlush("自己发的消息:" + msg + "\n");
//                }
//            });
//        }
    }
    /**
     * channelReadComplete,数据读取完毕之后,需要做的业务操作,回消息
     */
//    public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception {
//        //消息出站
//        System.out.println("Netty服务端读取消息完毕");
//        channelHandlerContext.writeAndFlush(Unpooled.copiedBuffer("--over", CharsetUtil.UTF_8));
//    }

    /**
     * exceptionCaught,发生异常的handler
     */
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable throwable) throws Exception {
        throwable.printStackTrace();
        channelHandlerContext.close();
    }
}

2. 测试

创建3个客户端

package test;

import client.NettyClient;

public class Client3 {
    public static void main(String[] args) {
        new NettyClient("127.0.0.1",6666).createNettyClient();
    }
}

每个客户端都发送消息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
服务器
在这里插入图片描述

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

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

相关文章

MS1826 HDMI 多功能视频处理器 4*4矩阵切换器

基本介绍 MS1826 是一款多功能视频处理器&#xff0c;包含 4 路独立 HDMI 音视频输入通道、4 路独立 HDMI 音视频输出通道以及四路独立可配置为输入或者输出的 SPDIF、I2S 音频信号。支持 4 个独立的字 库定制型 OSD&#xff1b;可处理隔行和逐行视频或者图形输入信号&#xff…

Spring Boot 中的 @ComponentScan 注解是什么,原理,如何使用

Spring Boot 中的 ComponentScan 注解是什么&#xff0c;原理&#xff0c;如何使用 在 Spring Boot 中&#xff0c;ComponentScan 是一种注解&#xff0c;它可以让 Spring 自动扫描指定的包及其子包中的组件&#xff0c;并将这些组件自动装配到 Spring 容器中。本文将介绍 Com…

UML14种图

UML14种图 UML是Unified Modeling Language的缩写&#xff0c;译为统一建模语言。 UML是软件行业的建模规范&#xff0c;可以对软件项目建立需求模型、设计模型、实现模型、测试模型。 UML2.0包含的14种图&#xff1a; UML各种图例&#xff08;常用图形&#xff09; 1. 类图&…

状态机编程实例-状态表法

上篇文章&#xff0c;使用嵌套switch-case法的状态机编程&#xff0c;实现了一个炸弹拆除小游戏。 本篇&#xff0c;继续介绍状态机编程的第二种方法&#xff1a;状态表法&#xff0c;来实现炸弹拆除小游戏的状态机编程。 1 状态表法 状态表法&#xff0c;顾名思义&#xff0…

YOLOv8的目标对象的分类,分割,跟踪和姿态估计的多任务检测实践(Netron模型可视化)

YOLOv8是目前最新版本&#xff0c;在以前YOLO版本基础上建立并加入了一些新的功能&#xff0c;以进一步提高性能和灵活性&#xff0c;是目前最先进的模型。YOLOv8旨在快速&#xff0c;准确&#xff0c;易于使用&#xff0c;使其成为广泛的目标检测和跟踪&#xff0c;实例分割&a…

MATLAB 之 Simulink 操作基础和系统仿真模型的建立

这里写目录标题 一、Simulink 操作基础1. Simulink 的启动与退出1.1 Simulink 的启动1.2 模型文件的打开1.3 Simulink 的退出 2. Simulink 仿真初步2.1 模型元素2.2 仿真步骤2.3 简单实例 二、系统仿真模型的建立1. Simulink 的基本模块2. 模块操作2.1 添加与删除模块2.2 选取模…

快速训练自己的大语言模型:基于LLAMA-7B的lora指令微调

目录 1. 选用工程&#xff1a;lit-llama2. 下载工程3. 安装环境4. 下载LLAMA-7B模型5. 做模型转换6. 初步测试7. 为什么要进行指令微调&#xff1f;8. 开始进行指令微调8.1. 数据准备8.2 开始模型训练8.3 模型测试 前言&#xff1a; 系统&#xff1a;ubuntu 18.04显卡&#xff…

大数据ETL工具对比(Sqoop, DataX, Kettle)

前言 在实习过程中&#xff0c;遇到了数据库迁移项目&#xff0c;对于数据仓库&#xff0c;大数据集成类应用&#xff0c;通常会采用ETL工具辅助完成&#xff0c;公司和客户使用的比较多的是Sqoop, DataX和Kettle这三种工具。简单的对这三种ETL工具进行一次梳理。 ETL工具&…

无法更新iPhone,提示“无法检查更新”怎么办?

当我们需要 iPhone更新系统时&#xff0c;可以前往iPhone设置-通用-软件更新中获取更新推送。不过一些用户可能会遇到无法更新的问题&#xff0c;例如会提示“无法检查更新&#xff0c;检查软件更新时出错”。 以上情况可能是网络问题&#xff0c;可以尝试重新打开设置&#xf…

vue2实现公式规则编辑校验弹窗功能

文章目录 需求描述技术栈最终效果演示功能实现逻辑拆分代码目录结构实现思路光标实现底部单个符号或字段结构设计监听键盘事件&处理光标公式规则校验 总结 需求描述 需要一个弹窗&#xff0c;弹窗内部需要能够进行公式规则的配置并进行公式规则合法性校验。 技术栈 vue2e…

Thymeleaf的常用语法

&#x1f31f; Thymeleaf的常用语法 Thymeleaf是一个Java模板引擎&#xff0c;用于处理HTML、XML、JavaScript、CSS等文件。它可以与Spring框架无缝集成&#xff0c;为Web应用程序提供优雅的模板解决方案。本文将介绍Thymeleaf的常用语法&#xff0c;包括th属性、表达式、内置…

Spring Boot 中的 @RestController 注解,如何使用

Spring Boot 中的 RestController 注解 在 Spring Boot 中&#xff0c;我们经常需要编写 RESTful Web 服务&#xff0c;以便于客户端与服务器之间的通信。为了简化 RESTful Web 服务的开发&#xff0c;Spring Boot 提供了 RestController 注解&#xff0c;它可以让我们更方便地…

Jmeter实现参数加密

目录 一、使用__digest自带函数 以md5加密算法演示使用方法 二、在BeanShell 中使用JAVA代码实现算法加密 规避BUG的方法 JMeter有两种方法可以实现算法加密 一、使用__digest自带函数 参数说明&#xff1a; Digest algorithm&#xff1a;算法摘要&#xff0c;可输入值&a…

如何避免在处理数据时出现错误?

介绍 当业务运营管道中发生大量事情时&#xff0c;数据输入任务的优先级往往较低。但是&#xff0c;数据输入被认为是所有行业部门中经常发生的最重要的任务之一。从销售数据到客户分析&#xff0c;从财务数据到库存管理&#xff0c;每项工作都依赖于数据输入&#xff0c;这意…

前端vue入门(纯代码)16

【18.如何在Vue中配置代理服务器】 vue脚手架配置代理总结&#xff1a;修改了vue.config.js文件后必须重启项目【npm run serve】 方法一 ​ 在vue.config.js中添加如下配置&#xff1a; devServer:{proxy:"http://localhost:5000" }说明&#xff1a; 优点&#…

家政上门预约小程序;

家政上门预约小程序开发是一款本地生活类服务上门预约系统&#xff0c;覆盖家政保洁、保姆月嫂、上门维修、管道疏通、上门安装等各种到家服务。可以帮助创业者在不需要相关技术人员及大量资金投入的情况下&#xff0c;就能够轻松搭建并运营一个上门家政服务平台。 那么开发一…

【零基础入门学习Python---Python错误处理和异常保姆级教程】

&#x1f680; Python &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【图像处理OpenCV(C++版)】——5.3 图像平滑之均值平滑(滤波)

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…

Flutter学习四:Flutter开发基础(一)Widget

目录 0 引言 1 Widget 简介 1.1 Widget 概念 1.2 Widget 接口 1.3 Flutter中的四棵树 1.4 StatelessWidget 1.4.1 简介 1.4.2 Context上下文 1.5 StatefulWidget 1.6 State 1.6.1 简介 1.6.2 State生命周期 1.7 在 widget 树中获取State对象 1.7.1 通过Context…

详解如何使用nvm管理Node.js多版本

目录 NVM进行NodeJS多版本管理 背景 安装步骤 1. 下载nvm安装包 2. 安装nvm 使用步骤 下载nodejs 切换版本nodejs ​编辑 常用命令 NVM进行NodeJS多版本管理 背景 有的时候开发环境需要多个NodeJS的版本&#xff0c;这个时候就可以用NVM进行管理。 安装步骤 1. 下载n…