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

IM聊天学习资源

文章目录

  • 参考链接
    • 使用
    • 前端界面
    • 简单效果
      • 消息窗口平滑滚动至底部
      • vue使用watch监听vuex中的变量变化
    • websocket握手认证
      • ChatKeyCheckHandler
      • NettyChatServer
      • NettyChatInitializer

参考链接

zzhua/netty-chat-web - 包括前后端

vue.js实现带表情评论功能前后端实现(仿B站评论)
vue.js实现带表情评论仿bilibili(滚动加载效果)

vue.js支持表情输入
vue.js表情文本输入框组件

个人说说vue组件

JS操作文本域获取光标/指定位置插入

分布式ID生成之雪花算法(SnowFlake)


netty-demo-crazy - 疯狂架构师netty

IM即时通讯系统[SpringBoot+Netty]——梳理(总), 代码 im-system 在gitee

构建IM即使通讯Web页面 - B站视频,仅前端代码,代码 chat-demo 在gitee

  • 禹神:一小时快速上手Electron,前端Electron开发教程,笔记。一篇文章入门Electron
  • Electron集成Vite + Vue 开发IM即使通讯
  • easychat - gitee代码

基于vue3实现一个简单的输入框效果
vue3通过组合键实现换行操作的示例详解

easychat - B站视频,前后端,代码在gitee

subtlechat-mini - 前后端,有mini版和完整版

lyf-im - 前后端项目

box-im - 盒子im,很棒

MallChat - 前后端项目,代码很棒

木杉/ 视频通话 netty webrtc websocket springboot uniapp

木杉/ /mushan-im

ProgHub/chat_room

yymao/chatroom - 仅前端,im界面好看

H260788/PureChat - im界面好看,前端难度大

netty-chatroom - netty实现,仅后端代码

liurq_netty_barrage - netty实现的1个简单的弹幕效果

yorick_socket一套基于Netty的轻量级Socket和WebSocket框架,可用于搭建聊天服务器和游戏同步服务器

【聊天系统】从零开始自己做一个"wechat" - uniapp 和 springboot

linyu-mini-web&linyu-mini-server gitee 前后端代码

aq-chat web端,AQChatServer,aqchat-mobile,AQChat文档中心,

考拉开源/im-uniapp,im-platform 后台代码

ws-chat - 前后端代码

yan代码,B站视频)

im-whale-shark代码,B站视频

使用

前端使用:vue.js + vuex + iconfont + element-ui
后端使用:springboot + mybatisplus + redis + netty + websocket + spring security

可能有不少问题,反正先按照自己思路一点一点写,再参考下别人是怎么搞的再优化

前端界面

先写下大概的前端界面,界面出来了,才有继续写下去的动力

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

简单效果

在这里插入图片描述

消息窗口平滑滚动至底部

