netty编程之广播消息

news2024/11/29 8:42:11

写在前面

本文看下使用netty如何实现广播消息,主要依赖于类io.netty.channel.group.ChannelGroup

1:正戏

首先定义server:

package com.dahuyou.netty.broadcastmsg;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {

    public static void main(String[] args) {
        new NettyServer().bing(7397);
    }

    private void bing(int port) {
        //配置服务端NIO线程组
        EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)    //非阻塞模式
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(new MyChannelInitializer());
            ChannelFuture f = b.bind(port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            childGroup.shutdownGracefully();
            parentGroup.shutdownGracefully();
        }

    }

}

MyChannelInitializer通道初始化类:

package com.dahuyou.netty.broadcastmsg;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.Charset;

public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) {
        /*System.out.println("远端连接初始化,IP:" + channel.remoteAddress().getHostString() + ", port:" + channel.remoteAddress().getPort());*/

        /* 解码器 */
        // 基于换行符号,基于换行符来分割字符串???
//        channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
        // 基于指定字符串【换行符,这样功能等同于LineBasedFrameDecoder】
        // e.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, false, Delimiters.lineDelimiter()));
        // 基于最大长度
        // e.pipeline().addLast(new FixedLengthFrameDecoder(4));
        // 解码转String,注意调整自己的编码格式GBK、UTF-8 byte->string
        channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));

        // 增加内置编码器,这样向对端发送消息就不要转换为二进制数据了
        channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
        //在管道中添加我们自己的接收数据实现方法
        channel.pipeline().addLast(new MyServerHandler());

    }

}

在通道初始化类中定义了接收消息的字符串解码器和发送消息的字符串编码器,以及自定义了接收消息的处理器MyServerHandler:

package com.dahuyou.netty.broadcastmsg;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.SocketChannel;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 通道激活
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketChannel channel = (SocketChannel) ctx.channel();
        // 添加通道到通道组中
        ChannelHandler.channelGroup.add(ctx.channel());

        System.out.println("远端连接初始化,IP:" + channel.remoteAddress().getHostString() + ", port:" + channel.remoteAddress().getPort());

        // 通知客户端链接建立成功
        String str = "notify channel build success: " + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";
        /*ByteBuf buf = Unpooled.buffer(str.getBytes().length);
        buf.writeBytes(str.getBytes("GBK"));
        ctx.writeAndFlush(buf);*/
        ctx.writeAndFlush(str);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        /*//接收msg消息
        ByteBuf buf = (ByteBuf) msg;
        byte[] msgByte = new byte[buf.readableBytes()];
        buf.readBytes(msgByte);
        System.out.print(new Date() + "接收到消息:");
        System.out.println(new String(msgByte, Charset.forName("GBK")));*/


        // 因为在初始化是已经设置了字符串的解码器,所以就不需要上述的手动读取二进制字节码的操作了,这就是框架的好处咯!
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg);


        // 通知客户端链消息发送成功
        /*String str = "server received your msg: " + new Date() + " " + msg + "\r\n";
        ByteBuf buf = Unpooled.buffer(str.getBytes().length);
        buf.writeBytes(str.getBytes("GBK"));
        ctx.writeAndFlush(buf);*/
        String str = "server received your msg: " + new Date() + " " + msg + "\r\n";
