Netty实战 IM即时通讯系统(五)客户端启动流程
##
Netty实战 IM即时通讯系统(五)客户端启动流程
零、 目录
- IM系统简介
- Netty 简介
- Netty 环境配置
- 服务端启动流程
- 实战: 客户端和服务端双向通信
- 数据传输载体ByteBuf介绍
- 客户端与服务端通信协议编解码
- 实现客户端登录
- 实现客户端与服务端收发消息
- pipeline与channelHandler
- 构建客户端与服务端pipeline
- 拆包粘包理论与解决方案
- channelHandler的生命周期
- 使用channelHandler的热插拔实现客户端身份校验
- 客户端互聊原理与实现
- 群聊的发起与通知
- 群聊的成员管理(加入与退出,获取成员列表)
- 群聊消息的收发及Netty性能优化
- 心跳与空闲检测
- 总结
- 扩展
五、 客户端启动流程
客户端启动demo
/*** 客户端启动流程* */public class Test_05_客户端启动流程 {public static void main(String[] args) {NioEventLoopGroup workerGroup = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap// 指定线程模型.group(workerGroup)// 指定IO 模型.channel(NioSocketChannel.class)// 指定业务处理逻辑.handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {}});// 建立连接bootstrap.connect("127.0.0.1" , 8000).addListener(future ->{if(future.isSuccess()) {System.out.println("连接成功");}else {System.out.println("连接失败");}});}}
- 从上面的代码可以看出 , 客户端的启动引导类是BootStrap , 负责启动客户端以及连接服务端 , 而上面一小节我们在描述服务端启动的时候, 这个引导类是 ServerBootStrap , 引导类创建完成之后我们描述一下客户端启动流程:
- 首先和服务端的启动流程一样 , 我们需要给特指定线程模型, 驱动着连接的数据读写
- 然后我们指定IO 模型为 NioSocketChannel , 表示IO模型为 NIO
- 接着, 我们给引导类指定一个handler , 这里主要就是定义连接的业务处理逻辑 , 不理解没有关系 , 我们在后面会详解
- 配置完线程模型 , IO 模型 , 业务处理逻辑之后 , 调用connect() 方法进行连接 , 可以看到connect() 方法有两个参数 , 第一个参数可以填写IP或域名 ,第二个参数填写的是端口号 , 由于connect() 方法返回的是一个Future , 也就是说这个方法是异步的 , 我们通过addListener方法可以监听到连接是否成功 , 进而打印连接状态
- 到这里一个客户端的demo 就完成了 , 其实只要和客户端Socket 编程模型对应起来 , 这里的三个概念就会显得非常简单
- 从上面的代码可以看出 , 客户端的启动引导类是BootStrap , 负责启动客户端以及连接服务端 , 而上面一小节我们在描述服务端启动的时候, 这个引导类是 ServerBootStrap , 引导类创建完成之后我们描述一下客户端启动流程:
失败重连
在网络差的情况下 , 客户端第一次连接可能会失败 , 这个时候我们可能会尝试重新连接 , 重新连接的逻辑写在连接失败的逻辑块里
// 建立连接bootstrap.connect("127.0.0.1" , 8000).addListener(future ->{if(future.isSuccess()) {System.out.println("连接成功");}else {System.out.println("连接失败");//TODO: 重新连接逻辑}});
重新连接时依然是调用相同的逻辑 , 所以我们把连接的代码抽取出来, 实现代码复用 , 在连接失败的情况下使用递归的方法 实现重连
public static void connect(Bootstrap bootstrap, String IP, int port) {// 建立连接bootstrap.connect(IP, port).addListener(future -> {if (future.isSuccess()) {System.out.println("连接成功");} else {System.out.println("连接失败,执行重连");connect(bootstrap, IP, port);}});}
以上代码就实现了重连机制 , 但是在通常情况下连接失败不会立即重连 , 而是通过一个指数退避的方式 , 比如 每隔1秒、2秒、4秒、8秒 , 以2的次幂来实现建立连接 , 然后到达一定次数之后就放弃重连
connect(bootstrap , "127.0.0.1" , 8000 , 5);public static void connect(Bootstrap bootstrap, String IP, int port ,int maxRetry , int... retryIndex) {// 建立连接bootstrap.connect(IP, port).addListener(future -> {// 由于闭包特性 不能修改外部的变量 所有需要在闭包内定义一个相同的变量 拷贝外部变量的值int[] finalRetryIndex ;if (future.isSuccess()) {System.out.println("连接成功");} else if(maxRetry == 0){System.out.println("到达重试最大次数,放弃重连");}else {// 初始化 重试计数if(retryIndex.length == 0) {finalRetryIndex = new int[] {0};}else {finalRetryIndex = retryIndex;}//计算时间间隔int delay = 1 << finalRetryIndex[0];// 执行重试System.out.println(new Date()+"连接失败,剩余重连次数:"+maxRetry+","+delay+"秒后执行第"+(finalRetryIndex[0]+1)+"次重连...");bootstrap.config().group().schedule(()->{connect(bootstrap, IP, port , maxRetry-1 , finalRetryIndex[0]+1);}, delay, TimeUnit.SECONDS);}});}执行结果:Thu Dec 27 11:04:19 CST 2018连接失败,剩余重连次数:5,1秒后执行第1次重连...Thu Dec 27 11:04:21 CST 2018连接失败,剩余重连次数:4,2秒后执行第2次重连...Thu Dec 27 11:04:24 CST 2018连接失败,剩余重连次数:3,4秒后执行第3次重连...Thu Dec 27 11:04:29 CST 2018连接失败,剩余重连次数:2,8秒后执行第4次重连...Thu Dec 27 11:04:38 CST 2018连接失败,剩余重连次数:1,16秒后执行第5次重连...到达重试最大次数,放弃重连
- 从上面的代码中我们可以看到 , 通过判断是否连接成功以及剩余重试次数 , 分别执行不同的逻辑
- 如果连接成功则打印连接成功的消息
- 如果连接失败 , 但是重试次数已经用完则放弃连接
- 如果连接失败 , 但是连接没有用完则计算下一次重试时间间隔 , 然后定时重连
- 从上面代码中我们可以看到 , 定时任务是调用bootstrap.config().group().schedule() , 其中bootStrap.config()这个方法返回的是BootStrapConfig , 他是对BootStrap 参数配置的抽象 , 然后ootstrap.config().group() 返回的就是我们一开始设置的线程模型workerGroup , 最后调用schedule() 方法就可以实现定时任务逻辑了。
- 从上面的代码中我们可以看到 , 通过判断是否连接成功以及剩余重试次数 , 分别执行不同的逻辑
客户端启动其他方法
attr(): attr()方法可以给客户端channel也就是NioSocketChannel绑定自定义属性 , 然后我们通过channel.attr()取出属性。 说白了就是给NioSocketChannel维护一个Map 而已
//设置属性bootstrap.attr(AttributeKey.newInstance("clientName"), "NettyClient");//业务逻辑bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {// 取出属性Attribute<Object> attr = ch.attr(AttributeKey.valueOf("clientName"));System.out.println("客户端名称:"+attr.get());}});
option(): option()可以给连接设置一些TCP底层的相关属性 : (ChannelOption相关参数详解在 上一节《服务端启动流程》中有连接地址)
// 设置TCP 相关的属性// 设置连接超时时间bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);// 开启TCP 心跳机制bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
总结:
- 在本节中我们学习了Netty客户端启动的流程 , 一句话来说就是: 创建一个引导类 , 然后给他指定线程 , IO模型 , 指定业务逻辑 ,连接特定的IP:port 客户端就启动起来了
- 然后我们学习到 connect()方法时异步的 , 我们可以通过异步回调机制来实现指数退避重连机制。
- 最后我们讨论了Netty客户端启动的额外参数 , 只要包括给客户端Channel 绑定自定义属性 , 设置TCP底层参数。
疑问:
- 客户端Channel设置的attr是否会被服务端接收到,并且以此进行必要的参数传递?
- 答: 客户端Channel 会被服务端接收到。
- 客户端Channel设置的attr是否会被服务端接收到,并且以此进行必要的参数传递?
Netty实战 IM即时通讯系统(五)客户端启动流程相关推荐
- Netty实战 IM即时通讯系统(十二)构建客户端与服务端pipeline
Netty实战 IM即时通讯系统(十二)构建客户端与服务端pipeline 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向 ...
- Netty实战 IM即时通讯系统(十)实现客户端和服务端收发消息
Netty实战 IM即时通讯系统(十)实现客户端和服务端收发消息 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数据 ...
- Netty实战 IM即时通讯系统(九)实现客户端登录
## Netty实战 IM即时通讯系统(九)实现客户端登录 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数据传输载 ...
- Netty实战 IM即时通讯系统(八)服务端和客户端通信协议编解码
Netty实战 IM即时通讯系统(八)服务端和客户端通信协议编解码 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数 ...
- Netty实战 IM即时通讯系统(六)实战: 客户端和服务端双向通信
## Netty实战 IM即时通讯系统(六)实战: 客户端和服务端双向通信 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 实战: 客户端和服务端双向通信 数据传输载 ...
- Netty实战 IM即时通讯系统(十一)pipeline与channelHandler
Netty实战 IM即时通讯系统(十一)pipeline与channelHandler 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端 ...
- Netty实战 IM即时通讯系统(七)数据传输载体ByteBuf介绍
## Netty实战 IM即时通讯系统(七)数据传输载体ByteBuf介绍 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向 ...
- Netty实战 IM即时通讯系统(四)服务端启动流程
## Netty实战 IM即时通讯系统(四)服务端启动流程 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 实战: 客户端和服务端双向通信 数据传输载体ByteBuf ...
- Netty实战 IM即时通讯系统(三)Netty环境配置
## Netty实战 IM即时通讯系统(三)Netty环境配置 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 实战: 客户端和服务端双向通信 数据传输载体ByteB ...
最新文章
- matplotlib cmap
- 高斯旋转热源与双椭球热源_电力分公司:多措并举保证供暖质量效益双提升
- 如何创建和维护你自己的man手册
- 第一集 企业IT体系结构
- mysql导入本地sql脚本的两种方式
- h3c s7506e 配置手册_H3C交换机s5500Web登录配置
- primefaces_PrimeFaces扩展中的全新JSF组件
- 串口UART串行总线协议
- SinGAN: Learning a Generative Model from a Single Natural Image
- ajax 文件上传,ajax
- JAVA“类”数组的创建与调用
- data标签怎么爬虫_scrapy爬虫笔记(1):scrapy基本使用
- fp算法例题_机器学习-FPGROWTH算法.pptx
- Devcpp使用技巧
- Scala中的集合排序
- 解决电脑不能进入BIOS页面
- 英文电影经典台词整理(原创)
- html载入3d模型,webGL3D模型的加载与使用
- Java窗体图书管理系统Java图书借阅管理系统(图书借阅系统)
- 【SpringBoot】人工更新SpringBoot项目,Jar包太大问题解决,SpringBoot瘦包
热门文章
- [na]tcpdump参数应用参考
- PHP学习:文件操作
- 每日程序C语言6-判断某范围之间的素数
- 判断是否是数组的方法
- Java黑皮书课后题第1章:1.7(求π的近似值)编写程序,显示4*(1-1/3+1/5-1/7+1/9-1/11【+1/13】)
- java bufferedinputstream 编码_java中关于编码的问题(字符转换流及字符缓冲流 )
- getchar、scanf以及缓冲区的概念
- PHP 使用程序进行数据库字典文件生成 导出数据库字典
- 年报统计系统—基本信息模块的目标文档
- 如何把微信文章中的语音/音乐下载下来