Netty学习——实战篇5 Netty 心跳监测/WebSocket长连接编程 备份

news2024/11/16 15:50:17

 1 心跳监测

MyServer.java
public class MyServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.DEBUG))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //加入Netty提供的 IdleStateHandler
                            /*
                                说明:IdleStateHandler 是netty提供的处理空闲状态的处理器
                                    long readerIdleTime:表示多长时间没有读,就会发送一个心跳监测包 检测是否连接
                                    long writerIdelTime:表示多长时间没有写,就会发送一个心跳监测包 监测是否连接
                                    long allIdelTime:表示多长时间没有读写,就会发送一个心跳检测包 监测是否连接
                             */
                            pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));
                            //加入自定义Handler,对空闲检测进一步处理
                            pipeline.addLast(new MyServerHandler());

                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
MyServerHandler.java 
@Slf4j
public class MyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent) evt;
            String eventType = null;
            switch (event.state()){
                case READER_IDLE:
                    eventType = "读空闲";
                    break;

                case WRITER_IDLE:
                    eventType = "写空闲";
                    break;

                case ALL_IDLE:
                    eventType = "读写空闲";
                    break;
            }
            log.info("{},---超时时间---,{}",ctx.channel().remoteAddress(),eventType);
            log.info("服务器做相应处理");

            //如果发生空闲,关闭通道
            ctx.channel().close();
        }
    }
}
NettyChatClient.java 
@Slf4j
public class NettyChatClient {
    private  String host;
    private  int port;

    public NettyChatClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    private void run(){
        NioEventLoopGroup loopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(loopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("decoder",new StringDecoder());
                            pipeline.addLast("encoder",new StringEncoder());
                            pipeline.addLast(new NettyChatClientHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            Channel channel = channelFuture.channel();
            log.info("客户端连接成功,地址是:{}",channel.remoteAddress());
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()){
                String msg = scanner.nextLine();
                channel.writeAndFlush(msg + "\r\n");
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            loopGroup.shutdownGracefully();
        }


    }

    public static void main(String[] args) {
        new NettyChatClient("127.0.0.1",8000).run();
    }
}
NettyChatClientHandler.java
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg.trim());
    }
}

服务端运行结果:

2  WebSocket实现服务器和客户端长连接

2.1 需求

        (1)Http协议是无状态的,浏览器和服务器之间的请求响应一次,下一次会重新创建连接。

        (2)实现基于webSocket的长连接的全双工的交互。

        (3)改变Http协议多次请求的约束,实现长连接,服务端可以发送消息给浏览器。

        (4)客户端浏览器和服务端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务端也会感知。

服务端代码:MyServer.java

public class MyServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //基于http协议,使用和图片的编解码
                            pipeline.addLast(new HttpServerCodec());
                            //以块方式写,添加chunkedwritehandler处理器
                            pipeline.addLast(new ChunkedWriteHandler());
                            /*
                            说明
                            1. http数据在传输过程中是分段, HttpObjectAggregator ,就是可以将多个段聚合
                            2. 这就就是为什么,当浏览器发送大量数据时,就会发出多次http请求
                             */
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            /*
                            说明
                            1. 对应websocket ,它的数据是以 帧(frame) 形式传递
                            2. 可以看到WebSocketFrame 下面有六个子类
                            3. 浏览器请求时 ws://localhost:7000/hello 表示请求的uri
                            4. WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接
                            5. 是通过一个 状态码 101
                             */
                            pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                            //自定义handler
                            pipeline.addLast(new MyTextWebSocketFrameHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

自定义Handler:MyTextWebSocketFrameHandler.java

@Slf4j
public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        log.info("服务器接收消息:{}",msg.text());
        //回复消息
        ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间"+ LocalDateTime.now()+ " " +msg.text()));
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("发生异常:{}",cause.getMessage());
        ctx.close();
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerAdded 被调用,channel id 是:{}",ctx.channel().id().asLongText());
        log.info("handlerAdded 被调用,channel id 是:{}",ctx.channel().id().asShortText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerRemoved 被调用,channel id 是:{}",ctx.channel().id().asLongText());
    }
}

