当前位置: 首页 > news >正文

Netty高级应用之:编解码器与群聊天室开发

Netty高级应用之:编解码器与群聊天室开发

文章目录

  • Netty高级应用之:编解码器与群聊天室开发
    • Netty编解码器
      • Java的编解码
      • Netty编解码器
        • 概念
        • 解码器(Decoder)
        • 编码器(Encoder)
        • 编码解码器Codec
    • Netty案例-群聊天室
      • 聊天室服务端编写
      • 聊天室客户端编写

Netty编解码器

Java的编解码

  1. 编码(Encode)称为序列化, 它将对象序列化为字节数组,用于网络传输、数据持久化或者其它用途。
  2. 解码(Decode)称为反序列化,它把从网络、磁盘等读取的字节数组还原成原始对象(通常是原始对象的拷贝),以方便后续的业务逻辑操作。

在这里插入图片描述

java序列化对象只需要实现java.io.Serializable接口并生成序列化ID,这个类就能够通过java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。

Java序列化目的:1.网络传输。2.对象持久化。

Java序列化缺点:1.无法跨语言。 2.序列化后码流太大。3.序列化性能太低

Java序列化仅仅是Java编解码技术的一种,由于它的种种缺陷,衍生出了多种编解码技术和框架,这些编解码框架实现消息的高效序列化。

Netty编解码器

概念

在网络应用中需要实现某种编解码器,将原始字节数据与自定义的消息对象进行互相转换网络中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码。

对于Netty而言,编解码器由两部分组成:编码器、解码器。

  • 解码器:负责将消息从字节或其他序列形式转成指定的消息对象。
  • 编码器:将消息对象转成字节或其他序列形式在网络上传输。

Netty 的编(解)码器实现了 ChannelHandlerAdapter,也是一种特殊的 ChannelHandler,所以依赖于 ChannelPipeline,可以将多个编(解)码器链接在一起,以实现复杂的转换逻辑。

Netty里面的编解码:

  • 解码器:负责处理“入站 InboundHandler”数据。
  • 编码器:负责“出站OutboundHandler” 数据。

解码器(Decoder)

解码器负责解码“入站”数据从一种格式到另一种格式,解码器处理入站数据是抽象ChannelInboundHandler的实现。需要将解码器放在ChannelPipeline中。对于解码器,Netty中主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder

在这里插入图片描述

抽象解码器

  • ByteToMessageDecoder: 用于将字节转为消息,需要检查缓冲区是否有足够的字节
  • ReplayingDecoder: 继承ByteToMessageDecoder,不需要检查缓冲区是否有足够的字节,但是 ReplayingDecoder速度略慢于ByteToMessageDecoder,同时不是所有的ByteBuf都支持。项目复杂性高则使用ReplayingDecoder,否则使用ByteToMessageDecoder
  • MessageToMessageDecoder: 用于从一种消息解码为另外一种消息(例如POJO到POJO)

核心方法

decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)

代码实现

解码器:

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.CharsetUtil;import java.util.List;/*** 消息解码器*/
public class MessageDecoder extends MessageToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {System.out.println("正在进行消息解码....");ByteBuf byteBuf = (ByteBuf) msg;out.add(byteBuf.toString(CharsetUtil.UTF_8));//传递到下一个handler}
}

通道读取方法

    /*** 通道读取事件** @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("客户端发送过来的消息:" + msg);}

启动类

	protected void initChannel(SocketChannel ch) throws Exception {//8. 向pipeline中添加自定义业务处理handlerch.pipeline().addLast(new MessageDecoder());//添加解码器ch.pipeline().addLast(new NettyServerHandler());}

编码器(Encoder)

与ByteToMessageDecoder和MessageToMessageDecoder相对应,Netty提供了对应的编码器实现MessageToByteEncoder和MessageToMessageEncoder,二者都实现ChannelOutboundHandler接口。

在这里插入图片描述

抽象编码器

  • MessageToByteEncoder: 将消息转化成字节
  • MessageToMessageEncoder: 用于从一种消息编码为另外一种消息(例如POJO到POJO)

核心方法

encode(ChannelHandlerContext ctx, String msg, List<Object> out)

代码实现

编码器:

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;import java.util.List;/*** 消息的编码器*/
public class MessageEncoder extends MessageToMessageEncoder {@Overrideprotected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {System.out.println("消息正在进行编码....");String str = (String) msg;out.add(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8));}
}

消息发送:

