Java实现在线沟通功能

news2024/10/3 8:20:43

文章目录

  • 1、介绍 和 特点
  • 2、整合SpringBoot
    • 2.1、导入依赖
    • 2.2、websocket 配置类
    • 2.3、消息处理类
    • 2.4、启动服务
    • 2.5、前端代码:张三
    • 2.6、前端代码:李四
  • 3、效果
  • 4、小结


1、介绍 和 特点

t-io是基于JVM的网络编程框架,和netty属同类,所以netty能做的t-io都能做,考虑到t-io是从项目抽象出来的框架,所以t-io提供了更多的和业务相关的API,大体上t-io具有如下特点和能力

  • 内置完备的监控和流控能力
  • 内置半包粘包处理
  • 一骑绝尘的资源管理能力
  • 内置心跳检查和心跳发送能力
  • 内置IP拉黑
  • 一流性能和稳定性(第三方权威平台TFB提供性能测试和稳定性服务)
  • 极其稳定的表现(很多用户还是停在t-io 1.x版本,就是因为太过稳定,不想变动)
  • 内置慢攻击防御
  • 唯一一个内置异步发送、阻塞发送、同步发送的网络框架
  • 唯一内置集群分发消息的能力
  • 独创的多端口资源共享能力(譬如一个端口是websocket协议,一个端口是私有的im协议,这两个端口的资源可以共享,这对协议适配极其有用)
  • 独创协议适配转换能力(让基于websocket和基于socket的应用看起来像是同一个协议)
  • 独一档的资源和业务绑定能力:绑定group、绑定userid、绑定token、绑定bsId,这些绑定几乎囊括了所有业务需求

2、整合SpringBoot

2.1、导入依赖

<!-- tio-websocket -->
<dependency>
    <groupId>org.t-io</groupId>
    <artifactId>tio-websocket-server</artifactId>
    <version>3.5.9.v20200214-RELEASE</version>
</dependency>

注意:每个版本之前存在差异请查看官方文档

2.2、websocket 配置类

import com.asurplus.tio.websocket.handle.MyWsMsgHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.tio.server.ServerTioConfig;
import org.tio.websocket.server.WsServerStarter;
 
import java.io.IOException;
 
/**
 * websocket 配置类
 */
@Configuration
public class WebSocketConfig {
 
   /**
     * 注入消息处理器
     */
    @Autowired
    private MyWsMsgHandler myWsMsgHandler;
 
    /**
     * 启动类配置
     *
     * @return
     * @throws IOException
     */
    @Bean
    public WsServerStarter wsServerStarter() throws IOException {
        // 设置处理器
        WsServerStarter wsServerStarter = new WsServerStarter(6789, myWsMsgHandler);
        // 获取到ServerTioConfig
        ServerTioConfig serverTioConfig = wsServerStarter.getServerTioConfig();
        // 设置心跳超时时间,默认:1000 * 120
        serverTioConfig.setHeartbeatTimeout(1000 * 120);
        // 启动
        wsServerStarter.start();
        return wsServerStarter;
    }
}

这里我们注入了 WsServerStarter 的 bean,这样在 SpringBoot 启动的时候,就能启动咱们的 websocket 服务

  • 注明了 websocket 的服务端口为:6789
  • 消息处理类为:myWsMsgHandler,在下一步我们将会去实现这个类
  • 设置了心跳的超时时间为:120秒,默认值,可以不设置

2.3、消息处理类

package com.ying.tiiochat.config;
 
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.HttpResponse;
import org.tio.websocket.common.WsRequest;
import org.tio.websocket.common.WsResponse;
import org.tio.websocket.server.handler.IWsMsgHandler;
 
/**
 * 消息处理类
 */
@Component
public class MyWsMsgHandler implements IWsMsgHandler {
 
   /**
    * <li>对httpResponse参数进行补充并返回,如果返回null表示不想和对方建立连接,框架会断开连接,如果返回非null,框架会把这个对象发送给对方</li>
    * <li>注:请不要在这个方法中向对方发送任何消息,因为这个时候握手还没完成,发消息会导致协议交互失败。</li>
    * <li>对于大部分业务,该方法只需要一行代码:return httpResponse;</li>
    *
    * @param httpRequest
    * @param httpResponse
    * @param channelContext
    * @return
    * @throws Exception
    */
   @Override
   public HttpResponse handshake(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
      // 可以在此做一些业务逻辑,返回null表示不想连接
      return httpResponse;
   }
 
