客户端 Client 登录和响应处理

疯狂创客圈 Java 分布式聊天室【 亿级流量】实战系列之 17【 博客园 总入口 】


文章目录

  • 客户端 Client 登录和响应处理
    • 写在前面
    • 客户端的会话管理
    • 客户端的逻辑构成
    • 连接服务器与Session 的创建
    • Session和 channel 相互绑定
    • AttributeMap接口的使用
    • 客户端登录请求
    • 处理登录成功的响应
    • 写在最后
    • 疯狂创客圈 Java 死磕系列

源码IDEA工程获取链接: Java 聊天室 实战 源码

写在前面

​ 大家好,我是作者尼恩。

​ 前面,已经完成一个高性能的 Java 聊天程序的四件大事:

  1. 完成了协议选型,选择了性能更佳的 Protobuf协议。具体的文章为: Netty+Protobuf 整合一:实战案例,带源码

  2. 介绍了 通讯消息数据包的几条设计准则。具体的文章为: Netty +Protobuf 整合二:protobuf 消息通讯协议设计的几个准则

  3. 解决了一个非常基础的问题,这就是通讯的 **粘包和半包问题。**具体的文章为:Netty 粘包/半包 全解 | 史上最全解读

  4. 前一篇文件,已经完成了 系统三大组成模块的组成介绍。 具体的文章为:Netty聊天程序(实战一):从0开始实战100w级流量应用

    今天介绍非常重要的一个内容:

    客户端的通讯、登录请求和登录响应设计

    下面,开启今天的 惊险和刺激实战之旅

客户端的会话管理

​ 什么是会话?

​ 为了方便客户端的开发,管理与服务器的连接,这里引入一个非常重要的中间角色——Session (会话)。有点儿像Web开发中的Tomcat的服务器 Session,但是又有很大的不同。

​ 客户端的会话概念图,如下图所示:

​ 客户端会话有两个很重的成员,一个是user,代表了拥有会话的用户。一个是channel,代表了连接的通道。两个成员的作用是:

  • 通过user,可以获得当前的用户信息

  • 通过channel,可以向服务器发送消息

    所以,会话左拥右抱,左手用户资料,右手服务器的连接。在本例的开发中,会经常用到。

客户端的逻辑构成

从逻辑上来说,客户端有三个子的功能模块。

模块一:Handler

入站处理器。

在Netty 中非常重要,负责处理入站消息。比方,服务器发送过来登录响应,服务器发送过来的聊天消息。

模块二:MsgBuilder

消息组装器。

将 Java 内部的 消息 Bean 对象,转成发送出去的 Protobuf 消息。

模块三:Sender

消息发送器。

Handler 负责收的工作。Sender 则是负责将消息发送出去。

三大子模块的类关系图:

介绍完成了主要的组成部分后,开始服务器的连接和Session 的创建。

连接服务器与Session 的创建

​ 通过bootstrap 帮助类,设置完成线程组、通道类型,向管道流水线加入处理器Handler后,就可以开始连接服务器的工作。

​ 本小节需要重点介绍的,是连接成功之后,创建 Session,并且将 Session和 channel 相互绑定。

​ 代码如下:

package com.crazymakercircle.chat.client;
//...
@Data
@Service("EchoClient")
public class ChatClient
{static final Logger LOGGER =LoggerFactory.getLogger(ChatClient.class);//..private Channel channel;private ClientSender sender;public void doConnect(Bootstrap bootstrap, EventLoopGroup eventLoopGroup){ChannelFuture f = null;try{if (bootstrap != null){bootstrap.group(eventLoopGroup);bootstrap.channel(NioSocketChannel.class);bootstrap.option(ChannelOption.SO_KEEPALIVE, true);bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);bootstrap.remoteAddress(host, port);// 设置通道初始化bootstrap.handler(new ChannelInitializer<SocketChannel>(){public void initChannel(SocketChannel ch) throws Exception{ch.pipeline().addLast(new ProtobufDecoder());ch.pipeline().addLast(new ProtobufEncoder());ch.pipeline().addLast(chatClientHandler);}});LOGGER.info(new Date() + "客户端开始登录[疯狂创客圈IM]");f = bootstrap.connect().addListener((ChannelFuture futureListener) ->{final EventLoop eventLoop = futureListener.channel().eventLoop();if (!futureListener.isSuccess()){LOGGER.info("与服务端断开连接!在10s之后准备尝试重连!");eventLoop.schedule(() -> doConnect(new Bootstrap(), eventLoop), 10, TimeUnit.SECONDS);initFalg = false;}else{initFalg = true;}if (initFalg){LOGGER.info("EchoClient客户端连接成功!");LOGGER.info(new Date() + ": 连接成功,启动控制台线程……");channel = futureListener.channel();// 创建会话ClientSession session = new ClientSession(channel);channel.attr(ClientSession.SESSION).set(session);session.setUser(ChatClient.this.getUser());startConsoleThread();}});// 阻塞f.channel().closeFuture().sync();}} catch (Exception e){LOGGER.info("客户端连接失败!" + e.getMessage());}}//...}

Session和 channel 相互绑定

Session和 channel 相互绑定,再截取出来,分析一下。

ClientSession session = new ClientSession(channel);
channel.attr(ClientSession.SESSION).set(session);
session.setUser(ChatClient.this.getUser());

​ 为什么要Session和 channel 相互绑定呢?