/*** 通道就绪事件** @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ChannelFuture future = ctx.writeAndFlush("你好呀.我是Netty客户端");future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {System.out.println("数据发送成功!");} else {System.out.println("数据发送失败!");}}});}

启动类:

@Override
protected void initChannel(SocketChannel ch) throws Exception {//6. 向pipeline中添加自定义业务处理handlerch.pipeline().addLast(new MessageDecoder());//添加解码器ch.pipeline().addLast(new MessageEncoder());//添加编码器ch.pipeline().addLast(new NettyClientHandler());
}

编码解码器Codec

编码解码器: 同时具有编码与解码功能,特点同时实现了ChannelInboundHandler和ChannelOutboundHandler接口,因此在数据输入和输出时都能进行处理。

在这里插入图片描述

Netty提供提供了一个ChannelDuplexHandler适配器类,编码解码器的抽象基类ByteToMessageCodec ,MessageToMessageCodec都继承与此类.

代码实现

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.util.CharsetUtil;import java.util.List;/*** 消息编解码器*/
public class MessageCodec extends MessageToMessageCodec {/*** 编码** @param ctx* @param msg* @param out* @throws Exception*/@Overrideprotected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {System.out.println("消息正在进行编码....");String str = (String) msg;out.add(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8));}/*** 解码** @param ctx* @param msg* @param out* @throws Exception*/@Overrideprotected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {System.out.println("正在进行消息解码....");ByteBuf byteBuf = (ByteBuf) msg;out.add(byteBuf.toString(CharsetUtil.UTF_8));//传递到下一个handler}
}

启动类

protected void initChannel(SocketChannel ch) throws Exception {//8. 向pipeline中添加自定义业务处理handlerch.pipeline().addLast(new MessageCoder());//添加编解码器ch.pipeline().addLast(new NettyServerHandler());
}

Netty案例-群聊天室

案例要求:

  1. 编写一个 Netty 群聊系统,实现服务器端和客户端之间的数据简单通讯
  2. 实现多人群聊
  3. 服务器端:可以监测用户上线,离线,并实现消息转发功能
  4. 客户端:可以发送消息给其它所有用户,同时可以接受其它用户发送的消息

聊天室服务端编写

NettyChatServer

import com.lagou.demo.NettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;/*** 聊天室服务端*/
public class NettyChatServer {//端口号private int port;public NettyChatServer(int port) {this.port = port;}public void run() throws InterruptedException {//1. 创建bossGroup线程组: 处理网络事件--连接事件EventLoopGroup bossGroup = null;//2. 创建workerGroup线程组: 处理网络事件--读写事件 2*处理器线程数EventLoopGroup workerGroup = null;try {bossGroup = new NioEventLoopGroup(1);workerGroup = new NioEventLoopGroup();//3. 创建服务端启动助手ServerBootstrap serverBootstrap = new ServerBootstrap();//4. 设置bossGroup线程组和workerGroup线程组serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) //5. 设置服务端通道实现为NIO.option(ChannelOption.SO_BACKLOG, 128)//6. 参数设置.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)//6. 参数设置.childHandler(new ChannelInitializer<SocketChannel>() { //7. 创建一个通道初始化对象@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//8. 向pipeline中添加自定义业务处理handler//添加编解码器ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new StringEncoder());// todoch.pipeline().addLast(new NettyChatServerHandler());}});//9. 启动服务端并绑定端口,同时将异步改为同步ChannelFuture future = serverBootstrap.bind(port);future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {System.out.println("端口绑定成功!");} else {System.out.println("端口绑定失败!");}}});System.out.println("聊天室服务端启动成功.");//10. 关闭通道(并不是真正意义上关闭,而是监听通道关闭的状态)和关闭连接池future.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {new NettyChatServer(9998).run();}
}