   /**
    * 握手成功后触发该方法
    *
    * @param httpRequest
    * @param httpResponse
    * @param channelContext
    * @throws Exception
    */
   @Override
   public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
      // 拿到用户id
      String id = httpRequest.getParam("userId");
      // 绑定用户
      Tio.bindUser(channelContext, id);
      // 给用户发送消息
      JSONObject message = new JSONObject();
      message.put("msg", "连接成功...");
      message.put("sendName", "系统提醒");
      WsResponse wsResponse = WsResponse.fromText(message.toString(), "UTF-8");
      Tio.sendToUser(channelContext.tioConfig, id, wsResponse);
   }
 
   /**
    * <li>当收到Opcode.BINARY消息时,执行该方法。也就是说如何你的ws是基于BINARY传输的,就会走到这个方法</li>
    *
    * @param wsRequest
    * @param bytes
    * @param channelContext
    * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,框架不会回消息
    * @throws Exception
    */
   @Override
   public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
      System.out.println("我走了onBytes");
      return null;
   }
 
   /**
    * 当收到Opcode.CLOSE时,执行该方法,业务层在该方法中一般不需要写什么逻辑,空着就好
    *
    * @param wsRequest
    * @param bytes
    * @param channelContext
    * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,框架不会回消息
    * @throws Exception
    */
   @Override
   public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
      // 关闭连接
      Tio.remove(channelContext, "WebSocket Close");
      return null;
   }
 
   /**
    * <li>当收到Opcode.TEXT消息时,执行该方法。也就是说如何你的ws是基于TEXT传输的,就会走到这个方法</li>
    *
    * @param wsRequest
    * @param text
    * @param channelContext
    * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,框架不会回消息
    * @throws Exception
    */
   @Override
   public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception {
      JSONObject message = JSONObject.parseObject(text);
      // 接收消息的用户ID
      String receiver = message.getString("receiver");
      // 发送消息者
      String sendName = message.getString("sendName");
      // 消息
      String msg = message.getString("msg");
 
      // 保存聊天记录到DB等业务逻辑...
 
      WsResponse wsResponse = WsResponse.fromText(message.toString(), "UTF-8");
      Tio.sendToUser(channelContext.tioConfig, receiver, wsResponse);
 
      JSONObject resp = new JSONObject();
      resp.put("sendName", "系统提醒");
      resp.put("msg", "发送成功");
      return resp.toString();
   }
}

我们实现了 IWsMsgHandler 接口,并重写了该接口的 5 个方法,这 5 个方法从 发送握手包,到消息收发,到断开连接等一系列过程

2.4、启动服务

在这里插入图片描述

启动成功后,可以看出 tio 的打印结果,我们可以看出服务端口为我们设置的 6789,我们便可以连接测试了

2.5、前端代码:张三

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通讯</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
    <!--  连接-->
    var socket;
    var userName;
    // 连接
    function connect() {
        var socketUrl = "ws://localhost:6789/?userId=" + $('#sendName').val();
        if (socket != null) {
            socket.close();
            socket = null;
        }
        socket = new WebSocket(socketUrl);
        //打开事件
        socket.onopen = function () {
            console.log("开始建立链接....")
        };
        //关闭事件
        socket.onclose = function () {
            console.log("websocket已关闭");
        };
        //发生了错误事件
        socket.onerror = function () {
            console.log("websocket发生了错误");
        };
        /**
         * 接收消息
         * @param msg
         */
        socket.onmessage = function (msg) {
            console.log(msg)
            var json = JSON.parse(msg.data);
            if (msg.msg != '连接成功') {
                $("#msgDiv").append('<p class="other" style="color:olivedrab;">' + json.sendName + ':'+json.msg+'</p>');
            }
        };
    }
 
 
    /**
     * 发送消息
     */
    function sendMessage() {
        var msg = $("#msg").val();
        if (msg == '' || msg == null) {
            alert("消息内容不能为空");
            return;
        }
        var receiver = $("#receiver").val();
        if (receiver == '' || receiver == null) {
            alert("接收人不能为空");
            return;
        }
 
        var sendName = $("#sendName").val();
        if (sendName == '' || sendName == null) {
            alert("发送人不能为空");
            return;
        }
        var msgObj = {
            "receiver": receiver,
            "sendName": sendName,
            "msg": msg
        };
         $("#msgDiv").append('<p class="user" style="color: red">' + sendName + ':'+msg+'</p>');
        try{
            socket.send(JSON.stringify(msgObj));
            $("#msg").val('');
        }catch (e) {
            alert("服务器内部错误");
        }
    }