  • 发的时候, 需要从Session 写入 Channel ,这相当于正向的绑定。
  • 收的时候,是从Channel 过来的,需要找到 Session ,这相当于反向的绑定。

​ Netty 中的 channel ,实现了AttributeMap接口 ,相当于一个 Map容器。 反向的绑定,利用了channel 的这个特点。

​ 看一下AttributeMap接口 如何使用的?

AttributeMap接口的使用

​ AttributeMap 是一个接口,并且只有一个attr()方法,接收一个AttributeKey类型的key,返回一个Attribute类型的value。按照Javadoc,AttributeMap实现必须是线程安全的。

​ AttributeMap内部结构看起来像下面这样:

不要被吓着了,其实很简单。

AttributeMap 的使用,主要是设置和取值。

  • 设值 Key-> Value

AttributeMap 的设值的方法,举例如下:

channel.attr(ClientSession.SESSION).set(session);

这个是链式调用,attr() 方法中的是 Key, set()方法中的是Value。 这样就完成了 Key-> Value 的设置。

  • 取值

AttributeMap 的取值的方法,举例如下:

ClientSession session =ctx.channel().attr(ClientSession.SESSION).get();

这个是链式调用,attr() 方法中的是 Key, get()方法返回 的是Value。 这样就完成了 取值。

关键是,这个key比较特殊

一般的Map,Key 的类型多半为字符串。但是这里的Key不行,有特殊的约定。

Key的类型必须是 AttributeKey 类型,而且这是一个泛型类,它的优势是,不需要对值进行强制的类型转换。

Key的例子如下:

public static final AttributeKey<ClientSession> SESSION = AttributeKey.valueOf("session");

客户端登录请求

登录的请求,大致如下:

ClientSender的 代码如下:

package com.crazymakercircle.chat.client;@Service("ClientSender")
public class ClientSender
{static final Logger LOGGER = LoggerFactory.getLogger(ClientSender.class);private User user;private ClientSession session;public void sendLoginMsg(){LOGGER.info("开始登陆");ProtoMsg.Message message = LoginMsgBuilder.buildLoginMsg(user);session.writeAndFlush(message);}//...public boolean isLogin(){return  session.isLogin();}
}

Sender 首先通过 LoginMsgBuilder,构造一个protobuf 消息。然后调用session发送消息。

session 会通过绑定的channel ,将消息发送出去。

session的代码,如下:

public synchronized void writeAndFlush(Object pkg)
{channel.writeAndFlush(pkg);
}

其他的客户端请求流程,大致也是类似的。

一个客户端的请求大致的流程有三步,分别从Sender 到session到channel。

处理登录成功的响应

​ 这是从服务器过来的入站消息。 如果登录成功,服务器会发送一个登录成功的响应过来。 这个响应,会从channel 传递到Handler。

处理器 LoginResponceHandler 的代码如下:

package com.crazymakercircle.chat.clientHandler;//...public class LoginResponceHandler extends ChannelInboundHandlerAdapter
{static final Logger LOGGER = LoggerFactory.getLogger(LoginResponceHandler.class);/*** 业务逻辑处理*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{LOGGER.info("msg:{}", msg.toString());if (msg != null && msg instanceof ProtoMsg.Message){ProtoMsg.Message pkg = (ProtoMsg.Message) msg;ProtoMsg.LoginResponse info = pkg.getLoginResponse();ProtoInstant.ResultCodeEnum result =ProtoInstant.ResultCodeEnum.values()[info.getCode()];if (result.equals(ProtoInstant.ResultCodeEnum.SUCCESS)){ClientSession session =ctx.channel().attr(ClientSession.SESSION).get();session.setLogin(true);LOGGER.info("登录成功");}}}}

​ LoginResponceHandler 对消息类型进行判断,如果是请求响应消息,并且登录成功。 则取出绑定的session,通过session,进一步完成登录成功后的业务处理。

​ 比如设置成功的状态,完成一些成功的善后处理操作等等。

​ 其他的客户端响应处理流程,大致也是类似的。

写在最后

​ 至此为止,可以看到,客户端登录的完整流程。

​ 下一篇:服务器的请求处理和通讯的全流程闭环介绍。


疯狂创客圈 Java 死磕系列

  • Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
  • Netty 源码、原理、JAVA NIO 原理
  • Java 面试题 一网打尽
  • 疯狂创客圈 【 博客园 总入口 】

Netty聊天程序(2):从0开始实战100w级流量应用 - 图解Netty系列相关推荐

  1. Netty聊天室(2):从0开始实战100w级流量应用

    目录 客户端 Client 登录和响应处理 写在前面 客户端的会话管理 客户端的逻辑构成 连接服务器与Session 的创建 Session和 channel 相互绑定 AttributeMap接口的 ...

  2. java实战案例聊天系统_Netty聊天器(实战一):从0开始实战100w级流量应用

    Java 聊天程序(百万级流量实战一):系统介绍疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之14 [博客园 总入口] 写在前面 ​ 大家好,我是作者尼恩. ​ 前面,已经完成一个高性能的 ...

  3. Netty实战项目:Spring boot 程序的聊天程序

    1.背景 主要是想使用这个聊天程序,做netty的实战项目,最终实现和钉钉或者QQ相似的功能,或者是锻炼一下自己的netty知识. 希望看到的一起加入一起完善. 2.说明 2.1 git地址 仓库地址 ...

  4. 微信小程序与Netty实现的WebSocket聊天程序

    一.微信小程序实现WebSocket客户端程序 1. 界面实现 <input name="url" value="{{url}}" bindinput = ...

  5. net core体系-web应用程序-4asp.net core2.0 项目实战(1)-11项目日志解决方案

    本文目录 1. Net下日志记录 2. NLog的使用     2.1 添加nuget引用NLog.Web.AspNetCore     2.2 配置文件设置     2.3 依赖配置及调用     ...

  6. 《Netty 实战》Netty In Action中文版 第2章——你的第一款Netty应用程序(一)

    第2章 你的第一款Netty应用程序 本章主要内容 设置开发环境 编写Echo服务器和客户端 构建并测试应用程序 在本章中,我们将展示如何构建一个基于Netty的客户端和服务器.应用程序很简单:客户端 ...

  7. net core体系-web应用程序-4asp.net core2.0 项目实战(1)-10项目各种全局帮助类

    本文目录 1.  前沿 2.CacheHelper基于Microsoft.Extensions.Caching.Memory封装 3.XmlHelper快速操作xml文档 4.Serializatio ...

  8. Netty(一)——Netty入门程序

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7447618.html 有兴趣的可先了解下:4种I/O的对比与选型 主要内容包括: Netty开发环境的搭建 ...

  9. java netty聊天室_java架构之路-(netty专题)netty的基本使用和netty聊天室

    上次回顾: 上次博客,我们主要说了我们的IO模型,BIO同步阻塞,NIO同步非阻塞,AIO基于NIO二次封装的异步非阻塞,最重要的就是我们的NIO,脑海中应该有NIO的模型图. Netty概念: Ne ...

最新文章

  1. [20180606]如何dump数据库里面的汉字.txt
  2. pyqt5学习(四)事件和信号
  3. ORA-01555 snapshot too old
  4. Python----面向对象---主动触发异常-raise
  5. 深度学习会不会被取代?深度学习必看发展史
  6. 工作278:控制数据从字典表获取
  7. 如果财富都在部分人手里会怎样?
  8. python调用扬声器、摄像头
  9. 【软件测试】美团一面、阿里一面复盘总结
  10. 2020年六十款数据分析的可视化工具推荐
  11. 深度学习模型---稀疏编码 Sparse Coding
  12. .netcore3 下Signalr 关于Joson序列化后对象属性变小写的问题
  13. 安装系统之九 U盘装原版WIN8教程
  14. CSMA/CD和CSMA/CA
  15. java准确读取word文件页数
  16. Scratch(三十一):掌握打字技巧
  17. Android视图绑定,设置控件点击事件不生效
  18. 使用Mysql函数生成指定的自增序列号
  19. 13.创建活动、布局、活动关联布局、注册活动
  20. hdu 5238 Calculator

热门文章

  1. 毕业设计 单片机stm32厨房环境检测系统 - 物联网 嵌入式
  2. 简单Python画折线图
  3. mysql 架构 ~ binlog_server
  4. 流媒体开发之-新浪网NBA赛程解析
  5. 计算机硬件系统和操作系统
  6. 网络入侵检测IDS常用数据集KDD Cup99/NSL-KDD/UNSW-NB15/ADFA/CIC IDS2017/2018下载途径
  7. Python实现恩尼格玛加密算法——附完整源码
  8. Quill富文本的使用以及自定义图片和视频处理事件
  9. 【植物大战僵尸-3】种植物无CD
  10. 汽车动力学与控制-轮胎侧偏与车辆动力学方程