[ Netty ] 通过Netty聊天业务来加深理解Netty运行以及网络编程.

news/2025/1/7 8:10:09/文章来源:https://www.cnblogs.com/MingHaiZ/p/18653442

引言

这几天在学习Netty网络编程的过程当中对Netty的运作原理及流程有一定的了解,通过Netty实现聊天业务来加深对Netty的理解.这里用一张图概括运行流程

这里我在Github上面找到一位大神总结的尚硅谷的学习笔记,里面有写Netty的运作原理(但是因为前面一直在讲原理我自己身原因容易听不进去所以先去看的黑马的Netty直接从代码层面开始学习,运行原理倒是听尚硅谷的讲解,将挺好的)
Netty学习手册
image
这张图基本就讲解了Netty运行的流程,bossGroup负责建立连接,workerGroup负责处理业务,而NioEventLoopGroup负责IO业务还能传给DefaultEventLoopGroup来提高并发,这里就不过多赘述了.

聊天业务

前期准备工作其实不是很值得讲,大概原理基本都清除所以这里就不讲了直接跳到我学习过程中需要思考理解一会的地方记录

单聊业务

  • 相关代码

    客户端
    public class ChatClient {public static void main(String[] args) {NioEventLoopGroup group = new NioEventLoopGroup();LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);MessageCodecSharable MESSAGECODEC_SHARABLE = new MessageCodecSharable();CountDownLatch WAIT_FOR_LOGIN = new CountDownLatch(1);AtomicBoolean LOGIN = new AtomicBoolean(false);try {Channel channel = new Bootstrap().group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new ProcotolFrameDecoder());
    //                            ch.pipeline().addLast(LOGGING_HANDLER);ch.pipeline().addLast(MESSAGECODEC_SHARABLE);ch.pipeline().addLast("clientHandler", new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {log.info("message: {}", msg);if (msg instanceof LoginResponseMessage) {LoginResponseMessage response = (LoginResponseMessage) msg;if (response.isSuccess()) {LOGIN.set(true);}WAIT_FOR_LOGIN.countDown();}}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {
    //                                    在连接建立后触发 active 事件
    //                                    负责用户在控制台的输入,负责向服务器发送各种消息new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("请输入用户名: ");String userName = scanner.nextLine();System.out.println("请输入密码: ");String password = scanner.nextLine();
    //                                        构造消息对象LoginRequestMessage loginRequestMessage = new LoginRequestMessage(userName, password);ctx.writeAndFlush(loginRequestMessage);System.out.println("wait...");try {WAIT_FOR_LOGIN.await();} catch (InterruptedException e) {throw new RuntimeException(e);}if (!LOGIN.get()) {ctx.channel().close();return;}while (true) {System.out.println("======================================");System.out.println("send [userName] [content]");System.out.println("gsend [group name] [content]");System.out.println("gcreate [group name] [m1,m2,m3...]");System.out.println("gmembers [group name]");System.out.println("gjoin [group name]");System.out.println("gquit [group name]");System.out.println("quit");System.out.println("======================================");String command = scanner.nextLine();String[] split = command.split(" ");switch (split[0]) {case "send":ctx.writeAndFlush(new ChatRequestMessage(userName, split[1], split[2]));break;case "gsend":ctx.writeAndFlush(new GroupChatRequestMessage(userName, split[1], split[2]));break;case "gcreate":Set<String> collect = Arrays.stream(split[2].split(",")).collect(Collectors.toSet());ctx.writeAndFlush(new GroupCreateRequestMessage(split[1], collect));break;case "gmembers":ctx.writeAndFlush(new GroupMembersRequestMessage(split[1]));break;case "gjoin":ctx.writeAndFlush(new GroupJoinRequestMessage(split[1], userName));break;case "gquit":ctx.writeAndFlush(new GroupQuitRequestMessage(split[1], userName));break;case "quit":ctx.channel().close();break;}}}, "System in").start();}});}}).connect("localhost", 8080).sync().channel();channel.closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {group.shutdownGracefully();}}
    }
    
    服务端
    public class ChatServer {public static void main(String[] args) {NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup();LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);MessageCodecSharable MESSAGECODEC_SHARABLE = new MessageCodecSharable();LoginRequestMessageHandler LOGIN_HANDLER = new LoginRequestMessageHandler();ChatRequestMessageHandler CHAT_HANDLER = new ChatRequestMessageHandler();try {Channel channel = new ServerBootstrap().group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new ProcotolFrameDecoder());ch.pipeline().addLast(LOGGING_HANDLER);ch.pipeline().addLast(MESSAGECODEC_SHARABLE);ch.pipeline().addLast(LOGIN_HANDLER);ch.pipeline().addLast(CHAT_HANDLER);}}).bind(8080).sync().channel();channel.closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}}
    
  • 业务流程
    在发送消息之前,先登录服务器,这里通过简易的一个Map来当数据库校验存储,在登录成功过后将userName绑定到服务器的Session当中

    • 登录业务
      @ChannelHandler.Sharable
      public class LoginRequestMessageHandler extends SimpleChannelInboundHandler<LoginRequestMessage> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, LoginRequestMessage msg) throws Exception {String username = msg.getUsername();String password = msg.getPassword();boolean login = UserServiceFactory.getUserService().login(username, password);LoginResponseMessage message;if (login) {SessionFactory.getSession().bind(ctx.channel(), username);message = new LoginResponseMessage(true, "登录成功");} else {message = new LoginResponseMessage(false, "登录失败,账号或密码错误");}ctx.writeAndFlush(message);}
      }
      
      • Session
      public class SessionMemoryImpl implements Session {private final Map<String, Channel> usernameChannelMap = new ConcurrentHashMap<>();private final Map<Channel, String> channelUsernameMap = new ConcurrentHashMap<>();private final Map<Channel,Map<String,Object>> channelAttributesMap = new ConcurrentHashMap<>();@Overridepublic void bind(Channel channel, String username) {usernameChannelMap.put(username, channel);channelUsernameMap.put(channel, username);channelAttributesMap.put(channel, new ConcurrentHashMap<>());}@Overridepublic void unbind(Channel channel) {String username = channelUsernameMap.remove(channel);usernameChannelMap.remove(username);channelAttributesMap.remove(channel);}@Overridepublic Object getAttribute(Channel channel, String name) {return channelAttributesMap.get(channel).get(name);}@Overridepublic void setAttribute(Channel channel, String name, Object value) {channelAttributesMap.get(channel).put(name, value);}@Overridepublic Channel getChannel(String username) {return usernameChannelMap.get(username);}@Overridepublic String toString() {return usernameChannelMap.toString();}
      }
      
    • 发送消息
      客户端通过判断命令来发送对应消息的Request类,chatServer pipline再通过对应的SimpleChannelInboundHandler来处理对应的业务
      @ChannelHandler.Sharable
      public class ChatRequestMessageHandler extends SimpleChannelInboundHandler<ChatRequestMessage> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, ChatRequestMessage msg) throws Exception {String to = msg.getTo();Channel channel = SessionFactory.getSession().getChannel(to);if (Objects.nonNull(channel)) {channel.writeAndFlush(new ChatResponseMessage(msg.getFrom(), msg.getContent()));} else {ctx.writeAndFlush(new ChatResponseMessage(false, "发送失败: 对方用户不在线"));}}
      }
      
      这里的一个流程就是用户在启动client的时候在服务器bossworker建立连接,在workerGroup上处理登录和发送消息的业务,发送消息就是将消息发送给对应的SimpleChannelInboundHandler来处理对应的事件,随后再通过Session获取对应user绑定的channel,对应的channel最后再writeandflush给client

      在 Netty 中,childHandler 里的 ChannelInitializer 中,channel 指定的是之前在 .channel(NioSocketChannel.class) 中定义的 Channel 类型。通过这个 Channel,服务器与客户端之间建立连接并进行通信。
      接下来,在 Server 类中创建一个 Session 类,其中包含一个 Map 来存储每个客户端连接的 Channel,并通过 username 来索引和获取对应的 Channel。这样,服务器可以通过 username 获取到指定的 Channel,实现与特定客户端的通信。
      当客户端断开连接时,Map 中存储的 Channel 会被移除。因此,当尝试根据 username 从 Map 获取 Channel 时,如果客户端已经断开连接,Map 会返回 null,表示该连接已失效。
      image
      之后的有什么值得记录的再写吧....其实这个也不是很难只是我捋清楚了想写下来加深印象,到时候还可以自己看看思考过程XD

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

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