</script>
<body>
用户名:<input type="text" id="sendName" value="张三">
<input type="button" value="连接" onclick="connect()" ><br>
发送者:<input type="text" id="sender" value="张三" ><br>
接受者:<input type="text" id="receiver" value="李四"><br><br>
消  息:<textarea id="msg"></textarea><br><input type="button" value="发送" onclick="sendMessage()"><br><br>
 
消息记录:<div id="msgDiv" style="border: 1px red solid;width: 400px;height: 200px"></div>
<br>
 
</body>
</html>

2.6、前端代码:李四

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通讯</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
    <!--  连接-->
    var socket;
    var userName;
    // 连接
    function connect() {
        var socketUrl = "ws://localhost:6789/?userId=" + $('#sendName').val();
        if (socket != null) {
            socket.close();
            socket = null;
        }
        socket = new WebSocket(socketUrl);
        //打开事件
        socket.onopen = function () {
            console.log("开始建立链接....")
        };
        //关闭事件
        socket.onclose = function () {
            console.log("websocket已关闭");
        };
        //发生了错误事件
        socket.onerror = function () {
            console.log("websocket发生了错误");
        };
        /**
         * 接收消息
         * @param msg
         */
        socket.onmessage = function (msg) {
            console.log(msg)
            var json = JSON.parse(msg.data);
            if (msg.msg != '连接成功') {
                $("#msgDiv").append('<p class="other" style="color:olivedrab;">' + json.sendName + ':'+json.msg+'</p>');
            }
        };
    }
 
 
    /**
     * 发送消息
     */
    function sendMessage() {
        var msg = $("#msg").val();
        if (msg == '' || msg == null) {
            alert("消息内容不能为空");
            return;
        }
        var receiver = $("#receiver").val();
        if (receiver == '' || receiver == null) {
            alert("接收人不能为空");
            return;
        }
 
        var sendName = $("#sendName").val();
        if (sendName == '' || sendName == null) {
            alert("发送人不能为空");
            return;
        }
        var msgObj = {
            "receiver": receiver,
            "sendName": sendName,
            "msg": msg
        };
         $("#msgDiv").append('<p class="user" style="color: red">' + sendName + ':'+msg+'</p>');
        try{
            socket.send(JSON.stringify(msgObj));
            $("#msg").val('');
        }catch (e) {
            alert("服务器内部错误");
        }
    }
</script>
<body>
用户名:<input type="text" id="sendName" value="李四">
<input type="button" value="连接" onclick="connect()" ><br>
发送者:<input type="text" id="sender" value="李四" ><br>
接受者:<input type="text" id="receiver" value="张三"><br><br>
消  息:<textarea id="msg"></textarea><br><input type="button" value="发送" onclick="sendMessage()"><br><br>
 
消息记录:<div id="msgDiv" style="border: 1px red solid;width: 400px;height: 200px"></div>
<br>
 
</body>
</html>

记得有两个HTML文件哦

在这里插入图片描述

3、效果

在这里插入图片描述

4、小结

但在实际开发中,远没有一对一聊天这么简单,我们可能还需要聊天列表,历史聊天记录,客户列表、聊天室、多对多聊天(平台客服、商家客服、用户),包括 商品快捷方式,订单信息快捷方式,不过这些都是可以根据这个Demo的基础上做出来的。等有时间会根据这个Demo做一个具体的用例

原文:https://blog.csdn.net/weixin_46522803/article/details/126548065?spm=1001.2014.3001.5506

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MES系统需求误区,一文告诉你需求分析有哪些

