Netty编写客户端

​ 上一篇博客讲了一下解码器,但是其实没有涉及到客户端的编写,今天补上这篇博客。同时深入了解一下Netty(对于我来说)。加深自己的印象。

​ 上面是一个简单的服务端的例子,之前的博客也讲过这个demo,下面按照自己最新的理解再次记录一下。首先是最开始创建两个线程组,boss线程组用来监听客户端的连接,只做到这一步,work线程组相当于干活的,当通道中有IO事件时就开始工作。这样做的好处就是可以提升读写性能。这一块的内容涉及到Netty的线程模型,这一块我不是很熟悉,找到一篇文章,大家可以了解一下,感兴趣的可以继续搜索一下:Netty线程模型,以后也会慢慢补上这些内容的,学习无涯。回到代码上,然后就是创建了一个ServerBootstrap,这个启动类的作用就是辅助与编写服务端,降低开发的复杂度。再然后就是设置线程组、设置Channel、配置TCP的参数,编写Netty之前最好还是有点NIO的编写经验,不要像我上来就开始,一点准备工作都没有做,读代码都很吃力。最后的两行代码含义就比较容易懂了,调用辅助类的bind()方法绑定监听某个端口,同时调用同步阻塞方法等待绑定操作完成。最后一行则是等待服务端链路关闭后,退出主方法。

​ 说了这么多,其实和客户端的编写一点关系都没有,主要还是记录一下Netty启动的流程,对于Netty原理和底层的一些操作,这个需要等我看完NIO再记录一下。下面就讲一下Netty编写客户端的思路。

​ 客户端不需要两个线程组,一个就够了,通道也和服务端不太一样,需要设置为NioSocketChannel,剩下就是设置TCP的参数和handler的设置。发起连接和服务端也不太一样,服务端是监听,客户端就是连接,调用connect()方法发起异步连接,然后再调用同步方法等待连接成功。最后就是连接关闭后释放线程组的资源。代码也比较简单,如下:

//配置客户端的线程组,客户端只有一个线程组
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {//放入自己的业务HandlersocketChannel.pipeline().addLast(new TCPClientHandler());}});//发起异步连接操作,同步阻等待结果ChannelFuture channelFuture = bootstrap.connect(host, port).sync();//等待客户端链路关闭channelFuture.channel().closeFuture().sync();
} finally {//释放NIO线程组eventLoopGroup.shutdownGracefully();
}

​ 不要以为到这里就结束了,上面这个只是一个简单的demo,自己写着玩还可以,但是到了生产环境或者实际用的时候,很多东西没有考虑到。断线重连问题、线程管理的问题、连接多服务端的情况。这些问题是我开发时遇到的问题,下面就一一讲一下实现的逻辑,最后则给出整体的代码。

  • 断线重连。断线重连一般时客户端要做的事情,也分为两种情况,一开始连接不上和连接上过一段时间断了。这两种情况需要不同处理,一开始连接不上需要添加监听器,异步执行,失败就调用两秒重连。断线则需要再handler中的channelInactive()方法做相应的业务处理。
  • 线程管理和连接多服务端的问题其实是一个场景。就是需要连接多个服务端的情况下,一个连接创建一个线程显然是不太合理的,同时也要对这些线程进行管理。这里暂时的办法是单独启动一个线程,线程的run()方法就是启动netty连接。后续在这个线程里面去完成多个客户端的建立或者一个netty线程去连接多个服务端。目前水平有限,只能想到这个地步。涉及到线程和NIO还有Netty。后续慢慢完善吧。

​ 所以最后的代码就如下:因为一些原因,没有给出全部的代码,给出了部分核心的代码,其他的细节和本次的内容关系不大。也可以按照自己的需求进行修改。(最好按照自己的想法修改一下,每个人的需求都不太一样,我的代码一定不合适所有人)

