1.添加依赖

2.netty服务器启动

springboot的bean代码,另开一个线程启动

@Component
public class NettyServer {private static Logger logger = LoggerFactory.getLogger(NettyServer.class);// 保存response的mappublic static Map<String, Response> map = new HashMap<String, Response>();// 保存客户端连接的通道引用public static SocketChannel sc = null;public static EventLoopGroup acceptor;public static EventLoopGroup worker;@PostConstructpublic void init() throws InterruptedException {new NettyServerThread().start();logger.info("nettyServer启动");}@PreDestroypublic void exit() {acceptor.shutdownGracefully();worker.shutdownGracefully();}}

具体启动的代码

public class NettyServerThread extends Thread {private static Logger logger = LoggerFactory.getLogger(NettyServerThread.class);@Overridepublic void run() {EventLoopGroup acceptor = new NioEventLoopGroup();EventLoopGroup worker = new NioEventLoopGroup();NettyServer.acceptor = acceptor;NettyServer.worker = worker;ServerBootstrap bootstrap = new ServerBootstrap();// 添加boss和worker组bootstrap.group(acceptor, worker);//这句是指定允许等待accept的最大连接数量,我只需要连一个客户端,这里就关掉了,java默认是50个// bootstrap.option(ChannelOption.SO_BACKLOG, 1024);bootstrap.option(ChannelOption.TCP_NODELAY, true);// 用于构造socketchannel工厂bootstrap.channel(NioServerSocketChannel.class);/*** 传入自定义客户端Handle(处理消息)*/bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {if (NettyServer.sc == null) {logger.info("来自" + ch.remoteAddress() + "的新连接接入");NettyServer.sc= ch;// 注册handlerch.pipeline().addLast(new ReadTimeoutHandler(10));ch.pipeline().addLast(new MessageHandler());} else {ch.close();}}});// 绑定端口,开始接收进来的连接ChannelFuture f;try {f = bootstrap.bind(8888).sync();// 等待服务器 socket 关闭 。f.channel().closeFuture().sync();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

这里是netty启动的核心代码,通过bootstrap添加各种配置来装饰nettyserver并最终启动

3.group

我们都知道nio对比bio最大的特点就是不会阻塞,具体怎么实现的呢,就在
NioEventLoopGroup里,这里其实是新建了一个线程池去执行一些任务
这里acceptor里的线程用来维护accept接入新连接的SelectionKey,worker里的线程用来维护客户端的SelectionKey

如果你只给一个线程池,实际上也可以使用,这种情况下acceptor和worker需要完成的工作都会使用这一个线程池中的线程

4.Handler

initChannel方法就是重载accept接入后初始化通道的方法了,通道被accept之后该通道的所有SelectionKey都会通过同一个线程来维护(为了避免线程并发的问题,但他们之间并非一一对应,一个线程可以同时维护多个通道的SelectionKey)

在initChannel方法,我给通道添加了两个Handler,第一个是超时十秒会抛出异常并断开连接,第二个是我自定义的处理客户端发送信息的Handler,netty基本上大部分业务代码会在自定义Handler里编写

public class MessageHandler extends ChannelInboundHandlerAdapter {private static Logger logger = LoggerFactory.getLogger(MessageHandler.class);/*** 本方法用于读取客户端发送的信息* * @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf msgByteBuf = (ByteBuf) msg;logger.info(msgByteBuf.toString());byte[] msgBytes = new byte[msgByteBuf.readableBytes()];// msg中存储的是ByteBuf类型的数据,把数据读取到byte[]中msgByteBuf.readBytes(msgBytes);// 释放资源msgByteBuf.release();// 可能返回到的msgByteBuf是多条信息拼起来的,把他们拆开分别处理List<byte[]> list = getMsgList(msgBytes);// 真正处理信息的方法list.forEach(v -> handler(v, ctx));}/*** 切分信息的方法* * @param msgBytes* @return*/private List<byte[]> getMsgList(byte[] msgBytes) {List<byte[]> list = new ArrayList<byte[]>();//具体业务代码略return list;}/*** 本方法用作处理异常* * @param ctx* @param cause* @throws Exception*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {if (cause.getClass() == io.netty.handler.timeout.ReadTimeoutException.class) {logger.info("来自" + NettyServer.sc.remoteAddress() + "的连接超时断开");} else {cause.printStackTrace();logger.info("来自" + NettyServer.sc.remoteAddress() + "的连接异常断开");ctx.close();}NettyServer.sc= null;}/*** 信息获取完毕后操作** @param ctx* @throws Exception*/@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}/*** 断开连接时操作** @param ctx* @throws Exception*/@Overridepublic void channelUnregistered(ChannelHandlerContext ctx) throws Exception {if (NettyServer.sc!= null) {logger.info("来自" + NettyServer.sc.remoteAddress() + "的连接主动断开");NettyServer.sc= null;}ctx.fireChannelUnregistered();}/*** 根据信息具体操作的业务方法* * @param msgBytes* @param ctx*/private void handler(byte[] msgBytes, ChannelHandlerContext ctx) {// 具体业务代码略,可以通过ctx的write和flush方法回应客户端的信息}}

真正重要的是重写的几个方法,下面逐一介绍

1.channelUnregistered

在客户端断开通道(或其他原因,总之触发了Unregistered这个SelectionKey)时,记录日志,调用ctx.fireChannelUnregistered();做netty关闭通道的一些处理,并把连入的客户端置空

2.exceptionCaught

处理异常,因为前面加Handler的顺序这个在ReadTimeoutHandler后面,所以ReadTimeoutHandler抛出的异常可以在这里被处理
如果是ReadTimeoutException,则记录超时断开的日志,否则打印出具体异常,关闭通道,并记录异常断开的日志

3.channelReadComplete

执行ctx.flush();
为什么要这么做,因为有时候客户端发送信息不会在发送后清空管道,这样就没有结束标识,read的SelectionKey不会触发,我们这里执行一下刷新

4.channelRead

这里就是接收到的具体信息,需要注意的是,这里可能是客户端发送的多条信息连起来,所以要按照业务的逻辑切分开分别处理,
可以在处理后通过传入的ctx写入你的回复

使用springboot+netty处理tcp/ip服务端编程相关推荐