客户端代码:hello.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var socket;
    //判断当前浏览器是否支持websocket
    if(window.WebSocket) {
        //go on
        socket = new WebSocket("ws://localhost:8000/hello");
        //相当于channelReado, ev 收到服务器端回送的消息
        socket.onmessage = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + ev.data;
        }

        //相当于连接开启(感知到连接开启)
        socket.onopen = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = "连接开启了.."
        }

        //相当于连接关闭(感知到连接关闭)
        socket.onclose = function (ev) {

            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + "连接关闭了.."
        }
    } else {
        alert("当前浏览器不支持websocket")
    }

    //发送消息到服务器
    function send(message) {
        if(!window.socket) { //先判断socket是否创建好
            return;
        }
        if(socket.readyState == WebSocket.OPEN) {
            //通过socket 发送消息
            socket.send(message)
        } else {
            alert("连接没有开启");
        }
    }
</script>
    <form onsubmit="return false">
        <textarea name="message" style="height: 300px; width: 300px"></textarea>
        <input type="button" value="发生消息" onclick="send(this.form.message.value)">
        <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
        <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
    </form>
</body>
</html>

服务端运行结果:

客户端运行结果:

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

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

相关文章

CPDA|0到1突破:构建高效数据分析体系的秘密武器

在现今信息爆炸的时代&#xff0c;数据已经渗透到了我们生活的方方面面&#xff0c;成为了决策、创新和竞争优势的关键。因此&#xff0c;构建一套高效的数据分析体系&#xff0c;对于企业和个人而言&#xff0c;都是至关重要的。那么&#xff0c;如何在众多的数据海洋中脱颖而…

一文读懂Partisia Blockhain:兼顾去中心化、安全性与可扩展性

“Partisia Blockhain 解决了区块链领域长期存在的问题&#xff0c;其兼顾了去中心化、安全性以及可扩展性” Partisia Blockchain 是一个具有独特零知识证明预言机以及分片解决方案的 Layer1&#xff0c;解决了困扰整个区块链行业的问题。 目前&#xff0c;多样化的区块链层出…

软考-论文写作-论软件设计模式

题目 素材 框架 一、 摘要 2020年12月,我参加了某省政协委员履职系统的开发。该系统为政协机关人员管理委员信息以及委员完成各项履职提供了全方位的软件支撑。我在该项目重担任系统架构师一职,负责履职系统的架构设计。本文结合作者的实践,以委员履职系统为例,主要讨论软…

计算机网络-IS-IS基础配置实验

前面我们了解了IS-IS的一些基础理论&#xff0c;从建立邻接、链路状态数据库同步以及路由计算&#xff0c;现在开始学习下配置操作。 一、IS-IS配置 网络拓扑图&#xff1a; 拓扑图 IS-IS有Level级别的区分&#xff0c;Level-1可以是性能较低的设备只维护区域内的LSDB&#xff…

035——从GUI->Client->Server->driver实现SPI控制DAC芯片

目录 1、修改GUI 2、修改client 3、server编写 4、driver_handle 5、test 6、 项目管理 1、修改GUI 我想让DAC控制是个滑动条 import PySimpleGUI as sgdef slider_callback(slider_value):print(fCurrent value: {slider_value})layout [[sg.Text(Select a value:)],…

百篇博客 · 千里之行

时光荏苒流逝&#xff0c;白驹匆匆过隙&#xff0c;不知不觉间&#xff0c;Damon小智已经在CSDN上记录了第一百多篇文章。恰逢128天创作纪念日的此刻&#xff0c;我感慨良多&#xff0c;这百余篇博客不仅是我的创作历程&#xff0c;更见证了我在这五年技术生涯中走过心路历程。…

imx6ull -- SPI

SPI 是 Motorola 公司推出的一种同步串行接口 技术&#xff0c;是一种高速、全双工的同步通信总线&#xff0c; SPI 时钟频率相比 I2C 要高很多&#xff0c;最高可以工作 在上百 MHz。 SPI 以主从方式工作&#xff0c;通常是有一个主设备和一个或多个从设备&#xff0c;一般 SP…

【网页在线小游戏源码】

网页在线小游戏源码 效果图部分源码领取源码下期更新预报 效果图 部分源码 index.html <!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <meta id"viewport" na…

Hive——DML(Data Manipulation Language)数据操作语句用法详解

DML 1.Load Load语句可将文件导入到Hive表中。 hive> LOAD DATA [LOCAL] INPATH filepath [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1val1, partcol2val2 ...)];关键字说明&#xff1a; local&#xff1a;表示从本地加载数据到Hive表&#xff1b;否则从HD…