<div class="panel-main-body" ref="panelMainBodyContainerRef"><!-- 对应会话 的消息列表 --><div class="msg-item-list" ref="msgItemListContainerRef"><div :class="['msg-item', familyChatMsg.senderId != currUserId ? 'other' : 'owner']"v-for="(familyChatMsg, idx) in familyChatMsgList" :key="idx"><div class="avatar-wrapper "><img :src="familyChatMsg.avatar" class="avatar fit-img" alt=""></div><div class="msg"><div class="msg-header">{{ familyChatMsg.nickName }}</div><div class="msg-content" v-html="familyChatMsg.content"></div></div></div></div></div><script>
export default {methods: {/* 滚动至底部,不过调用此方法的时机应当在familyChatMsgList更新之后, 因此需要监听它 */scrollToEnd() {const panelMainBodyContainerRef = this.$refs['panelMainBodyContainerRef']const msgItemListContainerRef = this.$refs['msgItemListContainerRef']// console.log(msgItemListContainerRef.scrollTop);// console.log(panelMainBodyContainerRef.scrollHeight);msgItemListContainerRef.scrollTop = msgItemListContainerRef.scrollHeightconsole.log('滚动至底部~');},}}
</script><style>
.msg-item-list {/* 平滑滚动 */scroll-behavior: smooth;
}
</style>

vue使用watch监听vuex中的变量变化

computed: {...mapGetters('familyChatStore', ['familyChatMsgList']),
},watch: {// 监听store中的数据 - 是通过监听getters完成的familyChatMsgList:{handler(newVal, oldVal) {// console.log('---------------------');// console.log(newVal.length, oldVal.length);this.$nextTick(()=>{this.scrollToEnd()})}}},

websocket握手认证

客户端在登录完成后,可以请求后端的接口获取1个chatKey(这个chatKey只有在用户登录后,携带token访问时才能得到),得到此chatKey后,连接websocket客户端时,把这个chatKey作为请求参数拼接到ws://xxxx.xx.xx:9091/ws?chatKey=xxx,这样在握手的时候,就可以拿到这个请求参数。但是,我不想在握手完成事件时再去拿这个chatKey(虽然这样做,也没什么问题,但感觉逻辑不是很好,都已经握手完成了,再来断掉ws连接有点不好),因此,设置1个ChatKeyCheckHandler,它继承自SimpleInboundHandlerAdapter,处理的泛型是FullHttpRequest,并且把这个处理器放在WebSocketServerProtocolHandler的前面,这样,在处理握手请求时,就可以拿到请求参数了,而握手完成之后,由于后面的消息是websocket协议帧数据,它不会FullHttpRequest类型的,因此不会经过这个处理器,这样感觉比较好~

ChatKeyCheckHandler

@Slf4j
@ChannelHandler.Sharable
@Component
public class ChatKeyCheckHandler extends SimpleChannelInboundHandler<FullHttpRequest> {public ChatKeyCheckHandler() {super(false);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {log.info("http请求-chatKeyCheckHandler处理");FullHttpRequest request = ((FullHttpRequest) msg);String uri = request.uri();log.info("请求uri: {}");log.info("请求header: {}", Arrays.toString(request.headers().names().toArray()));List<String> chatKeys = UriComponentsBuilder.fromUriString(uri).build().getQueryParams().get(Constants.CHAT_KEY);if (CollectionUtils.isEmpty(chatKeys)) {log.error("欲建立websocket连接,但未携带chatKey,直接略过");// 还得写个响应回去,并且关闭HTTP连接HttpRequest req = msg;FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,Unpooled.wrappedBuffer("NOT ALLOWD WITHOUT CHAT_KEY".getBytes()));response.headers().set(CONTENT_TYPE, TEXT_PLAIN).setInt(CONTENT_LENGTH, response.content().readableBytes());// Tell the client we're going to close the connection.response.headers().set(CONNECTION, CLOSE);ChannelFuture f = ctx.writeAndFlush(response);f.addListener(ChannelFutureListener.CLOSE);return;}String chatKey = chatKeys.iterator().next();ctx.channel().attr(WsContext.CHAT_KEY_ATTR).set(chatKey);log.info("建立websocket连接的握手请求, 携带了chatKey: {}", chatKey);// 在此处校验chatKey是否合理, 如果不合理, 则不允许建立websocket链接(不会进行后面的握手处理)ctx.fireChannelRead(request);}
}

NettyChatServer

@Slf4j
@Component
public class NettyChatServer implements SmartLifecycle {@Autowiredprivate NettyProperties nettyProps;@Autowiredprivate NettyChatInitializer nettyChatInitializer;private volatile boolean running = false;private ServerChannel serverChannel;private EventLoopGroup bossGroup;private EventLoopGroup workerGroup;public void start() {log.info("starting netty server~");bossGroup = new NioEventLoopGroup(1);workerGroup = new NioEventLoopGroup(2);ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(nettyChatInitializer);// 这里会异步调用ChannelFuture channelFuture = serverBootstrap.bind(nettyProps.getPort());channelFuture.addListener(future -> log.info("netty started, listening: {}", nettyProps.getPort()));// 保存对ServerSocketChannel的引用serverChannel = (NioServerSocketChannel) channelFuture.channel();channelFuture.channel().closeFuture().addListener(future -> log.info("netty stopped!"));running = true;}@Overridepublic void stop() {log.info("stop netty server");try {serverChannel.close();} catch (Exception e) {log.error("关闭ServerSocketChannel失败");}if (bossGroup != null) {bossGroup.shutdownGracefully();}if (workerGroup != null) {workerGroup.shutdownGracefully();}running = false;}@Overridepublic boolean isRunning() {return this.running;}}

NettyChatInitializer

@Component
public class NettyChatInitializer extends ChannelInitializer<SocketChannel> {@Autowiredprivate NettyProperties nettyProperties;@Autowiredprivate DispatcherMsgHandler dispatcherMsgHandler;@Autowiredprivate HandShakeHandler handShakeHandler;@Autowiredprivate ChatKeyCheckHandler chatKeyCheckHandler;@Overrideprotected void initChannel(SocketChannel ch) throws Exception {DefaultEventLoopGroup eventExecutors = new DefaultEventLoopGroup(2);ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));pipeline.addLast("http-decoder", new HttpRequestDecoder());pipeline.addLast("http-encoder", new HttpResponseEncoder());pipeline.addLast("aggregator", new HttpObjectAggregator(64 * 1024));pipeline.addLast(new ChunkedWriteHandler());WebSocketServerProtocolConfig wsServerConfig = WebSocketServerProtocolConfig.newBuilder().websocketPath(nettyProperties.getWsPath()).checkStartsWith(true).maxFramePayloadLength(Integer.MAX_VALUE).build();pipeline.addLast("chatKeyHandler", chatKeyCheckHandler);pipeline.addLast("websocketHandler", new WebSocketServerProtocolHandler(wsServerConfig));pipeline.addLast("handShakeHandler", handShakeHandler);pipeline.addLast("heartBeanCheckHandler", new HeatBeatCheckHandler());pipeline.addLast(eventExecutors, "dispatcherMsgHandler", dispatcherMsgHandler);}}

相关文章:

IM聊天学习资源

文章目录 参考链接使用前端界面简单效果消息窗口平滑滚动至底部vue使用watch监听vuex中的变量变化 websocket握手认证ChatKeyCheckHandlerNettyChatServerNettyChatInitializer 参考链接 zzhua/netty-chat-web - 包括前后端 vue.js实现带表情评论功能前后端实现&#xff08;仿…...

计算机视觉模型的未来:视觉语言模型

一、视觉语言模型 人工智能已经从识别数据中的简单模式跃升为理解复杂的多模态数据。该领域的发展之一是视觉语言模型 (VLM) 的兴起。这类模型将视觉和文本之间联系起来,改变了我们理解视觉数据并与之交互的方式。随着 VLM 的不断发展,它们正在为计算机视觉设定一个新的水平…...

【JAVA 基础 第(19)课】Hashtable 类用法和注意细节,是Map接口的实现类

Map接口&#xff1a;存放的是具有映射关系的键值对&#xff0c;键映射到值&#xff0c;键必须是唯一的 Hashtable 类&#xff0c;Map接口的实现类,键和值都不能为nullHashtable 是同步的&#xff0c;是线程安全的 public class MapTest {public static void main(String[] arg…...

浅谈 JVM

JVM 内存划分 JVM 内存划分为 四个区域&#xff0c;分别为 程序计数器、元数据区、栈、堆 程序计数器是记录当前指令执行到哪个地址 元数据区存储存储的是当前类加载好的数据&#xff0c;包括常量池和类对象的信息&#xff0c;.java 编译之后产生 .class 文件&#xff0c;运…...

html的iframe页面给帆软BI发送消息

需求&#xff1a;帆软的网页组件嵌套一个HTML页面&#xff0c;HTML页面要给帆软发消息。 解决方法是&#xff1a;fineReportWindow.duchamp.getWidgetByName("txt1").setValue(666); <!DOCTYPE html> <html lang"en"> <head> <…...

spark任务优化参数整理

以下参数中有sql字眼的一般只有spark-sql模块生效&#xff0c;如果你看过spark的源码&#xff0c;你会发现sql模块是在core模块上硬生生干了一层&#xff0c;所以反过来spark-sql可以复用core模块的配置&#xff0c;例外的时候会另行说明&#xff0c;此外由于总结这些参数是在不…...

C++ 模拟真人鼠标轨迹算法 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…...

生产环境中常用的设计模式

生产环境中常用的设计模式 设计模式目的使用场景示例单例模式保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点- 日志记录器- 配置管理器工厂方法模式定义一个创建对象的接口&#xff0c;让子类决定实例化哪个类- 各种工厂类&#xff08;如视频游戏工厂模式创…...

基于SpringBoot+Vue的药品管理系统【源码+文档+部署讲解】

系统介绍 基于SpringBootVue实现的药品管理系统采用前后端分离的架构方式&#xff0c;系统实现了用户登录、数据中心、药库管理、药房管理、物资管理、挂号管理、系统管理、基础设置等功能模块。 技术选型 开发工具&#xff1a;idea2020.3Webstorm2020.3 运行环境&#xff…...

【CompletableFuture实战】

CompletableFuture实战 前言 前言 过去的一年&#xff0c;匆匆忙忙&#xff0c;换了一次工作&#xff0c;写博客的习惯就落下了&#xff0c;总之&#xff0c;有点懈怠。希望今年能重拾信心&#xff0c;步入正规&#xff01; CompletableFuture的用法网上资料颇多&#xff0c;…...

Redis 缓存穿透、击穿、雪崩 的区别与解决方案

前言 Redis 是一个高性能的键值数据库&#xff0c;广泛应用于缓存、会话存储、实时数据分析等场景。然而&#xff0c;在高并发的环境下&#xff0c;Redis 缓存可能会遇到 缓存击穿、缓存穿透 和 缓存雪崩 这三大问题。这些问题不仅影响系统的稳定性和性能&#xff0c;还经常出…...

Python自动化测试中定位隐藏菜单元素的策略

大家都读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 在进行Python自动化测试时&#xff0c;尤其是使用Selenium等工具对Web应用进行测试时&#xff0c;可能会遇到某些元素被隐藏的问题。这使得元素定位和交互变得复杂。然而&#xff0c;通过一些技术手段…...

【张雪峰高考志愿填报】合集

【张雪峰高考志愿填报】合集 链接&#xff1a;https://pan.quark.cn/s/89a2d88fa807 高考结束&#xff0c;分数即将揭晓&#xff0c;志愿填报的关键时刻近在眼前&#xff01;同学们&#xff0c;这可是人生的重要转折点&#xff0c;选对志愿&#xff0c;就像为未来铺就一条…...

53,【3】BUUCTF WEB october 2019 Twice SQLinjection

题目得到信息&#xff0c;2次注入&#xff0c;进入靶场 登录页面&#xff0c;很自然想到SQL 第一次注入应该是这个可以登录&#xff0c;注册&#xff0c;提交简介的页面 第二次注入应该是在info处注入&#xff0c;信息显示在简介处 我真的纯脑子有病&#xff0c;人家二次注入不…...

【Linux系统】分区挂载

我们能够根据一个 inode 号在指定分区寻找目标文件的 struct inode&#xff0c;也能根据目录文件的内容&#xff0c;通过映射关系&#xff0c;找指定的 inode&#xff0c;可是&#xff0c;现在有个问题&#xff1a; 问题&#xff1a;inode 是不能跨分区使用的&#xff01;Linu…...

Oracle 可观测最佳实践

简介 Oracle 数据库是一种广泛使用的商业关系数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;由甲骨文公司&#xff08;Oracle Corporation&#xff09;开发。它支持 SQL 语言&#xff0c;能够存储和管理大量数据&#xff0c;并提供高级数据管理功能&#xff0c;如数…...

Ubuntu本地部署网站

目录 1.介绍 2.安装apache 3.网页升级 1.介绍 网站其实就相当于一个文件夹&#xff0c;用域名访问一个网页&#xff0c;就相当于访问了一台电脑的某一个文件夹&#xff0c;在网页中看见的视频&#xff0c;视频和音乐其实就是文件夹里面的文件。为什么网页看起来不像电脑文件夹…...

图数据库 | 18、高可用分布式设计(中)

上文我们聊了在设计高性能、高可用图数据库的时候&#xff0c;从单实例、单节点出发&#xff0c;一般有3种架构演进选项&#xff1a;主备高可用&#xff0c;今天我们具体讲讲分布式共识&#xff0c;以及大规模水平分布式。 主备高可用、分布式共识、大规模水平分布式&#xff…...

Java 读取 Windows 设备的唯一性标识及定位

在 Windows 系统中&#xff0c;获取设备唯一性标识及定位信息对设备管理、安全监控等场景意义重大。本文介绍 Java 中几种实现方法&#xff0c;如 JNA 库、WMI4Java 库及通过 JNI 结合 Windows API。 1. 使用 JNA 库读取 DEVPKEY_Device_ContainerId 在 Windows 系统中&…...

Spring boot框架下的RabbitMQ消息中间件

1. RabbitMQ 基础概念 1.1 消息处理流程与组件配合 Producer&#xff08;生产者&#xff09; 发送消息。消息先发送到 Exchange&#xff08;交换机&#xff09;&#xff0c;而不是直接到队列。Exchange&#xff08;交换机&#xff09; 接收到消息后&#xff0c;根据 Routing …...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...