  1. C#创建TCP/IP服务端和客户端,含测试demo及源码

    网上的TCP/IP创建服务端和客户端的方法鱼龙混杂,自己把在使用项目中的TCP服务端和客户端的代码抽了出来,做了demo,以供大家使用参考. 为了方便调用,我把一些基础方法都整合封装到了Reader. ...

  2. Springboot+Netty搭建TCP客户端-多客户端

    之前搭建了一个Springboot+Netty服务端的应用,既然有服务端,自然也有客户端的应用,现在搭建一个Springboot+Netty客户端的应用Demo程序,多客户端方式,使用服务端和客户端进 ...

  3. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  4. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  5. 使用Netty实现客户端和服务端之间的双向通信

    欢迎阅读本篇文章 提示:本文只是提供部分核心代码,源码详见代码示例 使用Netty实现客户端和服务端之间的双向通信 前言 一.服务端 二.客户端 前言 在上个月的开发计划中,有一个系统控制喇叭播放的功 ...

  6. 易语言tcp多线程服务端客户端_从TCP协议到TCP通信的各种异常现象和分析

    很多人总觉得学习TCP/IP协议没什么用,觉得日常编程开发只需要知道socket接口怎么用就可以了.如果大家定位过线上问题就会知道,实际上并非如此.如果应用在局域网内,且设备一切正常的情况下可能确实如 ...

  7. Qt中TCP服务端编程

    文章目录 1 Qt中的TCP服务端编程 1.1 TCP服务端编程介绍 1.2 Qt中的TCP服务端编程 1 Qt中的TCP服务端编程 1.1 TCP服务端编程介绍 网络中的服务端: 服务端是为客户端服 ...

  8. MSSQL TCP/IP服务无法启动的解决方法

    (转)SQL Server服务,SQL Server Agent,TCP/IP服务无法启动的解决方法 在使用360将loadrunner9.5卸载后,发现使用数据库的程序都不能正常启动,后去看SQL ...

  9. 《Linux多线程服务端编程:使用muduoC++网络库》学习笔记

    文章目录 第1章 线程安全的对象生命期管理 1.1 当析构函数遇到多线程 1.1.1 线程安全的定义 1.1.3 线程安全实例 1.2 对象的创建很简单 1.3 销毁很难 1.4 线程安全的Obser ...

最新文章

  1. 自定义控件(一) Activity的构成(PhoneWindow、DecorView)
  2. JS 创建自定义对象的方法
  3. PHP 实现中文截取无乱码的方法
  4. python wxpython_python GUI wxPython
  5. mysql中新建不了查询语句_将excel和mysql建立链接后,如何通过在excel里面执行mysql查询语句,然后建立查询...
  6. 机器学习笔记(2):单变量线性回归
  7. Linux统治超级计算领域的九个理由
  8. 前端安全 -- XSS攻击
  9. python的调试器_玩转Python调试器
  10. mysql分布式写入_分布式系统知识点七:mysql读写分离简介(转载)
  11. 智慧消防、消防管理、事件管理、维保巡检、应急管理、培训管理、值班管理、考试管理、设备列表、机构管理、应急预案、axure原型、rp原型
  12. 从Iris数据集开始---机器学习入门
  13. Linux安装python3.8时,编译过程中报错Could not build the ssl module!
  14. 生成对抗网络 GAN 基本原理与发展历程
  15. 研磨设计模式-设计模式的基础-设计模式有什么
  16. 【ESP32 Arduino平衡小车制作】(一)霍尔编码器解码
  17. 【过关斩将】如何制作高水平简历-观念篇
  18. (最完美)MIUI12系统的Usb调试模式在哪里开启的步骤
  19. 阿里云datav看板然后设置密码
  20. 2022/03/03js作业第一个不同宽度变色第二个是输入几年几月几日判断是今年的第几天(不算闰年2月为28日)

热门文章

  1. 移位操作和乘法的比较
  2. Lua 协程和线程区别
  3. 手机的光学变焦,实际上是不同焦距摄像头合成的
  4. 手机私有充电协议解读
  5. MySql保留两位小数(VIP典藏版)
  6. java-net-php-python-ssm二手商品交易平台的设计与实现(2)计算机毕业设计程序
  7. 音视频系列---最强播放器推荐
  8. 垂直搜索能否抢占通用搜索地盘?
  9. 你适合做程序员吗?一篇文章告诉你如何判断自己是否适合做程序员
  10. 对象入参指定泛型类型,如何得到正确的MethodInfo对象当一个类使用泛型和泛型类型参数...