相关文章

输出下列图形

题目很简单但也算是复习知识点,有三种做法,第一种是直接printf,第二种是循环语句,第三种是调用函数。printf省略 for循环: ① ②调用函数:

动手深度学习-PyTorch(第二版)PDF、EPUB免费下载

李沐,动手深度学习,中文第二版,2023年8月18日。本书是《动手学深度学习》的重磅升级版本,选用经典的PyTorch深度学习框架,旨在向读者交付更为便捷的有关深度学习的交互式学习体验。 本书重新修订《动手学深度学习》的所有内容,并针对技术的发展,新增注意力机制、预训练等…

Python数据结构与算法分析 第3版PDF、EPUB免费下载

只有洞彻数据结构与算法,才能真正精通Python!热门计算机科学教材,华盛顿大学、北京大学等多家高校采用,让你在代码编写的战场上所向披靡!适读人群 :1. 希望学习数据结构和算法的Python用户; 2. 计算机专业的学生和老师。 只有洞彻数据结构与算法,才能真正精通Python!热…

《计算机组成及汇编语言原理》读后感

一、 为什么选择这本书? 这本书来自于:https://github.com/codefollower/My-Blog/issues/1。工作之后,个人一直追求的一个方面就是“系统化”,笼统的来说就是从以下两方面进行系统化:“基础”+“专业”。这本书属于基础方面的深化,通过 Java 虚拟机来讲计算机组成。 二、…