SpringBoot学习之Redis下载安装启动【Windows版本】(三十六)

一、下载Redis for Windows Redis 官方网站没有提供 Windows 版的安装包,但可以通过 GitHub 来下载安装包,下载地址:https://github.com/tporadowski/redis/releases 1、网站提供了安装包和免安装版本,这里我们直接选择下面的免安装版本 2、下载后的压缩包解压以后,如下…

快速新建springboot项目

一、初始化 1.打开IDEA&#xff0c;在Spring initializer这里按照下图项目进行配置。注意&#xff1a;如果jdk是1.8建议将Server URL这里替换为图中的阿里云服务器&#xff0c;否则容易找不到对应的java8&#xff0c;然后点击next 2.在这里提前配置一些需要使用的依赖&#xf…

Node.js和cnpm环境搭建

Node.js和cnpm环境搭建 一、nodejs安装 1.1 傻瓜式一直下一步即可&#xff0c;不需要额外进行任何配置 nodejs下载链接&#xff0c;提取码&#xff1a;5555 1.2 查看是否安装成功 cmd进入命令行界面 输入node -v 显示node版本&#xff0c;显示则安装成功 1.3 改变全局模块路…

npm常用的命令大全(2024-04-21)

nodejs中npm常见的命令 npm主要是node包管理和发布的工具。 npm官网网址&#xff1a;npm | Homehttps://www.npmjs.com/官网英文文档&#xff1a; npm DocsDocumentation for the npm registry, website, and command-line interfacehttps://docs.npmjs.com/about-npm官网中文文…

golang学习笔记(defer基础知识)

什么是defer defer语句用于golang程序中延迟函数的调用&#xff0c; 每次defer都会把一个函数压入栈中&#xff0c; 函数返回前再把延迟的函数取出并执行。 为了方便描述&#xff0c; 我们把创建defer的函数称为主函数&#xff0c; defer语句后面的函数称为延迟函数。延迟函数…

MCU功耗测量

功耗测量 一、相关概念二、功耗的需求三、测量仪器仪表测量连接SMU功能SMU性能指标 四、功耗测量注意点板子部分存在功耗MCU方面&#xff0c;可能存在干扰项仪器仪表方面 一、相关概念 静态功耗和动态功耗&#xff1a;动态功耗为运行功耗&#xff0c;功耗测量注重每MHz下的功耗…

AppleWatch是真的能够减少我iPhone的使用时长

我应该是比较专情的果粉了&#xff0c;我有一台MacBook Pro、iPad Pro、airpods pro 2和iPhone 15 Pro Max。但我还从来没有用过苹果手表。 然后&#xff0c;我就去买了AppleWatchSeries9蜂窝款&#xff0c;并试用了一周&#xff0c;我想知道它是否能帮助我减少使用iPhone的时间…

穿越代码迷雾:解密Tracing技术的神奇力量

穿越代码迷雾&#xff1a;解密Tracing技术的神奇力量 在软件开发和性能优化领域&#xff0c;追踪&#xff08;Tracing&#xff09;技术是一种重要的工具&#xff0c;用于收集和分析程序的执行过程和性能数据。本文将深入讲解Tracing的原理、工作方式以及在不同领域的应用场景&a…

STM32F103ZET6_ADC

ADC主要特征 ● 12位分辨率 ●18个通道 &#xff0c;16个外部通道 2个内部通道 ● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断 ● 单次和连续转换模式 ● 从通道0到通道n的自动扫描模式 ● 自校准 ● 带内嵌数据一致性的数据对齐 ● 采样间隔可以按通道分…

【唯美情侣爱情表白纪念HTML单页】

唯美情侣爱情表白纪念HTML单页 效果图部分代码领取代码下期更新预报 效果图 整图 背景图 部分代码 index.html <!DOCTYPE html> <html lang"en"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"…