在企业的实际应用中&#xff0c;对MES系统需求的分析常常会出现六个错误。 要求广泛&#xff0c;目标不明确由于对MES系统的概念和企业的实际运作不了解&#xff0c;导致企业在提出MES系统的要求时&#xff0c;常常会笼统而不明确&#xff0c;有时会混淆目标和需要。比如&#…

LabVIEW主VI前面板中显示或使用多个子VI

LabVIEW主VI前面板中显示或使用多个子VI想在程序中连接一个或多个子VI的前面板&#xff0c;但是当调用它们时&#xff0c;每个子VI在计算机屏幕上显示为一个新窗口。那么怎么能让每个子VI作为主VI前面板的一部分进行显示&#xff0c;而不是在屏幕上显示多个窗口&#xff1f;正在…

python读取tif图像+经纬度

python读取tif的包很多&#xff0c;但大都只能读出图像像素值&#xff0c;不能读取到经纬度信息。原因&#xff1a;TIFF 简单理解就是一种图像格式&#xff0c;类似于 jpg、png 等。GeoTIFF 就是在普通 TIFF 文件上增加了地理位置、投影信息、坐标信息等&#xff0c;常用于遥感…

BBS系统的设计与实现

技术&#xff1a;Java、JSP等摘要&#xff1a;BBS全称为Bulletin Board System&#xff0c;中文就是“电子公告板”。 BBS是一种电子信息服务系统。它向用户提供了一块公共电子白板&#xff0c;每个用户都可以在上面发布信息或提出问题&#xff0c;早期的BBS由教育机构或研究机…

电脑硬盘如何重新分区 ?教你两招磁盘分区方法

摘要&#xff1a;对于刚刚购买的电脑来说&#xff0c;有些厂商在装机的时候没有根据用户需求&#xff0c;就给硬盘随意分区了&#xff0c;有的分区划分的不是很合理&#xff0c;在使用过程中会遇到一些麻烦&#xff0c;那么电脑硬盘如何重新分区 &#xff1f;本文将给大家详细介…

OpenShift 简介

OpenShift 是红帽 Red Hat 公司基于开源的云平台&#xff0c;是平台即服务&#xff08;PaaS&#xff09;&#xff0c;是一种容器应用平台。允许开发人员构建、测试和部署云应用。该系统是在 K8S 核心之上添加工具&#xff0c;从而实现更快的应用开发、部署及扩展。 在 OpenShi…

leetcode 1675. Minimize Deviation in Array(最小化数组偏差)

数组里面有n个正整数&#xff0c;里面的数字可以无限次进行如下操作&#xff1a; 1.偶数可以除以2 2.奇数可以乘以2 数组中任意两元素差的最大值称为偏差。 把数组中的元素进行上面2种操作&#xff0c;使偏差最小。 思路&#xff1a; 数组中现有2种数字&#xff0c;一种是奇数…

新手如何入门黑客技术,黑客技术入门该学什么?

你是否曾经也对黑客技术感兴趣呢&#xff1f;感觉成为黑客是一件很酷的事&#xff0c;那么作为新手如何入门黑客技术&#xff0c;黑客技术入门该学什么呢&#xff1f; 其实不管你想在哪个新的领域里有所收获&#xff0c;你需要考虑以下几个问题。 首先你要想明白为什么学这个&…

程序员是世界上最理性、最睿智的群体,耶稣也反驳不了我,我说的!

有人说&#xff0c;程序员是吃青春饭的&#xff0c;35 岁就提前退休了。 猛一看&#xff0c;这句话是对的&#xff1b;仔细一看&#xff0c;这句话是不对的。 说它对&#xff0c;是因为现实中确实有很多程序员 35 岁就被毕业了&#xff1b;说它不对&#xff0c;是因为 35 岁以…

【数据库】redis集群环境详解

目录 集群环境 一&#xff0c;集群介绍 1、为什么需要redis集群 2、什么是redis集群 二&#xff0c;数据分片 三&#xff0c; 主从复制模型 四&#xff0c;一致性保证 五&#xff0c;集群搭建 1&#xff0c; 集群结构 2&#xff0c;创建配置文件 &#xff08;1&#…

播放器问答弹题功能(视频播放弹出问题)教程与实际演示案例

