Netty 入门应用之Http服务WebSocket

news2024/11/26 20:49:56

Netty实现Http服务

主要的变化是在初始化器中引入了新的编解码器
一些创建的类作用和Netty HelloWorld的小demo一样我这里就不再次重复了

1、Http服务端代码

public class HttpServer {
    public static void main(String[] args) {

        // 创建Reactor
        // 用来管理channel 监听事件 ,是无限循环的事件组(线程池)
        EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
        EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
        // 服务端的启动对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 设置相关参数  这是一个链式编程
        serverBootstrap.group(bossLoopGroup,workerLoopGroup)
                // 声明通道类型
                .channel(NioServerSocketChannel.class)
                // 设置处理器  我这里设置了netty提供的Handler 处理器
                .handler(new LoggingHandler(LogLevel.INFO))
                // 定义客户连接端处理器的使用
                // ChannelInitializer 通道处理化
                // 可以自定义通道初始化器,如实现编码解码器时
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 需要处理的是客户端通道
                        // 通道代表的是 连接的角色 管道代表的是 处理业务的逻辑管理
                        // 管道相当与一个链表, 将不同的处理器连接起来,管理的是处理器的顺序
                        ch.pipeline().addLast(new HttpMyInitializer());

                    }
                });

        System.out.println("服务端初始化完成");

        // 启动需要设置端口  还需要设置是异步启动
        try {
            // 设置异步的future
            ChannelFuture future = serverBootstrap.bind(9988).sync();


            // 将关闭的通道也设置成异步的
            // 阻塞finally 中的代码
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 优雅关闭
            bossLoopGroup.shutdownGracefully();
            workerLoopGroup.shutdownGracefully();
        }
    }
}

1.1 Http服务自定义初始化器

下面是需要了解的组件
请求和响应的编码解码器:
客户端
HttpResponseDecoder 解码器,
处理服务端的响应(客户端)
HttpRequestEncoder 编码器,
处理服务端的请求(客户端)
服务端
HttpRequestDecoder 解码器,
处理客户端的请求(服务端)
HttpResponseEncoder 编码器,
处理客户端的响应(服务端)
由于上面的编码解码器都比较绕,所以还有两个组合的类提供
HttpClientCodeC :
编码解码器,用于客户端 HttpResponseDecoder + HttpRequestEncoder
HttpServerCodeC:
编码解码器,用于服务端 HttpRequestDecoder + HttpResponseEncoder

聚合
由于http的请求和响应,可能由很多部分组成,需要聚合成一个完整的消息
HttpObjectAggregator -> FullHttpRequest / FullHttpResponse

压缩
由于网络上有些情况文件或者图片需要压缩,所以需要压缩处理器
HttpContentCompressor 压缩,用于服务端
HttpContentDecompressor 解压缩,用于客户端

自定义初始化器HttpMyInitializer 需要继承ChannelInitializer泛型是Channel


public class HttpMyInitializer extends ChannelInitializer<Channel> {
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // 先解码后编码
//        pipeline.addLast("decoder",new HttpRequestDecoder());
//        pipeline.addLast("encoder",new HttpResponseEncoder());
        // 相当于上面两行
        pipeline.addLast("codec",new HttpServerCodec());
        // 压缩数据
        pipeline.addLast("compressor",new HttpContentCompressor());
        // 聚合成完整的消息  参数代表处理的最大值
        pipeline.addLast("aggregator",new HttpObjectAggregator(512 * 1024));

        // 添加处理器
        pipeline.addLast(new MyHttpHandler());

    }
}

1.2 Http服务自定义处理器

需要继承SimpleChannelInboundHandler类注意的是泛型需要定义为 FullHttpRequest


/**
 * 泛型需要定义为 FullHttpRequest
 *
 */
