Netty 入门应用之Http服务WebSocket

news/2024/4/30 2:43:45/文章来源:https://blog.csdn.net/weixin_46171048/article/details/137591457

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 {// 设置异步的futureChannelFuture 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> {@Overrideprotected 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*/@Overrideprotected 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 ,字段值使用HttpHeaderValuesHttpHeaders headers = response.headers();//content/type ;text/plainheaders.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*/@Overridepublic 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 {// 设置异步的futureChannelFuture 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*/@Overrideprotected 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/hellopipeline.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*/@Overrideprotected 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*/@Overridepublic 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*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println(ctx.channel().remoteAddress()+"下线了\r\n");}/**** @param ctx 通道处理器上下文* @throws Exception* 连接刚刚建立时 ,第一个被执行的方法,*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {System.out.println("[服务端地址]:"+ctx.channel().remoteAddress()+"连接成功\r\n");// 添加到通道组中管理channelGroup.add(ctx.channel());}/**** @param ctx  通道处理器上下文* @throws Exception* 当连接断开 最后执行的方法* 连接断开时 , channel 会自动从 通道组中移除*/@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {System.out.println("[服务端地址]:"+ctx.channel().remoteAddress()+"断开连接\r\n");}/***  通用异常处理类* @param ctx 通道处理器上下文* @param cause* @throws Exception*/@Overridepublic 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;// 判断当前浏览器是否支持websocktif (!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.luyixian.cn/news_show_1046052.aspx

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

相关文章

力扣HOT100 - 238. 除自身以外数组的乘积

解题思路&#xff1a; 当前位置的结果就是它左部分的乘积再乘以它右部分的乘积。因此需要进行两次遍历&#xff0c;第一次遍历用于求左部分的乘积&#xff0c;第二次遍历在求右部分的乘积的同时&#xff0c;再将最后的计算结果一起求出来。 class Solution {public int[] prod…

Vue3学习01 Vue3核心语法

Vue3学习 1. Vue3新的特性 2. 创建Vue3工程2.1 基于 vue-cli 创建项目文件说明 2.2 基于 vite 创建具体操作项目文件说明 2.3 简单案例(vite) 3. Vue3核心语法3.1 OptionsAPI 与 CompositionAPIOptions API 弊端Composition API 优势 ⭐3.2 setup小案例setup返回值setup 与 Opt…

【vue/uniapp】使用 smooth-signature 实现 h5 的横屏电子签名

通过github链接进行下载&#xff0c;然后代码参考如下&#xff0c;功能包含了清空、判断签名内容是否为空、生成png/jpg图片等。 签名效果&#xff1a; 预览效果&#xff1a; 下载 smooth-signature 链接&#xff1a;https://github.com/linjc/smooth-signature 代码参考&a…

nexus搭建maven与docker镜像的私有仓库

引言 通过nexus搭建maven与docker镜像的私有仓库,实现jar包与镜像动态更新、共享、存储。 一、nexus部署 通过docker-compose部署nexus name: java services:#############################环境#############################env-nexus:restart: always## 3.58.1image: so…

ANSYS 2024 R1 HFSS部分更新介绍(附下载)

1. 优化Layout component工作流 • 支持多区域 - 支持参数化弯曲定义的刚柔结合的PCB • Phi 网格可用 • 支持Mesh Fusion •简化创建复杂装配体的过程 2. 提升求解器速度 • 分布式矩阵汇编的内存使用率改进 ‐减少分布式矩阵求解器的RAM消耗 • 分布式稀疏直接求解器&am…

物联网实战--驱动篇之(六)4G通讯(Air780E)

目录 一、4G模块简介 二、AIR780E驱动程序 三、AIR780使用注意事项 四、结合MQTT传输测试 一、4G模块简介 4G应该是我们日常生活最常见的一种互联网通讯方式了&#xff0c;每个智能手机都配置了&#xff0c;不过手机的4G跟我们物联网领域要用的4G有点区别。首先是物联网采用…

芯来科技、IAR和MachineWare携手加速符合ASIL标准RISC-V汽车芯片创新

支持软件开发团队在虚拟硬件平台上进行固件和MCAL开发 芯来科技&#xff08;Nuclei&#xff09;、IAR和MachineWare紧密合作&#xff0c;加速RISC-V ASIL合规汽车解决方案的创新。此次合作简化了汽车电子的固件和MCAL开发&#xff0c;提供了虚拟和物理硬件平台之间的无缝集成。…

Harmony鸿蒙南向驱动开发-SDIO

SDIO&#xff08;Secure Digital Input and Output&#xff09;由SD卡发展而来&#xff0c;与SD卡统称为MMC&#xff08;MultiMediaCard&#xff09;&#xff0c;二者使用相同的通信协议。SDIO接口兼容以前的SD卡&#xff0c;并且可以连接支持SDIO接口的其他设备。 运作机制 …

SpringBoot --pagehelper分页

目录 1.建立数据库 2.页面显示 3.基本逻辑 4.配置依赖 5.使用pagehelper 6.页面列表 页面 效果 1.建立数据库 create database if not exists my_book; use my_book; create table if not exists myBook (id int primary key auto_increment,name varchar(50) not …

Node.js常用快捷键

1.常用的终端命令&#xff1a; &#xff08;1&#xff09;del 文件名&#xff1a; 删除文件 &#xff08;2&#xff09;ipconfig: 查看IP命令 &#xff08;3&#xff09;mkdir 目录名 &#xff1a;在当前目录新建指定目录 &#xff08;4&#xff09;rd 目录名&#xff1a;在当前…

【opencv】示例-ela.cpp JPEG图像的错误等级分析(ELA) 通过分析图像压缩后的差异来检测图像是否被篡改过...

ela_modified.jpg 原始ela_modified压缩后再解压得到compressed_img 差异图像Ela 这段代码的功能是实现JPEG图像的错误等级分析&#xff08;ELA&#xff09;&#xff0c;通过分析图像压缩后的差异来检测图像是否被篡改过。程序会首先读取一张图片&#xff0c;然后对其应用质量…

PHP自助建站系统,小白也能自己搭建网站

无需懂代码&#xff0c;用 自助建站 做企业官网就像做PPT一样简单&#xff0c;您可以亲自操刀做想要的效果&#xff01; 自助建站是一款简单、快捷、高效的工具&#xff0c;可以帮助您制作响应式网站。我们的自助建站系统&#xff0c;将传统的编码工作转化为直观的拖拽操作和文…

Qt | 元对象系统

一、QByteArray 类简介 1、QByteArray 类简介  该类是一个用于处理字符串的类似于 C++的 string 类型的类,在 Qt 中,对字符串的处理,经常使用的是 QString 类,该类保证字符串以\0结尾,并使用隐式共享(copy-on-write)来减少内存用量和不必要的数据复制。  QByteArra…

Web3 的社会影响:数字社会的新时代

随着科技的不断进步和创新&#xff0c;人类社会正逐步进入数字化时代的新阶段。Web3 技术作为数字社会的重要组成部分&#xff0c;正在以前所未有的方式重塑着我们的社会生活和交往方式。本文将探讨 Web3 技术对社会的影响&#xff0c;以及它所带来的数字社会的新时代。 1. Web…

Gitlab全量迁移

Gitlab全量迁移 一、背景1.前提条件 一、背景 公司研发使用的Gitlab由于服务器下架需要迁移到新的Gitlab服务器上。Gitlab官方推荐了先备份然后再恢复的方法。个人采用官方的另外一种方法&#xff0c;就写这篇文章给需要的小伙伴参考。 源Gitlab: http://old.mygitlab.com #地…

数据应用OneID:ID-Mapping Spark GraphX实现

前言 说明 以用户实体为例&#xff0c;ID 类型包含 user_id 和 device_id。当然还有其他类型id。不同id可以获取到的阶段、生命周期均不相同。 device_id 生命周期通常指的是一个设备从首次被识别到不再活跃的整个时间段。 user_id是用户登录之后系统分配的唯一标识&#xff…

3D数据发布工具HOOPS Publish如何将3D模型转换成PDF格式?

在现代科技发展的推动下&#xff0c;3D技术在各个行业中扮演着越来越重要的角色。从工程设计到医疗保健&#xff0c;从建筑规划到教育培训&#xff0c;3D模型都成为了理解、沟通和展示复杂数据的主要工具。而将这些3D模型转换成PDF格式&#xff0c;能够使其更易于共享、存档和展…

配置交换机端口安全

1、实验目的 通过本实验可以掌握&#xff1a; 交换机管理地址配置及接口配置。查看交换机的MAC地址表。配置静态端口安全、动态端口安全和粘滞端口安全的方法。 2、实验拓扑 配置交换机端口安全的实验拓扑如图所示。 配置交换机端口安全的实验拓扑 3、实验步骤 &#xff…

nginx配置证书和私钥进行SSL通信验证

文章目录 一、背景1.1 秘钥和证书是两个东西吗&#xff1f;1.2 介绍下nginx配置文件中参数ssl_certificate和ssl_certificate_key1.3介绍下nginx支持的证书类型1.4 目前nginx支持哪种证书格式&#xff1f;1.5 nginx修改配置文件目前方式也会有所不同1.6 介绍下不通格式的证书哪…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.3 月末操作:外币评估

2.6.3 月末操作&#xff1a;外币评估 企业的外币业务在记账时一般使用期初的汇率或者即时汇率&#xff0c;但在月末&#xff0c;需要按照月末汇率对外币的余额或者未清项进行重估&#xff08;revaluation&#xff09;。 企业在资产负债表日&#xff0c;应当按照下列规…