Effective Java中文版(原书第3版)PDF、EPUB免费下载

Jolt获奖作品全新升级,与《Java编程思想》和《Java核心技术》齐名,针对Java7、8、9全面更新Effective Java中文版(原书第3版)PDF、EPUB免费下载 适读人群 :本书并非面向Java初学者,而是要求读者有一定的Java编程经验。对于在Java开发方面已经积累一定经验的读者而言,本书…

2024-2025-1 20241301 《计算机基础与程序设计》第十五周学习总结

|这个作业属于哪个课程|2024-2025-1-计算机基础与程序设计| |这个作业要求在哪里|2024-2025-1计算机基础与程序设计第一周作业| |这个作业的目标|<复习知识,巩固基础>| |作业正文|https://www.cnblogs.com/HonJo/p/18653443| 一、教材学习内容 (一)《计算机科学概论》…

剖析Vue2内部运行机制

Vue.js 运行机制全局概览 全局概览 这一节笔者将为大家介绍一下 Vue.js 内部的整个流程,希望能让大家对全局有一个整体的印象,然后我们再来逐个模块进行讲解。从来没有了解过 Vue.js 实现的同学可能会对一些内容感到疑惑,这是很正常的,这一节的目的主要是为了让大家对整个流…

C++前缀和

有一个数组{2,1,3,6,4},询问三次结果: a[5] = {2, 1, 3, 6, 4} 1.数组第1到第2个元素的和是多少? 2. 数组第1到第3个元素的和是多少? 3. 数组第2到第4个元素的和是多少?没有用前缀和的原始用法:1 #include<iostream>2 #include<stdio.h>3 using namespace st…

Verilog6_串行通信协议

Verilog内容总结6_串行通信协议一、通信协议概述 1.串行通信与并行通信串行通信(serial communication):数据通过单根数据线一位一位地传输;成本低但速度慢;适用于远距离传输,用于计算机与外设之间,如UART、\(I^2C\)、SPI并行通信(parallel communication):通过多根数…

二进制码与格雷码之间的转换

二进制码到格雷码的转换 (1)格雷码的最高位(最左边)与二进制码的最高位相同。 (2)从左到右,逐一将二进制码相邻的两位相加(舍去进位),作为格雷码的下一位。格雷码到二进制码的转换 (1)二进制码的最高位(最左边)与格雷码的最高位相同。 (2)将产生的每一位二进制…

20241407《计算机基础与程序设计》课程总结

每周作业链接汇总 第0周作业: 自我介绍第一周作业: 1.基于VirtualBox虚拟机安装Ubuntu和安装Linux系统 2.快速浏览一遍教材计算机科学概论(第七版),课本每章提出至少一个自己不懂的或最想解决的问题第二周作业: 数字化 信息安全 *自学教材:计算机科学概论(第七版)第1章…

运算放大器在运算方面的应用

集成运算放大器与外部电阻、电容、半导体器件等构成闭环电路后,能对各种模拟信号进行比例、加法、减法、微分、积分、对数、反对数、乘法和除法等运算。运算放太器工作在线性区时,通常要引入深度负反馈。所以,它的输出电压和输入电压的关系基本决定于反馈电路和输入电路的结…