阿酷tony / 原创 / 2023-2-24 长沙问答弹题功能是指酷播云产品在视频播放的指定时间点弹出问答题目&#xff0c;适合在教学、培训类视频中使用。使用问答功能&#xff0c;既可以增加学生与内容的互动&#xff0c;有利于教学质量的提升&#xff0c;又可以评估学生的学习效果和课…

【 K8s 源码之调度学习】Pod 间亲和性和反亲和性的源码分析

查看案例 字段含义podAffinityPod 间的亲和性定义podAntiAffinityPod 间的反亲和性定义requiredDuringSchedulingIgnoredDuringExecution硬性要求&#xff0c;必须满足条件&#xff0c;保证分散部署的效果最好使用用此方式preferredDuringSchedulingIgnoredDuringExecution软性…

duilib.dll丢失怎么办?dll文件丢失修复方法分享

duilib.dll丢失怎么办&#xff1f;其实在使用 Windows 系统的过程中&#xff0c;有时会出现提示“duilib.dll丢失”的错误。这个错误可能会影响电脑的正常运行&#xff0c;但是不用担心&#xff0c;今天小编来给大家详细的讲解一下duilib.dll丢失都有哪些解决方法。 一.什么是…

SAFe(Scaled Agile Framework)学习笔记

1.SAFe 概述 SAFe&#xff08;Scaled Agile Framework&#xff09;是一种面向大型企业的敏捷开发框架&#xff0c;旨在协调多个团队和部门的协同工作&#xff0c;以实现高效的软件开发和交付。下面是SAFe框架的简单介绍总结&#xff1a; SAFe框架包括以下四个层次&#xff1a…

金测评 手感更细腻的游戏手柄,双模加持兼容更出色,雷柏V600S上手

很多朋友周末都喜欢玩玩游戏放松一下&#xff0c;在家玩游戏的时候&#xff0c;PC是大家常用的平台&#xff0c;当然了&#xff0c;玩游戏的时候用键鼠的话&#xff0c;手感难免差点意思&#xff0c;还是要手柄才能获得更好的体验。我现在用的是雷柏V600S&#xff0c;这是一款支…

飞鹅打印机怎么样?飞鹅打印机好用吗?飞鹅打印机怎么知道订单是否漏单?

外卖打印机怎么选?飞鹅打印机好用吗&#xff1f;飞鹅智能云打印机产品专注于云打印的解决方案和技术服务提供。2019 年飞鹅已经成为国内先进的云打印服务提供商&#xff0c;主要是服务美团、饿了么客户&#xff0c;产品主要优势&#xff1a;自动接单、自动打印&#xff0c;无需…

美好音乐不只在现场,索尼播放器NW-WM1ZM2和NW-WM1AM2满足聆听热爱

当两点一线的单调生活成了多数人的生活常态&#xff0c;那些有过程有讲究的仪式感开始变得弥足珍贵起来&#xff0c;爱乐者们不远千里奔赴音乐节、Livehouse的现场&#xff0c;除了追求当下高燃兴奋的感受&#xff0c;同样是为了获得一份全心投入的听音仪式感。而当不便出行的日…

.net core 本地环境切换网络遇到的问题 500.19 502.5 invalid_request

问题一 运行环境 IIS 部署.NET CORE 项目 出现 HTTP 错误 500.19 - Internal Server Error附上.NET CORE2.1版本的下载链接下载 .NET Core 2.1 (Linux、macOS 和 Windows) (microsoft.com)下载完成以后重启IIS&#xff0c;有的版本还需要在IIS设置.NET CLR版本为无托管代码二 H…

从0开始写Vue项目-Vue实现用户个人信息界面上传头像

从0开始写Vue项目-环境和项目搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue2集成Element-ui和后台主体框架搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue页面主体布局和登录、注册页面_慕言要努力的博客-CSDN博客从0开始写Vue项目-SpringBoot整合Mybatis-plus实现…

mybatis-plus深入学习篇(三)

mybatis-plus深入学习篇(三) 1 准备工作 1.1 建表sql语句(Emp表) SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0; -- ---------------------------- -- Table structure for emp -- ---------------------------- DROP TABLE IF EXISTS emp; CREATE TABLE emp (EMPNO int …