当前位置: 首页 > news >正文

基于Netty构建Websocket服务端

除了构建TCP和UDP服务器和客户端,Netty还可以用于构建WebSocket服务器。WebSocket是一种基于TCP协议的双向通信协议,可以在Web浏览器和Web服务器之间建立实时通信通道。下面是一个简单的示例,演示如何使用Netty构建一个WebSocket服务器。
项目目录:
在这里插入图片描述
引入pom依赖:

 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.69.Final</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

编写SocketServer:

package com.lzq.websocket.config;import com.lzq.websocket.handlers.WebSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;@Slf4j
@Configuration
public class WebSocketConfig implements CommandLineRunner {private static final Integer PORT = 8888;@Overridepublic void run(String... args) throws Exception {new WebSocketConfig().start();}public void start() {// 创建EventLoopGroupEventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);try {ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast(new HttpServerCodec());// 最大数据长度pipeline.addLast(new HttpObjectAggregator(65536));// 添加接收websocket请求的url匹配路径pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));// 10秒内收不到消息强制断开连接// pipeline.addLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS));pipeline.addLast(new WebSocketHandler());}});ChannelFuture future = serverBootstrap.bind(PORT).sync();log.info("websocket server started, port={}", PORT);// 处理 channel 的关闭,sync 方法作用是同步等待 channel 关闭// 阻塞future.channel().closeFuture().sync();} catch (Exception e) {log.error("websocket server exception", e);throw new RuntimeException(e);} finally {log.info("websocket server close");// 关闭EventLoopGroupbossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

编写WebSocketHandler:

package com.lzq.websocket.handlers;import com.lzq.websocket.config.NettyConfig;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;import java.nio.charset.StandardCharsets;@Slf4j
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {private WebSocketServerHandshaker webSocketServerHandshaker;private static final String WEB_SOCKET_URL = "ws://127.0.0.1:8888/websocket";@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {// 创建连接时执行NettyConfig.group.add(ctx.channel());log.info("client channel active, id={}", ctx.channel().id().toString());}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {// 关闭连接时执行NettyConfig.group.remove(ctx.channel());log.info("client channel disconnected, id={}", ctx.channel().id().toString());}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {// 服务端接收客户端发送过来的数据结束之后调用ctx.flush();}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {WebSocketServerProtocolHandler.HandshakeComplete handshake = (WebSocketServerProtocolHandler.HandshakeComplete) evt;log.info("client channel connected, id={}, url={}", ctx.channel().id().toString(), handshake.requestUri());}}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof FullHttpRequest) {// 处理客户端http握手请求handlerHttpRequest(ctx, (FullHttpRequest) msg);} else if (msg instanceof WebSocketFrame) {// 处理websocket连接业务handlerWebSocketFrame(ctx, (WebSocketFrame) msg);}}/*** 处理websocket连接业务** @param ctx* @param frame*/private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {log.info("handlerWebSocketFrame>>>>class={}", frame.getClass().getName());// 判断是否是关闭websocket的指令if (frame instanceof CloseWebSocketFrame) {webSocketServerHandshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain());return;}// 判断是否是ping消息if (frame instanceof PingWebSocketFrame) {ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));return;}if (!(frame instanceof TextWebSocketFrame)) {throw new RuntimeException("不支持消息类型:" + frame.getClass().getName());}String text = ((TextWebSocketFrame) frame).text();if ("ping".equals(text)) {ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));return;}log.info("WebSocket message received: {}", text);/*** 可通过客户传输的text,设计处理策略:* 如:text={"type": "messageHandler", "userId": "111"}* 服务端根据type,采用策略模式,自行派发处理** 注意:这里不需要使用线程池,因为Netty 采用 Reactor线程模型(目前使用的是主从Reactor模型),* Handler已经是线程处理,每个用户的请求是线程隔离的*/// 返回WebSocket响应ctx.writeAndFlush(new TextWebSocketFrame("server return:" + text));/*// 群发TextWebSocketFrame twsf = new TextWebSocketFrame(new Date().toString()+ ctx.channel().id()+ " : "+ text);NettyConfig.group.writeAndFlush(twsf);*/}/*** 处理客户端http握手请求** @param ctx* @param request*/private void handlerHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {log.info("handlerHttpRequest>>>>class={}", request.getClass().getName());// 判断是否采用WebSocket协议if (!request.getDecoderResult().isSuccess() || !("websocket".equals(request.headers().get("Upgrade")))) {sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));return;}WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);webSocketServerHandshaker = wsFactory.newHandshaker(request);if (webSocketServerHandshaker == null) {WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());} else {webSocketServerHandshaker.handshake(ctx.channel(), request);}}private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, DefaultFullHttpResponse response) {if (response.getStatus().code() != 200) {ByteBuf buf = Unpooled.copiedBuffer(response.getStatus().toString(), StandardCharsets.UTF_8);response.content().writeBytes(buf);buf.release();}// 服务端向客户端发送数据ChannelFuture f = ctx.channel().writeAndFlush(response);if (response.getStatus().code() != 200) {f.addListener(ChannelFutureListener.CLOSE);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// 非正常断开时调用log.error("client channel execute exception, id={}", ctx.channel().id().toString(), cause);ctx.close();}
}

NettyConfig:

package com.lzq.websocket.config;import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;public class NettyConfig {/*** 存储接入的客户端的channel对象*/public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}

使用Apifox测试:
在这里插入图片描述

相关文章:

基于Netty构建Websocket服务端

除了构建TCP和UDP服务器和客户端&#xff0c;Netty还可以用于构建WebSocket服务器。WebSocket是一种基于TCP协议的双向通信协议&#xff0c;可以在Web浏览器和Web服务器之间建立实时通信通道。下面是一个简单的示例&#xff0c;演示如何使用Netty构建一个WebSocket服务器。 项目…...

基于Rocket MQ扩展的无限延迟消息队列

基于Rocket MQ扩展的无限延迟消息队列 背景: Rocket MQ支持的延迟队列时间是固定间隔的, 默认19个等级(包含0等级): 0s, 1s, 5s, 10s, 30s, 1m, 2m, 3m, 4m, 5m, 6m, 7m, 8m, 9m, 10m, 20m, 30m, 1h. 我们的需求是实现用户下单后48小时或72小时给用户发送逼单邮件. 使用默认的…...

Python办公自动化 – 日志分析和自动化FTP操作

Python办公自动化 – 日志分析和自动化FTP操作 以下是往期的文章目录&#xff0c;需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处理 Python办公自动化 – 对…...

MyBatis 关联查询

目录 一、一对一查询&#xff08;sqlMapper配置文件&#xff09; 1、需求&#xff1a; 2、创建account和user实体类 3、创建AccountMapper 接口 4、创建并配置AccountMapper.xml 5、测试 二、一对多查询&#xff08;sqlMapper配置文件&#xff09; 1、需求&#xff1a;…...

NVIDIA NCCL 源码学习(十二)- double binary tree

上节我们以ring allreduce为例看到了集合通信的过程&#xff0c;但是随着训练任务中使用的gpu个数的扩展&#xff0c;ring allreduce的延迟会线性增长&#xff0c;为了解决这个问题&#xff0c;NCCL引入了tree算法&#xff0c;即double binary tree。 double binary tree 朴素…...

.net core webapi 大文件上传到wwwroot文件夹

1.配置staticfiles(program文件中) app.UseStaticFiles();2.在wwwroot下创建upload文件夹 3.返回结果封装 namespace webapi;/// <summary> /// 统一数据响应格式 /// </summary> public class Results<T> {/// <summary>/// 自定义的响应码&#xff…...

C++设计模式 #3策略模式(Strategy Method)

动机 在软件构建过程中&#xff0c;某些对象使用的的算法可能多种多样&#xff0c;经常改变。如果将这些算法都写在类中&#xff0c;会使得类变得异常复杂&#xff1b;而且有时候支持不频繁使用的算法也是性能负担。 如何在运行时根据需求透明地更改对象的算法&#xff1f;将…...

金融知识——OMS、EMS和PMS分别是什么意思

金融知识——OMS、EMS和PMS分别是什么意思 OMSEMSPMS OMS OMS&#xff08;Order Management System&#xff09;是为了管理头寸&#xff0c;以多种方式创建订单&#xff0c;并进行订单屈从检验以使得用户在订单创建时收到一些约束。在交易管理方面&#xff0c;OMS提供交易组合…...

Docker——微服务的部署

Docker——微服务的部署 文章目录 Docker——微服务的部署初识DockerDocker与虚拟机Docker架构安装DockerCentOS安装Docker卸载&#xff08;可选&#xff09;安装docker启动docker配置镜像加速 Docker的基本操作Docker的基本操作——镜像Docker基本操作——容器Docker基本操作—…...

AI时代架构设计新模式

云原生架构原则 云原生架构本身作为一种架构&#xff0c;也有若干架构原则作为应用架构的核心架构控制面&#xff0c;通过遵从这些架构原则可以让技术主管和架构师在做技术选择时不会出现大的偏差。 服务化原则 当代码规模超出小团队的合作范围时&#xff0c;就有必要进行服务…...

速盾网络:高防IP的好处

随着互联网的快速发展&#xff0c;网络安全问题日益突出&#xff0c;越来越多的企业和个人开始关注网络安全防护。其中&#xff0c;高防IP作为一种高效的防御手段&#xff0c;越来越受到用户的青睐。本文将介绍速盾网络高防IP的好处&#xff0c;帮助您了解其优势和应用场景。一…...

创建Maven Web工程

目录下也会有对应的生命周期。其中常用的是&#xff1a;clean、compile、package、install。 比如这里install &#xff0c;如果其他项目需要将这里的模块作为依赖使用&#xff0c;那就可以 install 。安装到本地仓库的位置&#xff1a; Java的Web工程&#xff0c;所以我们要选…...

【PHP入门】2.2 流程控制

-流程控制- 流程控制&#xff1a;代码执行的方向 2.2.1控制分类 顺序结构&#xff1a;代码从上往下&#xff0c;顺序执行。&#xff08;代码执行的最基本结构&#xff09; 分支结构&#xff1a;给定一个条件&#xff0c;同时有多种可执行代码&#xff08;块&#xff09;&am…...

springCould中的zookeeper-从小白开始【3】

目录 1.启动zookeeper❤️❤️❤️ 2.创建8004模块 ❤️❤️❤️ 3.临时节点还是永久节点❤️❤️❤️ 4.创建zk80消费模块❤️❤️❤️ 1.启动zookeeper❤️❤️❤️ 进入自己zookeeper的bin目录下 分别使用命令&#xff1a; ./zkServer.sh start 和 ./zkCli.sh -serve…...

Node.js-模块化(二)

1. 模块化的基本概念 1.1 什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层将系统拆分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 1.2 编程领域中的模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&…...

MAC 安装nginx

使用Homebrew方式进行安装 步骤&#xff1a; 1、更新 Homebrew brew update 2、下载并安装 Nginx brew install nginx 3、查看 nginx 配置信息 brew info nginx zhanghuaBreeze ~ % brew info nginx // 版本信息 > nginx: stable 1.25.1 (bottled), HEAD HTTP(S) se…...

开源 AI 新秀崛起:Bittensor 更像是真正的“OpenAI”

强大的人工智能正在飞速发展&#xff0c;而完全由 OpenAI、Midjourney、Google&#xff08;Bard&#xff09;这样的少数公司控制 AI 不免让人感到担忧。在这样的背景下&#xff0c;试图用创新性解决方案处理人工智能中心化问题、权力集中于少数公司的 Bittensor&#xff0c;可谓…...

设计模式:循序渐进走入工厂模式

文章目录 前言一、引入二、简单工厂模式1.实现2.优缺点3.扩展 三、工厂方法模式1.实现2.优缺点 四、抽象工厂模式1.实现2.优缺点3.使用场景 五、模式扩展六、JDK源码解析总结 前言 软件设计模式之工厂模式。 一、引入 需求&#xff1a;设计一个咖啡店点餐系统。 设计一个咖啡类…...

如何将图片(matlab、python)无损放入word论文

许多论文对插图有要求&#xff0c;直接插入png、jpg一般是不行的&#xff0c;这是一篇顶刊文章&#xff08;pdf&#xff09;的插图&#xff0c;放大2400%后依旧清晰&#xff0c;搜罗了网上的方法&#xff0c;总结了一下如何将图片无损放入论文中。 这里主要讨论的是数据生成的图…...

在Next.js和React中搭建Cesium项目

在Next.js和React中搭建Cesium项目&#xff0c;需要确保Cesium能够与服务端渲染(SSR)兼容&#xff0c;因为Next.js默认是SSR的。Cesium是一个基于WebGL的地理信息可视化库&#xff0c;通常用于在网页中展示三维地球或地图。下面是一个基本的步骤&#xff0c;用于在Next.js项目中…...

docker学习(十、搭建redis集群,三主三从)

文章目录 一、docker创建6个redis容器创建6个redis容器回顾各个属性含义 二、划分主从&#xff0c;3主3从划分主从查看状态查看节点信息 docker搭建Redis集群相关知识&#xff1a; docker学习&#xff08;九、分布式存储亿级数据知识&#xff09; docker学习&#xff08;十、搭…...

ES排错命令

GET _cat/indices?v&healthred GET _cat/indices?v&healthyellow GET _cat/indices?v&healthgreen确定哪些索引有问题&#xff0c;多少索引有问题。_cat API 可以通过返回结果告诉我们这一点 查看有问题的分片以及原因。 这与索引列表有关&#xff0c;但是索引…...

爬虫实战案例 -- 爬取豆瓣读书网页内容

进入网站检查信息 , 确定请求方式以及相关数据 找到爬取目标位置 开始敲代码 # 链接网站 def url_link(url):res requests.get(url,headers headers)response res.textparse_data(response)# 爬取信息 def parse_data(data):msg <li\sclass"media\sclearfix…...

某电子文档安全管理系统 SQL注入漏洞复现

漏洞介绍 亿赛通电子文档安全管理系统 (简称: CDG)是一款电子文档安全加密软件&#xff0c;该系统利用驱动层透明加密技术&#xff0c;通过对电子文档的加密保护&#xff0c;防止内部员工泄密和外部人员非法窃取企业核心重要数据资产&#xff0c;对电子文档进行全生命周期防护…...

ant-design-vue Message 用法以及内容为 html片段情况

全局配置&#xff1a; // main.ts// 进行全局配置 message.config({top: 0.7rem,//高度位置duration: 2,//提示持续时间maxCount: 1,//最大页面同时展示几条 });项目中最常用的用法&#xff1a; 1. 字符串 message.success("导入成功");2. html片段 message.error…...

2024 Move 开发者大会火热报名中!1 月 13 至 14 日上海见

2024 Move 开发者大会将于 1 月 13 日至 1 月 14 日在上海举办。本届 Move 开发者大会以 “Move 生态关键的一年” 为主题&#xff0c;由 MoveFuns 、OpenBuild 和 MoveBit 主办&#xff0c;Rooch、AptosGlobal、alcove、zkMove、云赛空间和 TinTinLand 协办&#xff0c;并得到…...

hbase用shell命令新建表报错ERROR: KeeperErrorCode = NoNode for /hbase/master

或者HMster开启后几秒消失问题解决 报错如图&#xff1a; 首先jps命令查看当前运行的内容有没有HMaster,如果没有&#xff0c;开启一下hbase,稍微等一会儿&#xff0c;再看一下HMaster,如果仍和下图一样没有&#xff0c;就基本找到问题了 本人问题原因&#xff1a;hbase-site…...

PyQt中的冒号(:)

在这段代码中&#xff0c;冒号&#xff08;:&#xff09;的使用是类型注解的一种形式&#xff0c;用于显式地指定变量的类型。在Python 3.5及以后的版本中&#xff0c;引入了类型注解的概念&#xff0c;可以在变量名后面使用冒号来注解变量的类型。 例如&#xff0c;在以下代码…...

yolo-nas无人机高空红外热数据小目标检测(教程+代码)

前言 YOLO-NAS是目前最新的YOLO目标检测模型。从一开始&#xff0c;它就在准确性方面击败了所有其他 YOLO 模型。与之前的 YOLO 模型相比&#xff0c;预训练的 YOLO-NAS 模型能够以更高的准确度检测更多目标。但是我们如何在自定义数据集上训练 YOLO NAS&#xff1f; 这将是我…...

Ubuntu22.04安装python2

Ubuntu 20.04是继Ubuntu 18.04 LTS之后的下一个长期支持(LTS)版本&#xff0c;从Ubuntu 20.04开始系统不再预装python2。 如果我们有一些老旧的代码需要运行&#xff0c;可以在新版的Ubuntu系统上自行安装python2。 安装命令&#xff1a; sudo apt install python2查看pytho…...

做网站需要多少钱呢/迅雷bt磁力链 最好用的搜索引擎

前端应用在不断壮大的过程中&#xff0c;内部模块间的依赖可能也会随之越来越复杂&#xff0c;模块间的 低复用性 导致应用 难以维护&#xff0c;不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题&#xff0c;接下来要讲述的 IoC 就是其中之一。什么是…...

做网站大公司有哪些/百度seo效果

内容出自《代码整洁之道》、Alex Kondov[1]的博文tao-of-react[2]和《Clean Code of Javascript》image.png代码整洁有什么用&#xff1f;image.png思路清晰&#xff0c;降低bug几率更容易维护&#xff0c;利于团队协作看起来舒服&#xff0c;提高效率......软件质量与代码整洁…...

哪个网站做脚本/抖音seo关键词优化怎么做

chown (change owner) 更改所有者 查看系统所有用户 这里面都是系统默认的用户 [rootevan-01 ~]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x…...

新建站点/网络营销管理名词解释

curl下载地址&#xff1a;https://curl.haxx.se/download.html&#xff0c;拉到页面最底下&#xff0c;选择红色选中的那个CAB的进行下载&#xff0c;如下图所示&#xff1a; 下载完成后&#xff0c;解压。 解决windows控制台curl中文乱码问题 下载iconv&#xff0c;地址&#…...

贵州贵阳网站开发/如何推广网站运营

今天给大家介绍一下经典的开源机器学习软件&#xff1a; 编程语言&#xff1a;搞实验个人认为当然matlab最灵活了&#xff08;但是正版很贵&#xff09;&#xff0c;但是更为前途的是Python&#xff08;numpyscipymatplotlib)和C/C&#xff0c;这样组合既可搞研究&#xff0c;也…...

自媒体平台账号注册/郑州网站seo顾问

Windows 8是微软公司推出的最新的客户端操作系统&#xff0c;内部名称Windows NT 80。相对于Windows NT 5.x&#xff0c;其网络结构变化非常大&#xff0c;原有的TDI&#xff0c;NDIS系统挂接方法不再适用。在Windows8系统中&#xff0c;微软引入了两种新的网络过滤系统&#x…...