netty编程之结合springboot一起使用

写在前面

源码 。
本文看下netty结合springboot如何使用。

1:netty server部分

server类(不要main,后续通过springboot来启动咯!)

package com.dahuyou.netty.springboot.server;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.net.InetSocketAddress;@Component("nettyServer")
public class NettyServer {private Logger logger = LoggerFactory.getLogger(NettyServer.class);//配置服务端NIO线程组private final EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));private final EventLoopGroup childGroup = new NioEventLoopGroup();private Channel channel;public ChannelFuture bing(InetSocketAddress address) {ChannelFuture channelFuture = null;try {ServerBootstrap b = new ServerBootstrap();b.group(parentGroup, childGroup).channel(NioServerSocketChannel.class)    //非阻塞模式.option(ChannelOption.SO_BACKLOG, 128).childHandler(new MyChannelInitializer());channelFuture = b.bind(address).syncUninterruptibly();channel = channelFuture.channel();} catch (Exception e) {logger.error(e.getMessage());} finally {if (null != channelFuture && channelFuture.isSuccess()) {logger.info("netty server start done. {大忽悠有限没责任公司}");} else {logger.error("netty server start error. {大忽悠有限没责任公司}");}}return channelFuture;}public void destroy() {if (null == channel) return;channel.close();parentGroup.shutdownGracefully();childGroup.shutdownGracefully();}public Channel getChannel() {return channel;}}

MyChannelInitializer:

package com.dahuyou.netty.springboot.server;import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;import java.nio.charset.Charset;public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel channel) {// 解码转String,注意调整自己的编码格式GBK、UTF-8channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));// 解码转String,注意调整自己的编码格式GBK、UTF-8channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));// 在管道中添加我们自己的接收数据实现方法channel.pipeline().addLast(new MyServerHandler());}}

MyServerHandler:

package com.dahuyou.netty.springboot.server;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.SocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.text.SimpleDateFormat;
import java.util.Date;public class MyServerHandler extends ChannelInboundHandlerAdapter {private Logger logger = LoggerFactory.getLogger(MyServerHandler.class);/*** 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {SocketChannel channel = (SocketChannel) ctx.channel();logger.info("链接报告开始");logger.info("链接报告信息:有一客户端链接到本服务端");logger.info("链接报告IP:{}", channel.localAddress().getHostString());logger.info("链接报告Port:{}", channel.localAddress().getPort());logger.info("链接报告完毕");//通知客户端链接建立成功String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";ctx.writeAndFlush(str);}/*** 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {logger.info("客户端断开链接{}", ctx.channel().localAddress().toString());}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//接收msg消息{与上一章节相比,此处已经不需要自己进行解码}logger.info(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 服务端接收到消息:" + msg);//通知客户端链消息发送成功String str = "服务端收到:" + new Date() + " " + msg + "\r\n";ctx.writeAndFlush(str);}/*** 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();logger.info("异常信息:\r\n" + cause.getMessage());}}

2:springboot部分

springboot app:

package com.dahuyou.netty.springboot.web;import com.dahuyou.netty.springboot.server.NettyServer;
import io.netty.channel.ChannelFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import java.net.InetSocketAddress;// CommandLineRunner接口方法会在springboot启动完毕之后调用org.springframework.boot.SpringApplication.callRunners
@SpringBootApplication
@ComponentScan("com.dahuyou.netty.springboot")
public class NettyApplication implements CommandLineRunner {@Value("${netty.host}")private String host;@Value("${netty.port}")private int port;@Autowiredprivate NettyServer nettyServer;public static void main(String[] args) {SpringApplication.run(NettyApplication.class, args);}@Overridepublic void run(String... args) throws Exception {InetSocketAddress address = new InetSocketAddress(host, port);ChannelFuture channelFuture = nettyServer.bing(address);// 在jvm钩子中停止netty(jvm正常退出时回调用)Runtime.getRuntime().addShutdownHook(new Thread(() -> nettyServer.destroy()));channelFuture.channel().closeFuture().syncUninterruptibly();}}

这里通过CommandLineRunner接口来启动netty程序,另外为了获取netty的相关状态还定义了一个controller:

package com.dahuyou.netty.springboot.web;import com.dahuyou.netty.springboot.server.NettyServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;@RestController
@RequestMapping(value = "/nettyserver", method = RequestMethod.GET)
public class NettyController {@Resourceprivate NettyServer nettyServer;@RequestMapping("/localAddress")public String localAddress() {return "nettyServer localAddress " + nettyServer.getChannel().localAddress();}@RequestMapping("/isOpen")public String isOpen() {return "nettyServer isOpen: " + nettyServer.getChannel().isOpen();}}

3:test case部分

代码:

package com.dahuyou.netty.springboot.test;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;public class ApiTest {public static void main(String[] args) {EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(workerGroup);b.channel(NioSocketChannel.class);b.option(ChannelOption.AUTO_READ, true);b.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel channel) throws Exception {// 基于换行符号channel.pipeline().addLast(new LineBasedFrameDecoder(1024));// 解码转String,注意调整自己的编码格式GBK、UTF-8channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));// 解码转String,注意调整自己的编码格式GBK、UTF-8channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));// 在管道中添加我们自己的接收数据实现方法channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//接收msg消息{与上一章节相比,此处已经不需要自己进行解码}System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 客户端接收到消息:" + msg);}});}});ChannelFuture f = b.connect("127.0.0.1", 7397).sync();System.out.println("netty client start done. {大忽悠有限没责任公司}");//向服务端发送信息f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,这里是大忽悠有限没责任公司,你可不要被我忽悠了哦!!!”\r\n");f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,这里是大忽悠有限没责任公司,你可不要被我忽悠了哦!!!”\r\n");f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,这里是大忽悠有限没责任公司,你可不要被我忽悠了哦!!!”\r\n");f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,这里是大忽悠有限没责任公司,你可不要被我忽悠了哦!!!”\r\n");f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,这里是大忽悠有限没责任公司,你可不要被我忽悠了哦!!!”\r\n");f.channel().closeFuture().syncUninterruptibly();} catch (InterruptedException e) {e.printStackTrace();} finally {workerGroup.shutdownGracefully();}}}

运行client输出:
在这里插入图片描述
服务端输出:
在这里插入图片描述
等等,好像有问题,在client明明是write了几句话的,就这样的:
在这里插入图片描述
怎么当作一句话来接收了呢?其实,这就是发生了粘包了,知道了问题所在,解决也就简单了,增加一个相关的解码器即可,因为在每句话的结尾我们都增加了换行符,所以这里我们直接使用netty提供的基于换行符的解码器LineBasedFrameDecoder就行,即修改MyChannelInitializer类即可:
在这里插入图片描述
接着重新启动测试,server输出就正常了:
在这里插入图片描述

写在后面

参考文章列表

springboot之项目搭建并say hi 。

扩展

在以上的client测试类中,我们在write数据的时候,每句话的结尾都增加了换行符\r\n,这显然是一个重复的工作了。是否可以将这个工作只做一遍呢,可以的,使用netty提供的outboundhandler就可以很轻松的来解决这个问题了,首先我们需要先来定义一个ChannelOutboundHandler:

package com.dahuyou.netty.springboot.test;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;/*** 同意增加换行符的outbound handler*/
public class MyAddNewLineOutMsgHandler extends ChannelOutboundHandlerAdapter {@Override // 调用ctx的writeAndFlush方法时会被该方法拦截执行public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {System.out.println("MyAddNewLineOutMsgHandler--统一增加换行符咯!!!");msg += "\r\n";super.write(ctx, msg, promise);}}

接着修改client测试类,将所有的换行符删掉:
在这里插入图片描述
还需要将统一增加换行符的handler添加到channel中:
在这里插入图片描述
再重新启动clienit测试:
在这里插入图片描述
效果还是一样的,但是少做了很多重复的工作,是吧???

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

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

相关文章

Python实现t-分布随机邻域嵌入(t-SNE)降维算法

目录 Python实现t-分布随机邻域嵌入&#xff08;t-SNE&#xff09;降维算法的博客引言t-SNE算法原理t-SNE的优势与局限Python实现t-SNE算法1. 创建t-SNE类2. 示例场景&#xff1a;MNIST手写数字数据集3. 结果分析 结论运行结果 Python实现t-分布随机邻域嵌入&#xff08;t-SNE&…

2024上海初中生古诗文大会备考:单选题真题和每道题独家解析

新学年开学倒计时了&#xff0c;也意味着上海中小学生的几项传统赛事即将拉开帷幕了。 其中&#xff0c;2024年初中生古诗文大会初选还有2个多月&#xff08;官宣了11月3日线上初选正式开赛&#xff09;&#xff0c;我们来看10道历年的选择题真题和详细解析。为帮助孩子自测和…

模型 KT决策法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。系统分析&#xff0c;明智选择。 1 KT决策法的应用 1.1 餐饮连锁店菜单更新 一家餐饮连锁店计划更新菜单&#xff0c;以吸引更多顾客并提高销售额。使用 KT 决策法&#xff08;Kepner-Tregoe&#x…

xss-labs靶场通关详解(11-15关)

第11关 referer 进行抓包 添加referer:click me!" type"button" οnmοuseοver"alert(/xss/)进行放包 第12关 进行抓包 修改User Agent&#xff1a;click me!" type"button" οnmοuseοver"alert(/xss/)进行放包 第13关 抓包 修改C…

python可视化-密度图

1、加载数据 import pandas as pd import numpy as np from sklearn.datasets import load_iris import warnings# 禁用所有警告信息 warnings.filterwarnings(ignore)# 加载数据 iris load_iris() iris iris.keys() df pd.DataFrame(iris.data, columnsiris.feature_names)…

mac nvm安装及使用(nvm安装指定版本node npm pnpm)

mac nvm安装及使用&#xff08;nvm安装指定版本node npm pnpm&#xff09; 1.卸载电脑的node 打开终端&#xff1a;依次执行以下命令&#xff1a; sudo rm -rf /usr/local/bin/npm sudo rm -rf /usr/local/share/man/man1/node.1 sudo rm -rf /usr/local/lib/dtrace/node.d s…

开源word文档相似度对比 软件WinMerge

WinMerge 官网下载 &#xff1a;GitHub - WinMerge/winmerge: WinMerge is an Open Source differencing and merging tool for Windows. WinMerge can compare both folders and files, presenting differences in a visual text format that is easy to understand and hand…

API网关之Kong

Kong 是一个高性能的开源 API 网关和微服务管理平台&#xff0c;用于管理、保护和扩展 API 和微服务。它最初由 Mashape 公司开发&#xff0c;并于 2015 年作为开源项目发布。Kong 能够处理 API 的路由、认证、负载均衡、缓存、监控、限流等多种功能&#xff0c;是微服务架构中…

网络应用层之(2)DNS协议

网络应用层之(2)DNS协议 Author: Once Day Date: 2024年8月12日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 通信网络技术_Once-Day的博客-CSDN…

PyTorch深度学习网络(二:CNN)

卷积神经网络&#xff08;CNN&#xff09;是一种专门用于处理具有类似网格结构数据的深度学习模型&#xff0c;例如图像&#xff08;2D网格的像素&#xff09;和时间序列数据&#xff08;1D网格的信号强度&#xff09;。CNN在图像识别、图像分类、物体检测、语音识别等领域有着…

R语言绘制可用于论文发表的生存曲线图|科研绘图·24-08-25

小罗碎碎念 有关于生存曲线的基本概念&#xff08;例如删失事件的定义&#xff09;和绘图的详细教程我已经在5月的推文中介绍过了&#xff0c;有需求的同学欢迎前去考古。 R语言绘制生存分析曲线从概念到实战的保姆级教程&#xff5c;2024-05-12 https://mp.weixin.qq.com/s/Z…

SQL进阶技巧:如何按任意时段分析时间区间问题? | 分区间讨论【左、中、右】

目录 0 场景描述 1 数据准备 2 问题分析 方法1:分情况讨论,找出重叠区间 方法2:暴力美学法。按区间展开成日期明细表 3 拓展案例 4小结 0 场景描述 现有用户还款计划表 user_repayment ,该表内的一条数据,表示用户在指定日期区间内 [date_start, date_end] ,每天…

秋招突击——8/21——知识补充——计算机网络——cookie、session和token

文章目录 引言正文Cookie——客户端存储和管理Session——服务端存储和管理Token补充签名和加密的区别常见的加密算法和签名算法 面试题1、HTTP用户后续的操作&#xff0c;服务端如何知道属于同一个用户&#xff1f;如果服务端是一个集群机器怎么办&#xff1f;2、如果禁用了Co…

【Python 千题 —— 基础篇】简易图书管理系统

Python 千题持续更新中 …… 脑图地址 👉:⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目描述 题目描述 编写一个面向对象的程序,模拟一个图书管理系统。要求定义一个 Book 类,具有基本的书籍信息功能;然后,创建一个 Library 类,用于管理多个 B…

Vue3搜索框(InputSearch)

效果如下图&#xff1a;在线预览 APIs InputSearch 参数说明类型默认值width搜索框宽度&#xff0c;单位 pxstring | number‘100%’icon搜索图标boolean | slottruesearch搜索按钮&#xff0c;默认时为搜索图标string | slotundefinedsearchProps设置搜索按钮的属性&#xf…

【Qt】容器类控件GroupBox

容器类控件GroupBox 使用QGroupBox实现一个带有标题的分组框&#xff0c;可以把其他的控件放在里面里面作为一组&#xff0c;这些内部的控件的父元素也就不是this了。 其目的只是为了让界面看起来更加好看&#xff0c;例如当一个界面比较复杂的时候&#xff0c;包含了很多的控…

APP封装安装配置参考说明

APP封装安装配置参考说明 一, 环境准备 宝塔环境 nginx php5.6 mysql5.6 java-openjdk1.8 apktool 1,安装 nginx,php,mysql自行安装 java-openjdk1.8 安装 推荐使用命令行安装 1.1 yum install java-1.8.0-openjdk1.2 yum install -y java-1.8.0-openjdk-devel1.3 设置…

Unity | 性能标准分析工具图形API简介

目录 一、相关术语 1.物理页 2.PSS内存 3.Reserved Total 二、耗时推荐值 三、内存推荐值 四、分析工具 1.Profiler &#xff08;1&#xff09;Profiler各平台对比 &#xff08;2&#xff09;构建到目标平台 &#xff08;3&#xff09;Frame数量修改 &#xff08;4…

天宝TBCTrimble Business Center中文版本下载安装使用介绍

天宝TBC&#xff1a;测绘之道&#xff0c;尽在其中 引言 昔日杜甫&#xff0c;忧国忧民&#xff0c;今朝我辈&#xff0c;测绘天下。天宝TBC&#xff0c;乃测绘之利器&#xff0c;助我等行走于山川河流之间&#xff0c;绘制天地之图。此文将以杜甫之笔&#xff0c;述说TBC之妙…

【数据结构】栈(stack)

目录 栈的概念 栈的方法 栈的实现 数组实现 push方法 压栈 pop方法 出栈 peek方法 获取栈顶元素 size方法 获取有效元素个数 链表实现 结尾 完整代码 数组实现栈代码 双向链表实现栈代码 栈的概念 栈是一种特殊的线性表&#xff0c;只允许在 固定的一段 进行插入…