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

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

时间:2025-01-05 16:54:54浏览次数:7  
标签:Netty String 编程 ctx Channel 聊天 new public channel

引言

这几天在学习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>() {
    						@Override
    						protected 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() {
    
    								@Override
    								public 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();
    									}
    
    								}
    
    								@Override
    								public 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>() {
    						@Override
    						protected 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> {
      	@Override
      	protected 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<>();
      	@Override
      	public void bind(Channel channel, String username) {
      		usernameChannelMap.put(username, channel);
      		channelUsernameMap.put(channel, username);
      		channelAttributesMap.put(channel, new ConcurrentHashMap<>());
      	}
      
      	@Override
      	public void unbind(Channel channel) {
      		String username = channelUsernameMap.remove(channel);
      		usernameChannelMap.remove(username);
      		channelAttributesMap.remove(channel);
      	}
      
      	@Override
      	public Object getAttribute(Channel channel, String name) {
      		return channelAttributesMap.get(channel).get(name);
      	}
      
      	@Override
      	public void setAttribute(Channel channel, String name, Object value) {
      		channelAttributesMap.get(channel).put(name, value);
      	}
      
      	@Override
      	public Channel getChannel(String username) {
      		return usernameChannelMap.get(username);
      	}
      
      	@Override
      	public String toString() {
      		return usernameChannelMap.toString();
      	}
      }
      
    • 发送消息
      客户端通过判断命令来发送对应消息的Request类,chatServer pipline再通过对应的SimpleChannelInboundHandler来处理对应的业务
      @ChannelHandler.Sharable
      public class ChatRequestMessageHandler extends SimpleChannelInboundHandler<ChatRequestMessage> {
      
      	@Override
      	protected 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

标签:Netty,String,编程,ctx,Channel,聊天,new,public,channel
From: https://www.cnblogs.com/MingHaiZ/p/18653442

相关文章

  • 《汇编程序语言》第11~15章
    第十一章输入输出程序设计键盘输入(BIOS中断调用);数据段定义DATASEGMENT;预留一个字节用于存储读取的字符INPUT_CHARDB?DATAENDS;代码段定义CODESEGMENTASSUMECS:CODE,DS:DATASTART:MOVAX,DATA;将数据段地址加载到AX寄存器......
  • 《汇编程序语言》第6~10章
    以下是对《汇编程序语言》中这几章内容的详细介绍:第六章循环结构程序一、循环结构概述概念:循环结构允许程序在满足特定条件时,重复执行一段代码。这在需要多次执行相同或相似操作的场景中非常有用,例如对数组的每个元素进行处理,或者进行多次迭代计算。通过循环结构,可以避......
  • 树莓派 Pico RP2040 教程点灯 双核编程案例
    双核点亮不同的LED示例,引脚分别是GP0跟GP1。#include"pico/stdlib.h"#include"pico/multicore.h"#defineLED10//核心0控制的LED引脚#defineLED21//核心1控制的LED引脚//thesetupfunctionrunsoncewhenyoupressresetorpowertheboardvo......
  • python期末考试必考40个基础编程题
            该40个基础编程涉及领域广泛,适合各专业,金融,数学,算法,数据结构,可视化等多个领域,涵盖了pyth基础语法,列表操作,字符串操作,数据类型转换,循环结构,判断结构,函数定义,变量声明,class类编程等基础知识。        运行代码无需环境配置,只要安装python成功,复制代码即......
  • java实验6 J.U.C并发编程
    实验6  J.U.C并发编程要求:1)严禁上网抄袭、互相抄袭和各种形式的抄袭(如代码抄袭,运行截图一图多用),一旦发现单次作业按零分处理!2)课程报告正文内容基本格式为:宋体,小五号,1.5倍行距。3)作业报告请务必保持排版的整洁规范,排版混乱者将直接判为不及格。4)为避免办公软件兼容性导......
  • 【C语言编程】地址与指针:理解与应用
    在C语言编程中,理解地址和指针的概念至关重要。地址是内存中可以唯一标识内存单元的数字,而指针则是存储这些地址的变量。通过指针,程序员可以直接访问和操作内存,这是C语言强大功能的基础。地址的概念在C语言中,每个变量都存储在内存中的一个特定位置,这个位置就是变量的地址。例如,一......
  • 【嵌入式编程】内存分布
    一、内存分布图在操作系统中,内存被组织和管理以支持进程的运行。以下是一些常见的内存分布概念:【内核空间】:操作系统内核使用的内存区域,用于存储内核代码、数据结构和进程控制块(PCB)。【用户空间】:存储用户的代码。未初始化变量区(.bss):存放未初始化的全局变量和静态变......
  • 重生之我在异世界学编程之数据结构与算法:深入数和二叉树篇
    大家好,这里是小编的博客频道小编的博客:就爱学编程很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!目录一、树的基本概念二、二叉树的基本概念三、在C语言中实现二叉树快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞......
  • 面向对象编程
    一、面向过程与面向对象面向过程(POP)与面向对象(OOP)面向对象:ObjectOrientedProgramming面向过程:ProcedureOrientedProgramming2.面向对象的三大特征封装(Encapsulation)继承(Inheritance)多态(Polymorphism)3.面向对象的思想概述程序员从面向过程的执行者转......
  • Python函数:编程的基石
    Python是一种非常灵活且强大的编程语言,其功能丰富,应用广泛。在Python的世界里,函数是构建程序的基本单元,它封装了一段代码,使其可以被重复调用和复用。本文将从函数的基本概念、定义、参数传递、返回值、作用域、高级特性等方面,深入探讨Python函数的奥秘,帮助读者掌握这一编程利器......