Netty 入门应用之Http服务WebSocket

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服务器,运行前端代码如下如所示:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FME学习之旅---day22

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 教程&#xff1a;栅格入门 FME 支持读取和写入 70 多种栅格格式。本教程将介绍几个基本示例&#xff0c;展示如何使用 FME 读取、转换和写入栅格数据。 FME 数据检查器不应用任何对比度增强。因…

实战项目——智慧社区(一)

1、项目介绍 系统功能 登录、修改密码、登出 &#xff08;1&#xff09;首页 &#xff08;1.1&#xff09;数据统计&#xff1a;小区人员统计对比图&#xff0c;占比图 &#xff08;2&#xff09;物业管理 &#xff08;2.1&#xff09;小区管理&#xff1a;小区数据的增删改…

centos 7.9 nginx本地化安装,把镜像改成阿里云

1.把centos7.9系统切换到阿里云的镜像源 1.1.先备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2.下载新的CentOS-Base.repo配置文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo特别…

Redux和Redux Toolkit

Redux 概念&#xff1a;redux是react最常用的集中状态管理工具&#xff0c;类似于Vue中的Pinia(vuex)&#xff0c;可以独立于框架运行作用&#xff1a;通过集中管理的方式管理应用的状态 Redux快速体验 不和任何框架绑定&#xff0c;不使用任何构建工具&#xff0c;使用纯Re…

uniapp小程序编译报错

说明 微信小程序编译每次都出现[ project.config.json 文件内容错误] project.config.json: libVersion 字段需为 string, 解决 找到manifest.json文件 添加&#xff1a;"libVersion": "latest"&#xff0c;重新编译即可。

MySQL limit N offset M 速度慢?来实际体验下

直接开始 有一张表&#xff1a;trade_user&#xff0c;表结构如下&#xff1a; mysql> desc trade_user; ------------------------------------------------------------------ | Field | Type | Null | Key | Default | Extra | -------------…

记一次IP访问MySQL失败多次被自动锁定导致无法连接问题,解决方法只需一条SQL。

&#x1f469;&#x1f3fd;‍&#x1f4bb;个人主页&#xff1a;阿木木AEcru &#x1f525; 系列专栏&#xff1a;《Docker容器化部署系列》 《Java每日面筋》 &#x1f4b9;每一次技术突破&#xff0c;都是对自我能力的挑战和超越。 前言 今天下午还在带着耳机摸鱼&#xff…

Linux CentOS 安装 MySQL 服务教程

Linux CentOS 安装 MySQL 服务教程 1. 查看系统和GNU C库(glibc)版本信息 1.1 查询机器 glibc 版本信息 glibc&#xff0c;全名GNU C Library&#xff0c;是大多数Linux发行版中使用的C库&#xff0c;为系统和应用程序提供核心的API接口。在Linux系统中&#xff0c;特别是在…

动态规划-入门理解

一、什么情况可以使用动态规划 动态规划 最优子结构 重叠子问题 转移方程 最优子结构&#xff1a;保证能从局部解推出全局解&#xff0c;也就是保证能够写出转移方程 重叠子问题&#xff1a;说明暴力解法太耗时&#xff0c;我们可以使用动态规划进行优化 转移方程&#xff…

自用---

零、环境配置 keil代码补全 keil pack包 cubemx配置安装包 一、LED cubemx配置PD2引脚为输出模式 uint16_t led_value 0x00; void led_set(uint8_t led_dis) {HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,led_dis<<8,GPIO_PIN_R…

03-JAVA设计模式-桥接模式

桥接模式 什么是桥接模式 桥接模式&#xff08;Bridge Pattern&#xff09;是一种将抽象与实现解耦的设计模式&#xff0c;使得二者可以独立变化。在桥接模式中&#xff0c;抽象部分与实现部分通过一个桥接接口进行连接&#xff0c;从而允许抽象部分和实现部分独立演化。 场…

服务器docker应用一览

文章目录 一、简单需求二、业务流程三、运行效果四、实现过程1. 前提2. 源码3.核心代码4. 项目打包5、部署 一、简单需求 现有某云主机服务器&#xff0c;用来做项目演示用&#xff0c;上面运行了docker应用&#xff0c;现希望有一总览页面&#xff0c;用来展示部署的应用。 …

微信小程序实现输入appid跳转其他小程序

前言 本文记录wx.navigateToMiniProgram打开另一个小程序API使用方法&#xff0c;并封装为组件。 wxml 部分 输入框用来记录appid&#xff0c;按钮用来查询并跳转。 <view class"container"><input class"input" placeholder"请输入要查…

Linux: softirq 简介

文章目录 1. 前言2. softirq 实现2.1 softirq 初始化2.1.1 注册各类 softirq 处理接口2.1.2 创建 softirq 处理线程 2.2 softirq 的 触发 和 处理2.1.1 softirq 触发2.1.2 softirq 处理2.1.2.1 在 中断上下文 处理 softirq2.1.2.2 在 ksoftirqd 内核线程上下文 处理 softirq 3.…

Facial Micro-Expression Recognition Based on DeepLocal-Holistic Network 阅读笔记

中科院王老师团队的工作&#xff0c;用于做微表情识别。 摘要&#xff1a; Toimprove the efficiency of micro-expression feature extraction,inspired by the psychological studyof attentional resource allocation for micro-expression cognition,we propose a deep loc…

HTTP与HTTPS:深度解析两种网络协议的工作原理、安全机制、性能影响与现代Web应用中的重要角色

HTTP (HyperText Transfer Protocol) 和 HTTPS (Hypertext Transfer Protocol Secure) 是互联网通信中不可或缺的两种协议&#xff0c;它们共同支撑了全球范围内的Web内容传输与交互。本文将深度解析HTTP与HTTPS的工作原理、安全机制、性能影响&#xff0c;并探讨它们在现代Web…

[leetcode]remove-duplicates-from-sorted-list-ii

. - 力扣&#xff08;LeetCode&#xff09; 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&…

百度OCR身份证识别C++离线SDKV3.0 C#对接

百度OCR身份证识别C离线SDKV3.0 C#对接 目录 说明 效果 问题 项目 代码 下载 说明 自己根据SDK封装了动态库&#xff0c;然后C#调用。 SDK 简介 本 SDK 适应于于 Windows 平台下的⾝份证识别系统,⽀持 C接⼜开发的 SDK,开发者可在VS2015 下⾯进⾏开发&#xff08;推荐…

爬虫+RPC+js逆向---直接获取加密值

免责声明:本文仅做技术交流与学习,请勿用于其它违法行为;如果造成不便,请及时联系... 目录 爬虫RPCjs逆向---直接获取加密值 target网址: 抓包 下断点 找到加密函数 分析参数 RPC流程 一坨: 二坨: 运行py,拿到加密值 爬虫RPCjs逆向---直接获取加密值 target网址: 优志…

Django+Celery框架自动化定时任务开发

本章介绍使用DjCelery即DjangoCelery框架开发定时任务功能&#xff0c;在Autotestplat平台上实现单一接口自动化测试脚本、业务场景接口自动化测试脚本、App自动化测试脚本、Web自动化测试脚本等任务的定时执行、调度、管理等&#xff0c;从而取代Jenkins上的定时执行脚本和发送…