springboot+webSocket对接chatgpt

news2024/11/15 17:26:20

webSocket对接参考

话不多说直接上代码

WebSocket

package com.student.config;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Description:
 * @Author: hwk
 * @Date: 2024-07-17 17:46
 * @Version: 1.0
 **/
@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * gpt密钥
     */
    private static final String key = "";
    /**
     * 请求地址
     */
    private static final String url = "";


    /**
     * 用户ID
     */
    private String userId;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    //虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
    //  注:底下WebSocket是当前类名
    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
    // 用来存在线连接用户信息
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();

    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        try {
            this.session = session;
            this.userId = userId;
            webSockets.add(this);
            sessionPool.put(userId, session);
            log.info("【websocket消息】有新的连接,总数为:" + webSockets.size());
        } catch (Exception e) {
        }
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
            webSockets.remove(this);
            sessionPool.remove(this.userId);
            log.info("【websocket消息】连接断开,总数为:" + webSockets.size());
        } catch (Exception e) {
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端消息:" + message);
        JSONObject jsonObject = new JSONObject();
        JSONArray objects = new JSONArray();
        JSONObject messages = new JSONObject();
        messages.put("role", "user");
        messages.put("content", message);
        objects.add(messages);
        jsonObject.put("model", "gpt-3.5-turbo");
        jsonObject.put("messages", objects);
        jsonObject.put("max_tokens", 1024);
        jsonObject.put("temperature", 0);
        jsonObject.put("stream", true);
        Map<String, String> heads = new HashMap<>();
        heads.put("Content-Type", "application/json");
        heads.put("Accept", "application/json");
        heads.put("Authorization", "Bearer "+key);
        WebClient webClient = WebClient.create();
        Flux<String> stringFlux = webClient.post()
                .uri(url)
                .header("Content-Type", "application/json")
                .header("Accept", "application/json")
                .header("Authorization", "Bearer " + key)
                .accept(MediaType.TEXT_EVENT_STREAM)
                .bodyValue(jsonObject)
                .retrieve()
                .bodyToFlux(String.class);
        stringFlux.subscribe(s -> {
            if (!Objects.equals(s, "[DONE]")) {
                JSONObject parsed = JSONObject.parseObject(s);
                JSONArray choices = parsed.getJSONArray("choices");
                if (!choices.isEmpty()) {
                    JSONObject dataJson = JSONObject.parseObject(choices.get(0).toString());
                    String content = dataJson.getJSONObject("delta").getString("content");
                    if (StringUtils.hasLength(content)) {
                        try {
                            content = content.replaceAll("\n", "<br>");
                            content = content.replace(" ", "");
                            log.info(content);
                            if (sessionPool != null) {
                                sessionPool.get(userId).getBasicRemote().sendText(content);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    /**
     * 发送错误时的处理
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误,原因:" + error.getMessage());
        error.printStackTrace();
    }


    /**
     * 此为广播消息
     *
     * @param message
     */
    public void sendAllMessage(String message) {
        log.info("【websocket消息】广播消息:" + message);
        for (WebSocket webSocket : webSockets) {
            try {
                if (webSocket.session.isOpen()) {
                    webSocket.session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 此为单点消息
     *
     * @param userId
     * @param message
     */
    public void sendOneMessage(String userId, String message) {
        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            try {
                log.info("【websocket消息】 单点消息:" + message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 此为单点消息(多人)
     *
     * @param userIds
     * @param message
     */
    public void sendMoreMessage(String[] userIds, String message) {
        for (String userId : userIds) {
            Session session = sessionPool.get(userId);
            if (session != null && session.isOpen()) {
                try {
                    log.info("【websocket消息】 单点消息:" + message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

WebSocketConfig

package com.student.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @Description: WebSocketConfig配置
 * @Author: hwk
 * @Date: 2024-07-17 17:44
 * @Version: 1.0
 **/
@Configuration
public class WebSocketConfig {
    /**
     *   注入ServerEndpointExporter,
     *   这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

html代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>ChatGPT</title>
    <script src="marked.min.js"></script>
    <link rel="stylesheet" type="text/css" href="index.css">
    <style>
        .normal-text {
            color: black;
        }

        .rich-text {
            color: blue;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h2>ChatGPT</h2>
    <div id="message-container">
        <p id="message"></p>
    </div>

    <div id="footer">
        <input id="text" class="my-input" type="text" />
        <button onclick="send()">发送</button>
    </div>

    <div id="footer1">
        <br />
        <button onclick="closeWebSocket()">关闭WebSocket连接</button>
        <button onclick="openWebSocket()">建立WebSocket连接</button>
    </div>

    <script>
    marked.setOptions({
        highlight: function (code, lang) {
            return hljs.highlightAuto(code).value;
        }
    });
        var websocket = null;

        // 判断当前浏览器是否支持WebSocket,是则创建WebSocket
        if ('WebSocket' in window) {
            console.log("浏览器支持WebSocket");
            websocket = new WebSocket("ws://127.0.0.1:8056/websocket/1");
        } else {
            alert('当前浏览器不支持WebSocket');
        }

        // 连接发生错误的回调方法
        websocket.onerror = function () {
            console.log("WebSocket连接发生错误");
            setMessageInnerHTML("WebSocket连接发生错误");
        };

        // 连接成功建立的回调方法
        websocket.onopen = function () {
            console.log("WebSocket连接成功");
        };

        // 接收到消息的回调方法
        websocket.onmessage = function (event) {
            if (event.data) {
                setMessageInnerHTML(event.data);
            }
            console.log(event.data);
        };

        // 连接关闭的回调方法
        websocket.onclose = function () {
            console.log("WebSocket连接关闭");
        };

        // 关闭WebSocket连接
        function closeWebSocket() {
            websocket.close();
        }

        // 发送消息
        function send() {
            var message = document.getElementById('text').value;
            websocket.send(message);
        }

        // 建立连接的方法
        function openWebSocket() {
            websocket = new WebSocket("ws://127.0.0.1:8056/websocket/1");
            websocket.onopen = function () {
                console.log("WebSocket连接成功");
            };
        }

        // 将消息显示在网页上
        function setMessageInnerHTML(innerHTML) {
            console.log(innerHTML);
            // var element = document.getElementById('message');
            // if (innerHTML.match(/```/g)) {
            //     element.innerHTML += marked(innerHTML); // 使用marked渲染Markdown
            // } else {
            //     element.innerHTML += innerHTML; // 直接添加普通文本消息
            // }
            document.getElementById('message').innerHTML += innerHTML;
        }

        // 如果websocket连接还没断开就关闭了窗口,后台server端会抛异常。
        // 所以增加监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接
        window.onbeforeunload = function () {
            closeWebSocket();
        };
    </script>
</body>
</html>

效果
在这里插入图片描述

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

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

相关文章

C#知识|账号管理系统:实现修改管理员登录密码

哈喽,你好啊,我是雷工! 本节主要记录实现修改管理员登录密码的后端逻辑及相关功能,以下为学习笔记。 01 实现逻辑 ①:首先输入原密码,验证,验证通过然后可以输入新密码进行修改; ②:新密码修改为了避免输入失误导致输入的密码与自己以为修改的密码不符的情况,增加了…

Langchain核心模块与实战[7]:专业级Prompt工程调教LLM[输入输出接口、提示词模板与例子选择器的协同工程]

Langchain核心模块与实战[7]:专业级Prompt工程调教LLM[输入输出接口、提示词模板与例子选择器的协同工程] 1. 大模型IO接口 任何语言模型应用的核心元素是…模型的输入和输出。LangChain提供了与任何语言模型进行接口交互的基本组件。 提示 prompts : 将模型输入模板化、动态…

**卷积神经网络典型CNN**

LeNet&#xff1a;最早用于数字识别的CNN AlexNet&#xff1a;2012年ILSVRC比赛冠军&#xff0c;远超第二名的CNN&#xff0c;比LeNet更深&#xff0c;用多层小卷积叠加来替换单个的大卷积 ZF Net&#xff1a;2013ILSVRC冠军 GoogleNet&#xff1a;2014ILSVRC冠军 VGGNet&a…

go语言Gin框架的学习路线(十一)

目录 GORM的CRUD教程 更新操作 更新所有字段 更新指定字段 使用 Select 和 Omit 更新 无 Hooks 更新 批量更新 删除操作 删除记录 批量删除 软删除 物理删除 示例代码 GORM的CRUD教程 CRUD 是 "Create, Read, Update, Delete"&#xff08;创建、查询、…

百度,有道,谷歌翻译API

API翻译 百度&#xff0c;有道&#xff0c;谷歌API翻译&#xff08;只针对中英相互翻译&#xff09;,其他语言翻译需要对应from&#xff0c;to的code 百度翻译 package fills.tools.translate; import java.util.ArrayList; import java.util.HashMap; import java.util.Lis…

windows服务器启动apache失败,提示请通过cmd命令行启动:net start apache

Windows Server 2012 R2服务器突然停止运行apche&#xff0c;启动apache失败&#xff0c;提示请通过cmd命令行启动:net start apache 1.报错截图&#xff1a; 进入服务里输入命令启动也不行&#xff0c;提示由于登录失败而无法启动服务。 2.问题原因&#xff1a; 服务器www用…

数据库(MySQL)-DQL数据查询语言

DQL(Data Query Language 数据查询语言)的用途是查询数据库数据&#xff0c;如select语句。其中&#xff0c;可以根据表的结构和关系分为单表查询和多表联查。 单表查询 单表查询&#xff1a;针对数据库中的一张数据表进行查询 全字段查询 语法&#xff1a;select 字段名 fro…

User Allocation In MEC: A DRL Approach 论文笔记

论文&#xff1a;ICWS 2021 移动边缘计算中的用户分配&#xff1a;一种深度强化学习方法 代码地址&#xff1a;使用强化学习在移动边缘计算环境中进行用户分配 目录 Ⅰ.Introduction II. MOTIVATION-A.验证假设的观察结果 II. MOTIVATION-A Motivating Example 数据驱动…

我在百科荣创企业实践——简易函数信号发生器(5)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

C#,.NET常见算法

1.递归算法 1.1.C#递归算法计算阶乘的方法 using System;namespace C_Sharp_Example {public class Program{/// <summary>/// 阶乘&#xff1a;一个正整数的阶乘Factorial是所有小于以及等于该数的正整数的积&#xff0c;0的阶乘是1&#xff0c;n的阶乘是n&#xff0…

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(五)-无人机跟踪

目录 引言 5.3 无人机跟踪 5.3.1 无人机跟踪模型 5.3.2 无人机位置报告流程 5.3.3 无人机存在监测流程 引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别、跟踪及A2X&#xff08;Aircraft-to-Everyth…

Samtec技术科普小课堂 | 一文入门射频连接器~

【摘要/前言】 在本文中&#xff0c;我们将回到基础知识&#xff0c;了解一下什么是射频连接器。如果您是信号完整性专家&#xff0c;请点击阅读原文访问我们的网站视频&#xff0c;通过我们的网络研讨会视频了解教科书上可能找不到的知识。 如果您是电气工程领域的新手&#…

Prometheus 监控Tomcat等java应用的状态

5月应用服务出现问题&#xff0c;当别的小伙伴问我&#xff0c;有没有Tomcat等应用状态的监控的时候&#xff0c;我有点儿尴尬。所以赶紧抽空部署一下。 在配置之前&#xff0c;就当已经会安装jdk和tomcat了。 一、下载jmx_exporter #linux下 cd /usr/local/prometheus wget …

新增ClamAV病毒扫描功能、支持Java和Go运行环境,1Panel开源面板v1.10.12版本发布

2024年7月19日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布了v1.10.12版本。 在这一版本中&#xff0c;1Panel新增了多项实用功能。社区版方面&#xff0c;1Panel新增ClamAV病毒扫描功能、支持Java和Go运行环境&#xff0c;同时1Panel还新增了文件编辑器…

浪潮GS企业管理软件GetChildFormAndEntityList存在反序列化漏洞

一、漏洞简介 浪潮GS 面向大中型集团企业采用SOA 架构和先进开放的GSP 应用中间件开发,形成了集团管控13 大领域15 大行业60余个细分行业的解决方案。在管理方面,浪潮GS 有效帮助企业有效实现财务集中管理、资金集中管理、资产集中管理、供应链集中管理,从而达到集团信息的…

案例|华能某风电场配电房智能巡检机器人解决方案

随着风电产业的迅猛发展&#xff0c;风电场内配电房是风电场电能传输和转换的关键节点&#xff0c;其设备运行状况直接影响到风电场的整体运行效率和安全性。传统的人工巡检方式存在效率低、误差大、安全风险高等问题&#xff0c;难以满足现代风电场对高效、可靠运维的需求。智…

第一百七十二节 Java IO教程 - Java I/O缓冲区

Java IO教程 - Java I/O缓冲区 什么是NIO&#xff1f; 在NIO中&#xff0c;我们处理I/O操作的通道和缓冲区。 像流一样的通道表示数据源/接收器和用于数据传输的Java程序之间的连接。 通道提供双向数据传输设施。我们可以使用通道来读取数据以及写入数据。根据我们的需要&a…

【python】Python常见的面试题解析:深入探索与实践,助你少走弯路

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Redis分布式系统中的主从复制

本篇文章主要对Redis的主从复制进行讲解。主要分析复制的原理&#xff0c;包括:建立复制、全量复制、部分复制、全量复制、心跳检测等。希望本篇文章会对你有所帮助。 文章目录 一、主从复制简介 二、配置主从复制模式 断开主从复制 安全性 只读 传输延迟 三、拓扑结构 四、主…

秋招复习笔记——八股文部分:网络TCP

TCP 三次握手和四次挥手 TCP 基本认识 序列号&#xff1a;在建立连接时由计算机生成的随机数作为其初始值&#xff0c;通过 SYN 包传给接收端主机&#xff0c;每发送一次数据&#xff0c;就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。 确认应答号&#xf…