public class ConnectThread extends ConnectThread {private Logger logger = LoggerFactory.getLogger(ConnectThread.class);private Bootstrap bootstrap;private EventLoopGroup eventLoopGroup;private AtomicBoolean running = new AtomicBoolean(true);@Overridepublic void run() {beginConnect();Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {shutDown();}});//阻塞住while (running.get()) {try {Thread.sleep(200l);} catch (Exception e) {logger.error("程序运行发生错误");e.printStackTrace();shutDown();}}}public void beginConnect() {try {eventLoopGroup = new NioEventLoopGroup();bootstrap = new Bootstrap();assembleBootStrap();reConnect();} catch (Exception e) {logger.error("客户端连接发送错误");e.printStackTrace();shutDown();}}/*** Description: 组装BootStrap** @param* @return void* @Author: Peng Shiquan* @Date: 2020/12/9*/public void assembleBootStrap() {ConnectThread ConnectThread = this;bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class);bootstrap.remoteAddress(host, port);bootstrap.option(ChannelOption.TCP_NODELAY, true);bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 6000);bootstrap.option(ChannelOption.SO_KEEPALIVE, true);bootstrap.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) {socketChannel.pipeline().addFirst(new IdleStateHandler(10, 0, 0)).addLast(new Decoder()).addLast(new Handler(ConnectThread));}});}@Overridepublic void reConnect() {super.reConnect();logger.info("雷达开始重试,雷达信息为:{}:{}", host, port);bootstrap.connect(host, port).addListener(new ConnectListener(this));}public void shutDown() {running.set(false);if (closeChannel != null && closeChannel.isActive()) {closeChannel.close().awaitUninterruptibly();closeChannel = null;}bootstrap = null;if (eventLoopGroup != null) {eventLoopGroup.shutdownGracefully();}eventLoopGroup = null;logger.error("客户端关闭连接,雷达服务端信息为:{} : {}", host, port);}
}

​ 来讲一下上面的代码大致的思路。连接类继承了线程,重写了run()方法,run方法里面只有三个部分的代码,一个是启动连接,一个是程序运行中有异常执行关闭方法,释放资源。最后一个就是将线程阻塞住,因为启动连接是异步的,如果不阻塞,代码直接就执行完毕了,后续也没法进行重连的操作。启动连接方法里面只有两步,一个是组装BootStrap启动类,另外一个就是执行重连操作,之前说过,断线重连发生在两个地方,最开始没有连接上和中间断掉,所以这里直接掉同一个方法,不用再重复写方法了。组装启动类按照自己的需求组装就可以了,设置超时事件、设置长时间无数据返回、设置解码器,业务处理类。重连方法就是调用启动类的connect()方法,这里和上面的demo有一点不一样,这里是异步返回的,没有阻塞住,添加了一个监听器,也是为了做重连用的。如果和demo一样的话,这里发生断连,程序就直接关闭了,就没法重连了。想要重连必须重新创建线程组、启动类、通道等,这个显然不是我们想看到了,因为断连后,线程组和和启动类还是可以继续使用的,只不过要换一个连接。

​ 下面就继续附上监听器和handler的代码。(handler中有一部分断线重连的方法)

public class ConnectListener implements ChannelFutureListener {private Logger logger = LoggerFactory.getLogger(ConnectListener.class);private final ConnectThread ConnectThread;public ConnectListener(ConnectThread ConnectThread) {this.ConnectThread = ConnectThread;}@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {logger.info("[" + ConnectThread.getHost() + ":" + ConnectThread.getPort() + "] 启动成功!!!");ConnectThread.setTime(0);ConnectThread.setCloseChannel(future.channel());} else {if (ConnectThread.getTime() >= 1800) {logger.error(ConnectThread.getHost() + ":" + ConnectThread.getPort() + "重试超过一小时,不再重试");return;}logger.warn("[" + ConnectThread.getHost() + ":" + ConnectThread.getPort() + "]Channel失联,2秒后重连。。。 ");future.channel().eventLoop().schedule(new Runnable() {@Overridepublic void run() {ConnectThread.reConnect();}}, 2l, TimeUnit.SECONDS);}}
}

​ 监听器的代码就是判断是否连接上,没有的话进行重试,连接上了就重置次数,同时返回通道信息,为后面关闭通道做准备。剩下句没有什么可以说的了。最好就是handler的代码。

public class XYDHandler extends ChannelInboundHandlerAdapter {private Logger logger = LoggerFactory.getLogger(XYDHandler.class);private XYDConnectThread xydConnectThread;public XYDHandler(XYDConnectThread xydConnectThread) {this.xydConnectThread = xydConnectThread;}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {logger.error("连接断开,IP端口为:{}", ctx.channel().remoteAddress());ctx.channel().eventLoop().schedule(() -> {xydConnectThread.reConnect();}, 2, TimeUnit.SECONDS);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = Unpooled.copiedBuffer((byte[]) msg);logger.info("接收到消息为:{}", ByteBufUtil.hexDump(byteBuf));}

​ 重连的方法就是channelInactive(),和监听器里的代码也是差不多。剩下就没有什么可以说的了。

​ 上面的代码就是完成一个断线重连的Netty的客户端,代码直接粘贴是不能用的,需要进行一些修改,我现在也比较烦直接将代码粘贴过来,上来就开始测试。还是要了解一下具体如何实现的,要不然改代码都不知道如何修改。就像这次编写客户端,最开始写很吃力,因为之前是一知半解,不知道如何修改,不知道如何排查,所以走了很多的弯路,代码完全不能看,也不起用。现在了解了一些后,慢慢能够按照自己的想法将代码改造成自己想要的样子,我觉得这样才算一个东西你彻底掌握了,与君共勉。

​ 就这样吧,结束。

Netty编写客户端相关推荐