NettyChatServerHandle

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;import java.util.ArrayList;
import java.util.List;/*** 聊天室业务处理类*/
public class NettyChatServerHandler extends SimpleChannelInboundHandler<String> {public static List<Channel> channelList = new ArrayList<>();/*** 通道就绪事件** @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();//当有新的客户端连接的时候, 将通道放入集合channelList.add(channel);System.out.println("[Server]:" +channel.remoteAddress().toString().substring(1) + "在线.");}/*** 通道未就绪--channel下线** @param ctx* @throws Exception*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();//当有客户端断开连接的时候,就移除对应的通道channelList.remove(channel);System.out.println("[Server]:" +channel.remoteAddress().toString().substring(1) + "下线.");}/*** 通道读取事件** @param ctx* @param msg* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {//当前发送消息的通道, 当前发送的客户端连接Channel channel = ctx.channel();for (Channel channel1 : channelList) {//排除自身通道if (channel != channel1) {channel1.writeAndFlush("[" + channel.remoteAddress().toString().substring(1)+ "]说:" + msg);}}}/*** 异常处理事件** @param ctx* @param cause* @throws Exception*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();Channel channel = ctx.channel();//移除集合channelList.remove(channel);System.out.println("[Server]:" +channel.remoteAddress().toString().substring(1) + "异常.");}
}

聊天室客户端编写

NettyChatClient

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;import java.util.Scanner;/*** 聊天室的客户端*/
public class NettyChatClient {private String ip;//服务端IPprivate int port;//服务端端口号public NettyChatClient(String ip, int port) {this.ip = ip;this.port = port;}public void run() throws InterruptedException {//1. 创建线程组EventLoopGroup group = null;try {group = new NioEventLoopGroup();//2. 创建客户端启动助手Bootstrap bootstrap = new Bootstrap();//3. 设置线程组bootstrap.group(group).channel(NioSocketChannel.class)//4. 设置客户端通道实现为NIO.handler(new ChannelInitializer<SocketChannel>() { //5. 创建一个通道初始化对象@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//6. 向pipeline中添加自定义业务处理handler//添加编解码器ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new StringEncoder());//添加客户端的处理类ch.pipeline().addLast(new NettyChatClientHandler());}});//7. 启动客户端,等待连接服务端,同时将异步改为同步ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();Channel channel = channelFuture.channel();System.out.println("-------" + channel.localAddress().toString().substring(1) + "--------");Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String msg = scanner.nextLine();//向服务端发送消息channel.writeAndFlush(msg);}//8. 关闭通道和关闭连接池channelFuture.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {new NettyChatClient("127.0.0.1", 9998).run();}
}

NettyChatClientHandle

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;/*** 聊天室处理类*/
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {/*** 通道读取就绪事件** @param ctx* @param msg* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println(msg);}
}

相关文章:

Netty高级应用之:编解码器与群聊天室开发

Netty高级应用之&#xff1a;编解码器与群聊天室开发 文章目录Netty高级应用之&#xff1a;编解码器与群聊天室开发Netty编解码器Java的编解码Netty编解码器概念解码器(Decoder)编码器(Encoder)编码解码器CodecNetty案例-群聊天室聊天室服务端编写聊天室客户端编写Netty编解码器…...

Vue的生命周期

Vue的生命周期是指Vue实例从创建到销毁的过程&#xff0c;它包括了以下几个阶段&#xff1a;初始化、编译、挂载、更新、渲染和销毁。 初始化&#xff1a;Vue实例创建时&#xff0c;会执行初始化过程&#xff0c;主要包括以下几个步骤&#xff1a; 初始化数据&#xff1a;Vue…...

MySQL —— 数据库基础

文章目录1. centos7 安装Mysql2. 数据库的概念3. 数据库下创建库&#xff0c;表4. 库&#xff0c;表 的本质5. 数据库服务器 和 库 &#xff0c;表的关系6. MySQL架构7. 存储引擎前言&#xff1a; 数据库是对数据进行管理的软件。1. centos7 安装Mysql 需要把系统自带的MySQL给…...

多线程知识点

多线程 基本知识 创建线程的常用三种方式&#xff1a; 继承Thread类实现Runnable接口实现Callable接口&#xff08;JDK1.5>&#xff09; public class ThreadTest extends Thread {Overridepublic void run() {System.out.println(this.getName() "..开始.."…...

有序表之红黑树

文章目录1、五个条件2、调整策略2.1 插入调整的情况2.1.1 情况一&#xff1a;插入节点是红色&#xff0c;其父节点也是红色2.1.2 情况二2.1.2 代码实现2.2 删除调整的情况2.2.1 情况一&#xff1a;双重黑节点的兄弟节点也是黑色&#xff0c;且其兄弟的两个孩子也是黑色2.2.2 情…...

HTTP状态码都有哪些?

100&#xff1a;客户必须继续发出请求 101&#xff1a;客户要求服务器根据请求转换HTTP协议版本 二&#xff1a;2xx 200&#xff1a;交易成功 201&#xff1a;提示知道新文件的URL 202&#xff1a;接受和处理、但处理未完成 203&#xff1a;返回信息不确定或不完整 204&#…...

Sketch+摹客,100M文件上传最快47s

哈喽&#xff0c;小摹来啦~ 去年12月底&#xff0c;摹客Sketch插件上新了「规范检查工具」&#xff0c;自功能上线以来&#xff0c;小摹收到了许多的好评及赞扬。 虽好评如潮&#xff0c;但我们不会止步不前&#xff0c;将持续攻克难点&#xff0c;旨在为大家提供更加稳定高效…...

关系型数据之分区分表分库

文章目录1.为什么需要分区分表分库2.各种分区分表分库的情况3.弊端3.1分区弊端3.2分表分库弊端1.为什么需要分区分表分库 数据量达到一定规模&#xff0c;进行常规的sql语句优化已经效果不大的情况下&#xff0c;常见为mysql数据库&#xff0c;单表的记录达到1000W和空间占用至…...

微信小程序:基本开发相关文档

1、微信公众平台&#xff08;后台登录页&#xff09;&#xff1a;https://mp.weixin.qq.com/2、微信小程序官网文档&#xff08;组件api等&#xff09;&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/component/audio.html3、微信开放社区&#xff08;问题社区…...

Win10关闭自动更新

Win10关闭自动更新第一步&#xff1a;修改电脑系统时间第二步&#xff0c;设置自动更新时间第三步&#xff1a;再次修改系统时间为正确时间因为国内使用的操作系统&#xff0c;很多‍是非正版的系统&#xff0c;如果更新了系统&#xff0c;很容易造成电脑蓝屏&#xff0c;系统运…...

Embedding 理解

Word Embedding 单词表示最简单的是 one-hot 但是它的缺点是 矩阵表示过于稀疏&#xff0c;占用空间对相关的词语无法得知它们的含义是相近的。 Word Embedding 解决了上述两个缺点&#xff0c;一个 Word Embedding 直观的例子如下图所示。 每个维度表示一个特征&#xff0…...

工业树莓派和PLC怎么选?

一、 什么是虹科工业树莓派 1、树莓派 在了解虹科工业树莓派之前&#xff0c;首先要了解一下什么是树莓派。树莓派是一款基于ARM的小型电脑&#xff0c;在树莓派上提供丰富的接口&#xff0c;能够实现较多功能。它同样是开发人员的最爱&#xff0c;其搭载Linux系统&#xff0…...

多层感知机的区间随机初始化方法

摘要&#xff1a; 训练是构建神经网络模型的一个关键环节&#xff0c;该过程对网络中的参数不断进行微调&#xff0c;优化模型在训练数据集上的损失函数。参数初始化是训练之前的一个重要步骤&#xff0c;决定了训练过程的起点&#xff0c;对模型训练的收敛速度和收敛结果有重要…...

分析JEP 290机制的Java实现

简介 https://openjdk.org/jeps/290 Filter Incoming Serialization Data过滤传入的序列化数据 JEP290是Java官方提供的一套来防御反序列化的机制&#xff0c;其核心在于提供了一个ObjectInputFilter接口&#xff0c;通过设置filter对象&#xff0c;然后在反序列化&#xff…...

Leetcode.2140 解决智力问题

题目链接 Leetcode.2140 解决智力问题 Rating &#xff1a; 1709 题目描述 给你一个下标从 0开始的二维整数数组 questions&#xff0c;其中 questions[i] [pointsi, brainpoweri]。 这个数组表示一场考试里的一系列题目&#xff0c;你需要 按顺序 &#xff08;也就是从问题…...

新时代下的医疗行业新基建研讨会

1、会议纪要 2023年2月17日&#xff0c;HIT专家网进行了《新时代下的医疗行业新基建研讨会》的会议。 对会议内容进行了记录。 会议中有友谊医院、301、北肿主任进行了分享。大纲如下所示 2、本人所想 本人的所想所感&#xff1a; 1、301在多院区的医疗信息建设&#xff0c…...

BEV感知:DETR3D

3D检测&#xff1a;DETR3D前言MethodImage Feature Extracting2D-to-3D Feature TransformationLoss实验结果前言 在这篇paper&#xff0c;作者提出了一个更优雅的2D与3D之间转换的算法在自动驾驶领域&#xff0c;它不依赖于深度信息的预测&#xff0c;这个框架被称之为DETR3D…...

亿级高并发电商项目-- 实战篇 --万达商城项目 十二(编写用户服务、发送短信功能、发送注册验证码功能、手机号验证码登录功能、单点登录等模块)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…...

整合spring cloud云服务架构 - 企业分布式微服务云架构构建

1. 介绍 Commonservice-system是一个大型分布式、微服务、面向企业的JavaEE体系快速研发平台&#xff0c;基于模块化、服务化、原子化、热插拔的设计思想&#xff0c;使用成熟领先的无商业限制的主流开源技术构建。采用服务化的组件开发模式&#xff0c;可实现复杂的业务功能。…...

leetcode 540. Single Element in a Sorted Array(排序数组中的单个元素)

给一个已经排好序的升序数组&#xff0c;其中每个元素都会重复2次&#xff0c;只有一个元素只有一个&#xff0c; 找出这个只有一个的元素。 要求时间复杂度在O(logn), 空间复杂度在O(1). 思路&#xff1a; 时间复杂度为O(logn), 让人想到了binary search. 因为时间复杂度为…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

2.3 物理层设备

在这个视频中&#xff0c;我们要学习工作在物理层的两种网络设备&#xff0c;分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间&#xff0c;需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质&#xff0c;假设A节点要给…...

StarRocks 全面向量化执行引擎深度解析

StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计&#xff0c;相比传统行式处理引擎&#xff08;如MySQL&#xff09;&#xff0c;性能可提升 5-10倍。以下是分层拆解&#xff1a; 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...