public class MyHttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    /**
     *
     * @param ctx 通道处理器上下文
     * @param msg 接收客户端数据消息
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        // 设定 版本 、响应码、响应的数据(ByteBuf) 等
        DefaultFullHttpResponse response =new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer("http hello netty demo ".getBytes()));
        // 还需要设置响应头 HttpHeaders 来接收
        // 设置字段名 使用HttpHeaderNames ,字段值使用HttpHeaderValues
        HttpHeaders headers = response.headers();
        //content/type ;text/plain
        headers.add(HttpHeaderNames.CONTENT_TYPE,HttpHeaderValues.TEXT_PLAIN+"charset=UTF-8");
        // 设置包的大小时, 调用 readableBytes方法
        headers.add(HttpHeaderNames.CONTENT_LENGTH,response.content().readableBytes());

        // 将response 写入通道  这里不用writeAndFlush方法, 而是在channelReadComplete读完成的方法内来刷新通道
        ctx.write(response);


    }

    /**
     *  用来刷新channelRead0 写入通道里面的response 数据
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}

1.3 Http服务最后展示结果

启动服务端、客户端我展示代码,可以随便启动一个我之前的小demo客户端记得改端口9988就行

在这里插入图片描述

客户端控制台展示

访问localhost:9988
在这里插入图片描述

在Edge浏览器展示

Netty实现WebSocket服务

http协议的缺陷: 通信只能由客户端发起。需要一种服务端能够主动推送的能力—websocket。这种双向通信的能力,也叫“全双工”。
协议标识符: http://127.0.0.1/ -> ws://127.0.0.1/
通信的最小单位是frame。

2、WebSocket服务服务端代码

同样的配方,大同小异, 只是初始化器和处理器不同,需要自定义


public class WebSocketServer {
    public static void main(String[] args) {
        // 创建Reactor
        // 用来管理channel 监听事件 ,是无限循环的事件组(线程池)
        EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
        EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
        // 服务端的启动对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 设置相关参数  这是一个链式编程
        serverBootstrap.group(bossLoopGroup,workerLoopGroup)
                // 声明通道类型
                .channel(NioServerSocketChannel.class)
                // 设置处理器  我这里设置了netty提供的Handler 处理器
                .handler(new LoggingHandler(LogLevel.INFO))
                // 定义客户连接端处理器的使用
                // ChannelInitializer 通道处理化
                // 可以自定义通道初始化器,如实现编码解码器时
                .childHandler(new WebSocketInitializer());

        System.out.println("服务端初始化完成");

        // 启动需要设置端口  还需要设置是异步启动
        try {
            // 设置异步的future
            ChannelFuture future = serverBootstrap.bind(7777).sync();


            // 将关闭的通道也设置成异步的
            // 阻塞finally 中的代码
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 优雅关闭
            bossLoopGroup.shutdownGracefully();
            workerLoopGroup.shutdownGracefully();
        }


    }
}

2.1 WebSocket服务自定义初始化器

继承ChannelInitializer 泛型是SocketChannel


public class WebSocketInitializer extends ChannelInitializer<SocketChannel> {
    /**
     *
     * @param ch
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // 设置编码解码器
        pipeline.addLast(new HttpServerCodec());
        // 用于处理较大的数据
        pipeline.addLast(new ChunkedWriteHandler());
        // 设置聚合器
        pipeline.addLast(new HttpObjectAggregator(512 * 1024));
        // 声明请求路径 ws://127.0.0.1:7777/hello
        pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));

        // 自定义处理器
        pipeline.addLast(new WebSocketHandler());

    }
}

2.2 WebSocket服务自定义处理器

主要的是channelRead0方法

/**
 *  本次业务处理的数据是文本, WebSocket通信是通过帧来传输
 *  所以泛型为 TextWebSocketFrame
 */
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    // 当多个通道传入handler , 使用通道组的管理方法
    // GlobalEventExecutor 全局事件执行器
    //INSTANCE 代表的是单例
    private  static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    /**
     *
     * @param ctx 通道处理器上下文
     * @param msg 文本消息帧
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 浏览器返回的信息帧
        System.out.println("msg:"+msg.text());
        Channel channel = ctx.channel();
        // 需要响应会浏览器的信息, 需要是TextWebSocketFrame 类型

        TextWebSocketFrame webSocketFrame = new TextWebSocketFrame(ctx.channel().remoteAddress()+"客户端:"+msg.text()+"\r\n");
        channel.writeAndFlush(webSocketFrame);

    }


    /**
     *  连接成功, 此时通道是活跃的时候触发
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LocalDate today = LocalDate.now();
        String dateStr = today.toString(); // 默认格式为 "yyyy-MM-dd"
        ctx.writeAndFlush("Welcome to server-- now :"+dateStr+"\r\n");
    }

    /**
     *  通道不活跃 ,用于处理用户下线的逻辑
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"下线了\r\n");
    }

    /**
     *
     * @param ctx 通道处理器上下文
     * @throws Exception
     * 连接刚刚建立时 ,第一个被执行的方法,
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

        System.out.println("[服务端地址]:"+ctx.channel().remoteAddress()+"连接成功\r\n");
        // 添加到通道组中管理
        channelGroup.add(ctx.channel());
    }

    /**
     *
     * @param ctx  通道处理器上下文
     * @throws Exception
     * 当连接断开 最后执行的方法
     * 连接断开时 , channel 会自动从 通道组中移除
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("[服务端地址]:"+ctx.channel().remoteAddress()+"断开连接\r\n");
    }

    /**
     *  通用异常处理类
     * @param ctx 通道处理器上下文
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 关闭
        ctx.close();
    }
}

2.3 WebSocket服务前端界面

实现一个聊天的小demo

在这里插入图片描述

WebSocket客户端建立连接流程

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello WebSocket</title>
</head>
<body>

<script>
    var socket;
    // 判断当前浏览器是否支持websockt
    if (!window.WebSocket) {
        alert("不支持websocket")
    } else {
        socket = new WebSocket("ws://127.0.0.1:7777/hello");
        // 设置开启连接的方法
        socket.onopen = function (ev) {
            var tmp = document.getElementById("respText");
            tmp.value = "连接已开启";
        }
        // 设置关闭连接的方法
        socket.onclose = function (ev) {
            var tmp = document.getElementById("respText");
            tmp.value = tmp.value + "\n" + "连接已关闭";
        }
        // 设置接收数据的方法
        socket.onmessage = function (ev) {
            var tmp = document.getElementById("respText");
            tmp.value = tmp.value + "\n" + ev.data;
        }

    }

    function send(message) {
        // 先判断socket是否已经创建
        if (!window.socket) {
            return
        }

        // 判断socket的状态
        //   CONNECTING  正在连接    CLOSING  正在关闭
        //   CLOSED  已经关闭或打开连接失败
        //   OPEN  连接成功 可以正常通信
        if (socket.readyState == WebSocket.OPEN) {
            socket.send(message);
        } else {
            alert("连接未开启");
        }
    }
</script>

<!--防止表单自动提交-->
<form onsubmit="return false">
    <textarea name="message" style="height: 400px;width: 400px"></textarea>
    <input type="button" value="发送" onclick="send(this.form.message.value)">
    <textarea id="respText" style="height: 400px;width: 400px"></textarea>
</form>


</body>
</html>

2.3 WebSocket结果展示

启动WebSocke服务器,运行前端代码如下如所示:
在这里插入图片描述

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

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

相关文章

氮气柜温湿度含氧量控制及联网监测功能介绍

氮气柜主要用于存储对氧气敏感的物料&#xff0c;如精密电子元器件、金属材料、化学品、药品、IC芯片等。通过持续充入氮气&#xff0c;降低柜内氧气含量至安全水平&#xff0c;从而抑制氧化反应的发生&#xff0c;保护物料免受氧化损害。 氮气柜可以通过氧浓度传感器来实现对柜…

【TI毫米波雷达】MMWave SDK:UART串口数据发送时的数据丢失、出错及避坑配置,Polling方法和Write方法的使用避坑

【TI毫米波雷达】MMWave SDK&#xff1a;UART串口数据发送时的数据丢失、出错及避坑配置&#xff0c;Polling方法和Write方法的使用避坑 文章目录 BUG复现Polling方法和Write方法的使用避坑串口配置避坑附录&#xff1a;结构框架雷达基本原理叙述雷达天线排列位置芯片框架Demo工…

手术麻醉信息管理系统源码 网格编辑、麻醉质控指标

目录 什么是手术麻醉信息管理系统 满足医院等级评级需求 满足电子病历评级需求 满足科室需求 术前 1、患者术前评估/诊断 2、术前讨论制定手术方案 3、手术准备 4、术前准备 术中 1、送手术室 2、麻醉前 3、手术 术后 1、手术结束 2、术后监护 3、术后随访 什…

龙蜥社区「人人都可以参与开源」——体验开源成为“开源人“

龙蜥社区「人人都可以参与开源」体验开源——让更多的人了解开源&#xff01; 龙蜥社区开源概述&#xff1a;龙蜥社区开源的探索过程:龙蜥社区收获总结:AtomGit评测:服务设计上:功能结构上:安全设计上: AtomGit测评总结: 龙蜥社区开源概述&#xff1a; 在追求技术的路上少不了…

Netty学习——高级篇2 Netty解码技术 备份

接上篇&#xff1a;Netty学习——高级篇1 拆包 、粘包与编解码技术&#xff0c;本章继续介绍Netty的其他解码器 1 DelimiterBasedFrameDecoder分隔符解码器 DelimiterBasedFrameDecoder 分隔符解码器是按照指定分隔符进行解码的解码器&#xff0c;通过分隔符可以将二进制流拆分…

将扁平数据转换为树形数据的方法

当遇到了好多扁平数据我们都无从下手&#xff1f;不知道如何处理&#xff1f; 家人们 无脑调用这个函数就好了 接口请求回来以后 调用这个函数传入实参就可以用啦~ // 树形菜单函数 function GetTreeData(data) {let TreeData [];let map new Map(); //存在id,对应所在的内…

大厂Java笔试题之判断字母大小写

/*** 题目&#xff1a;如果一个由字母组成的字符串&#xff0c;首字母是大写&#xff0c;那么就统计该字符串中大写字母的数量&#xff0c;并输出该字符串中所有的大写字母。否则&#xff0c;就输出* 该字符串不是首字母大写*/ public class Demo2 {public static void main(St…

功能测试_验证某城市电话号码的正确性

案例&#xff1a;验证某城市电话号码的正确性 功能测试_等价类设计用例&#xff1a; 步骤&#xff1a; 1:明确需求&#xff1a;电话号码是否正确 2:划分等价类&#xff1a;有效等价类、有效取值、无效等价类、无效取值 3&#xff1a;提取数据编写用例&#xff1a;用例编号…

4.9QT

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&#xf…

vite+react+ts+scss 创建项目

npm create vitelatest输入项目名称选择react选择typescript swc WC 通过利用 Rust 编写的编译器&#xff0c;使用了更先进的优化技术&#xff0c;使得它在处理 TypeScript 代码时能够更快地进行转换和编译。特别是在大型项目中&#xff0c;SWC 相对于传统的 TypeScript 编译器…

液冷是大模型对算力需求的必然选择?|英伟达 GTC 2024六大亮点

在这个以高性能计算和大模型推动未来通用人工智能时代&#xff0c;算力已成为科技发展的隐形支柱。本文将重点探讨算力的演进&#xff0c;深入分析在不同领域中算力如何成为推动进步的基石&#xff1b;着眼于液冷如何突破算力瓶颈成为引领未来的先锋&#xff0c;对液冷散热的三…

探索RAG:加强问答能力的新技术

文章目录 1. RAG是什么&#xff1f;1.1 技术简介1.2 挑战与解决方案1.3 RAG技术构成1.4 应用与前景 2. RAG架构详解2.1 典型的RAG应用有两个主要组件2.2 从原始数据到答案的完整流程 3. RAG在实际应用中的案例 1. RAG是什么&#xff1f; 1.1 技术简介 Retrieval Augmented Ge…

设计模式学习笔记 - 设计模式与范式 -行为型:9.迭代器模式(上):相比直接遍历集合数据,使用迭代器模式有哪些优势?

概述 上篇文章&#xff0c;我们学习了状态模式。状态模式是状态机的一种实现方式。它通过将事件触发的状态转移和动作执行&#xff0c;拆分到不同的状态类中&#xff0c;以此来避免状态机类中的分支判断逻辑&#xff0c;应对状态机类代码的复杂性。 本章&#xff0c;学习另外…

【千帆平台】百度智能云千帆AppBuilder应用探索益智游戏之猜物小游戏

欢迎来到《小5讲堂》 这是《千帆平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 背景AppBuilder控制台创建应用设置应用自动配置随机生成AI生成应…

Elasticsearch 嵌套类型的深度剖析与实例

文章目录 **嵌套类型的原理与特点****嵌套类型的创建与映射定义****嵌套查询与过滤****嵌套聚合****实战应用举例** Elasticsearch 嵌套类型的实例 Elasticsearch 索引中的嵌套类型&#xff08;Nested Types&#xff09;是处理具有层次结构或一对多关系数据的有效工具。它允许在…

电工技术学习笔记——正弦交流电路

一、正弦交流电路 1. 正弦量的向量表示法 向量表示方法&#xff1a;正弦交流电路中&#xff0c;相量表示法是一种常用的方法&#xff0c;用于描述电压、电流及其相位关系。相量表示法将正弦交流信号表示为复数&#xff0c;通过复数的运算来描述电路中各种参数的相互关系 …

墨迹天气联合TopOn搭建创新合作模式,深挖广告流量价值 | TopOn变现案例

日前&#xff0c;墨迹天气与移动广告聚合管理平台TopOn达成合作&#xff0c;开发创新思路&#xff0c;通过搭建高效的合作模式&#xff0c;提升商业化效果广告效率和业务水平&#xff0c;共建新场景。 墨迹天气相关负责人表示&#xff0c;作为国内最早布局天气类应用的厂商之一…

python开发poc,fofa爬虫批量化扫洞

学习使用python做到批量化的漏洞脚本 1.通过fofa搜索结果来采集脚本 2.批量化扫描漏洞 ---glassfish存在任意文件读取在默认48484端口&#xff0c;漏洞验证的poc为: "glassfish" && port"4848" && country"CN" http://loca…

工厂水电能耗监测管理云平台

在当今工业生产中&#xff0c;对水电等能源的有效管控已成为企业降低成本、提升竞争力的关键所在。随着云计算、大数据以及物联网技术的飞速发展&#xff0c;工厂水电能耗监测管理云平台作为一种新型的能源管理解决方案&#xff0c;正受到越来越多企业的青睐。该平台通过云技术…

二维相位解包理论算法和软件【全文翻译- DCT相位解包裹(5.3.2)】

5.3.2 基于 DCT 的方法 在本节中,我们将详细介绍如何通过 DCT 算法解决非加权最小二乘相位解缠问题,而不是通过FFT.我们将使用公式 5.53 所定义的二维余弦变换。我们开发的算法等同于 FFT 方法 2(第 5.3.1 节)。与 FFT 方法 I 等价的 DCT 算法也可以推导出来,但我们将其作…