  1. Netty工作笔记0042---Netty入门--编写客户端

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 这里来编写客户端 跟服务器的编写实际上是很像的. 可以看到编写上基本都是差不多的,可以看到客户端, ...

  2. 4、netty编写http服务器、增加压缩支持、netty编写client、netty添加SL/TLS保护https支持

    注:源代码来自享学课堂,学习之后所做笔记,方便回顾,也给大家一个参考 目录 1 netty编写http服务器 1.1 主函数 1.2 ServerHandlerInit 1.3 服务端自定义BusiH ...

  3. netty websocket客户端_Websocket操作字节序 之 服务端

    Websocket在JavaScript中操作字节序 之 客户端 在上一篇文章中,把页面的websocket编码写好了,那么服务端又该如何实现呢?由于该文是在上上篇demo中修改的,所以不全的代码还请 ...

  4. 0729------Linux网络编程----------使用 select 、poll 和 epoll 模型 编写客户端程序

    1.select 模型 1.1 select 函数原型如下,其中 nfds 表示的描述符的最大值加1(因为这里是左闭右开区间),中间三个参数分别表示要监听的不同类型描述符的集合,timeout用来表示 ...

  5. Netty 快速开始(netty websocket客户端使用流程)

    文章目录 一.网络IO的基本知识与概念 1. 同步.异步.阻塞.非阻塞概念 2. IO模型 3. NIO和IO有什么区别? 4. Java NIO 工作流程 二.netty 1. 什么是netty? ...

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

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

  7. Netty工作笔记0022---NIO快速入门--编写客户端

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 客户端要比服务器端简单 这里要把这个socketchannel设置为非阻塞的.然后连接到服务器 然 ...

  8. 使用netty编写IM通信界面

    前驱知识 WebSocket 维基百科: WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补 ...

  9. Netty获取客户端IP

    Netty4.X 获取客户端IP 原创 laukinghaozi 2014-09-20 17:21:57 评论(0) 8514人阅读 最近使用netty-4.0.23.Final 版本编写服务端代码, ...

最新文章

  1. Java项目:角色权限后台脚手架系统(java+Springboot+Maven+myBaits-Plus+Vue+Element-UI+Mysql)
  2. Java数据结构和算法(十)——二叉树
  3. MySQL的原理以及流程_MySQL的复制原理以及流程?
  4. TensorFlow数据读取机制:文件队列 tf.train.slice_input_producer和 tf.data.Dataset机制
  5. mysqlworkbench导入sql文件_将sql脚本文件导入数据库
  6. 在CentOS上安装NodeJS_v14.x
  7. C/C++ stack栈的理解以及使用
  8. 为什么说微服务一定要有 API 网关?
  9. 关于armhf (hard-float ABI for ARM)
  10. 【Mac brew】代理安装brew insall
  11. linux cad转pdf文件怎么打开,PDF猫CAD转PDF官方版
  12. 最新最全的微信小程序入门学习教程,微信小程序零基础入门到精通
  13. 大数据与人工智能方向基础课程简单介绍
  14. 以太坊 私链 节点连接(window)
  15. 数据挖掘招聘关键字分析
  16. 100以内的奇数和偶数的和
  17. sequelize 设置mysql字段类型,text字符长度太小可采用longText
  18. 半监督学习:semi-supervised learning 直推学习:transductive learning 主动学习:active learning
  19. mysql水果销售系统数据库_mysql数据库水果销售系统
  20. Unity游戏开发客户端面经——算法(初级)

热门文章

  1. 计算机网络应用试卷,计算机网络应用基础试卷.doc
  2. html5 框架angularjs,5款最好用的AngularJS程序构建框架
  3. 线性数据结构的实现与应用_双端队列_逆波兰式_呼叫中心_XAUAT_(原问题自杜克大学Duke University cps110 C++ Stacks and Queues and Lists)
  4. 益和VA市场发展良好,产品稳定两增一减
  5. 工信部部长苗圩:今年我国部分地区将发放5G临时牌照
  6. 【docker】教程 常用命令 基础使用
  7. 代码(2):聚类图以及PCA图
  8. HBuilder无法运行到Chrome
  9. 手把手带你用Python完成一个能写进简历的项目(实战篇)
  10. 怎样启动python_如何运行python