//        ctx.writeAndFlush(str);
        ChannelHandler.channelGroup.writeAndFlush(str);


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        System.out.println("异常信息:\r\n" + cause.getMessage());
    }

    /**
     * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 从通道组中移除通道
        ChannelHandler.channelGroup.remove(ctx.channel());
        System.out.println("客户端断开链接" + ctx.channel().localAddress().toString());
    }
}

主要代码是ChannelHandler.channelGroup,用来维护连接中的通道,并通过代码ChannelHandler.channelGroup.writeAndFlush(str);来实现将消息发送到通过方法ChannelHandler.channelGroup.add(ctx.channel());添加的所有通道。ChannelHandler代码如下:

package com.dahuyou.netty.broadcastmsg;

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

public class ChannelHandler {

    //用于存放用户Channel信息,也可以建立map结构模拟不同的消息群
    // 这里的一个通道可以映射为群聊的一个群,如果是有多个群的话,创建多个通道组就可以了,之后使用比如map结构就可以维护
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

}

接着我们就可以启动2个client来测试,首先启动第一个:
在这里插入图片描述
启动第二个:
在这里插入图片描述
使用其中一个说一句话,server的回复另一个client也可以收到:
在这里插入图片描述

写在后面

参考文章列表

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

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

相关文章

Midjourney中文版教程:参数详解

1.长宽比 可以设置图片的纵横比。按照需求可以选择不同的尺寸&#xff0c;也可以自定义。 注意&#xff1a;--ar必须使用整数。使用139&#xff1a;100代替1.39&#xff1a;1。 长宽比会影响生成图像的形状和构图。 在放大时&#xff0c;某些长宽比可能会稍微改变。 较旧的…

「字符串」详解AC自动机并实现对应的功能 / 手撕数据结构(C++)

前置知识 在此前&#xff0c;你应该首先了解trie树&#xff08;字典树&#xff09;的概念&#xff1a; 「字符串」详解Trie&#xff08;字典树|前缀树&#xff09;并实现对应的功能 / 手撕数据结构&#xff08;C&#xff09; 我们建议你先阅读这篇文章&#xff0c;以了解我们…

C语言-用指向指针的指针的方法对n个整数排序并输出。要求将排序单独写成一个函数。n个整数在主函数中输入,最后在主函数中输出。

题目要求&#xff1a; 用指向指针的指针的方法对n个整数排序并输出。要求将排序单独写成一个函数。n个整数在主函数中输入&#xff0c;最后在主函数中输出。 程序&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int arr[100];int* pa…

王者壁纸下载站照片墙-(HTML+CSS)

效果图&#xff1a; 参考源代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>王者荣耀高清壁纸</title><style>#main{width:1366px;height:768px;margin-top:20px;}#main img{width:350px;}…

算法的学习笔记—从上往下打印二叉树(牛客JZ32)

&#x1f600;前言 在二叉树的遍历问题中&#xff0c;从上往下、从左到右打印出二叉树的每个节点值是一种常见的题目类型。这种遍历方式与广度优先搜索&#xff08;BFS&#xff09;密切相关&#xff0c;常常用于需要层次遍历&#xff08;Level-order Traversal&#xff09;的场…

PCIE-Precode

Transmitter Precode Request: Precoding可以有效的降低Burst errors&#xff08;突发连续&#xff09;的影响&#xff0c;但是Bit Error Rate&#xff08;BER&#xff09;将上升为之前的两倍. 32GT/s就必须使用这项功能吗&#xff1f;&#xff1f; [FPGA 实现及PCIe IP 核知…

SD 卡数据恢复免费版软件推荐及无法读取修复指南!

SD 卡数据怎么恢复&#xff1f;SD卡无法读取怎么修复&#xff1f;有没有SD卡数据恢复免费版&#xff1f; 在如今数字化的时代&#xff0c;SD 卡作为我们存储珍贵记忆和重要文件的常用工具&#xff0c;一旦出现数据丢失或无法读取的情况&#xff0c;往往会让人感到焦虑和无助。…

JAVA中的语序

目录 1. 顺序 2. 分支 2.1 if语句 2.1.1 单一条件的if语句 2.1.2 if-else语句 2.1.3 多层if-else语句 2.2 switch语句 3. 循环 3.1 for循环 3.2 while循环 3.3 do-while循环 3.4 break和continue 语序即代码运行的顺序&#xff0c;主要分为三种&#xff0c;顺序、分…

Java数组怎么转List,Stream的基本方法使用教程

Stream流 Java 的 Stream 流操作是一种简洁而强大的处理集合数据的方式,允许对数据进行高效的操作,如过滤、映射、排序和聚合。Stream API 于 Java 8 引入,极大地简化了对集合(如 List、Set)等数据的处理。 一、创建 Stream 从集合创建: List<String> list = Ar…

C#用户控件usercontrol中的子控件事件及属性的传递

也不知道这个标题怎么写&#xff0c;但是问题是个老问题&#xff0c;大家都可能遇到过&#xff0c;不过有同学问到&#xff0c;那就写出来。其实很简单。只不过有的同学看了其他博文后脑子还是懵懵的。所以这里就分两部分来说明一下。 文章目录 一、属性的传递1、原理2、步骤3…

一个实验带你全面学废网络OSPF协议内容

文章目录 一、OSPF实验与原理1. 多区域与虚链路配置及原理2. OSPF区域与接口认证的方法3. OSPF的Cost 值的作用4. 明白OSPF引入缺省、直连路由、宣告路由方法5. 描述OSPF区域间路由汇总和外部路由汇总6. 描述OSPF的5种类型的LSA区别&#xff0c;以及作用7. 控制OSPF的DR与BDR选…

微信小程序——弹出隐私指引教程(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【echarts】ECharts 解决地图标签重叠问题的技术实践

ECharts 解决地图标签重叠问题的技术实践 在使用 ECharts 绘制地图时&#xff0c;遇到的一个常见问题是某些地区的名称标签会因为地理位置接近而出现重叠&#xff0c;导致可读性变差。为了提升地图的展示效果&#xff0c;确保每个地区的名称都能清晰可见&#xff0c;我们可以通…

Java语言程序设计——篇十五(4)

&#xff1a; &#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;…

亦菲喊你来学习之机器学习(7)--逻辑回归内下采样

文章目录 下采样优缺点下采样步骤总结 下采样 在逻辑回归中&#xff0c;下采样是一种处理数据不平衡问题的方法&#xff0c;特别是在处理二分类问题时&#xff0c;如果某一类的样本数量远多于另一类&#xff0c;就可能导致模型偏向于多数类&#xff0c;从而忽略了少数类的特征…

基于推荐算法的景点攻略网站的设计与实现---附源码130855

摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于旅游景点分享也是十分常见的。过去使用传统广告方式对旅游景点进行推荐分享&#xff0c;造成了流程繁琐、难以维护&#xff0c;难于进准推荐给适合需求的人群等问题&#xff0…

java15-网络编程

一 网络编程概述 1.1 网络编程简介 其实&#xff0c;所谓的网络编程&#xff0c;就是编写程序&#xff0c;实现让同一个网络中的机器实现数据的传递&#xff0c;实现通信。 Java是 Internet 的语言&#xff0c;它从语言级上提供了对网络应用程序的支持。 Java提供的网络类库&a…

注册Github账号详细过程

目录 一、准备工作 二、注册步骤 一、准备工作 在注册GitHub账号之前&#xff0c;请确保您已经准备好以下信息&#xff1a; 一个有效的电子邮箱地址&#xff1a;用于接收验证邮件和GitHub的后续通知。 用户名&#xff1a;确保该用户名在GitHub上是唯一的&#xff0c;且符合…

如何选择合适的端口管控软件

一、明确需求 管控的端口类型&#xff1a;首先确定需要管控的端口类型是USB端口&#xff0c;还是需要同时管控串口、并口等其他类型的端口。 管控的力度&#xff1a;确定是需要对所有端口进行统一管控&#xff0c;还是需要根据不同的用户或部门实施不同的策略。 日志记录需求…

图神经网络教程1-综述

目录 前言 介绍 贡献 分类 预备知识 学习方式 转导式学习 归纳学习 系列文章列表 前言 翻译自A Practical Tutorial on Graph Neural Networks&#xff0c;并给出详细的解释和注意事项以及个人的思考&#xff0c;原作者如下&#xff1a; 介绍 当代人工智能(AI)&#x…