uniapp实现websocket前后端代码

背景

正在进行的微信小程业务开发,需要用到websocket.经过不断的调试,终于跑通一个简单的demo。

在阅读下文之前,请先进行环境配置。
nginx配置https及wss

有上文的基础为参考, 下面只是将uniapp代码和boot端代码进行展示。

uniapp

xml 复制代码
<template>
	<view style="text-align: center;">
		<view style="border: 1px solid #67C23A; width: 718rpx; height: 400rpx;margin:12rpx auto;background-color: #fff;">
			<view  v-for="(item, index) in  msgList" style="color: #909399; font-size: 14px;">
				{{ item }}
			</view>
		</view>
		<tui-input placeholder="请输入内容" v-model="myText"></tui-input>
		<button @click="sendMessage()" style="background-color: #fff;">发送消息</button>
	</view>
</template>

<script>
import { mapState, mapMutations } from 'vuex';	
export default {
	computed: mapState(['hasLogin','userInfo']),
	data() {
		return {
			msgList: [],
			myText: "a",
			wsClient: null
		}
	},

	mounted() {
		console.log("--mounted--"+ this.userInfo.id);
		uni.connectSocket({
			url: "wss://xxx.com:443/jeecg-activiti-gateway/ws/push/"+this.userInfo.id,
			success: (data) => {
				console.log("--connect--"+ JSON.stringify(data));
			}
		})
		this.watchSocket();
	},
	onLoad() {
			
	},
	methods: {
		sendMessage(){
			console.log("--send--");
			uni.sendSocketMessage({
				data: this.myText
			})
		},
		watchSocket() {
			console.log("--watch--");
			uni.onSocketMessage(res=>{
				this.msgList.push(JSON.stringify(res))
				console.log("res:"+JSON.stringify(res))
			})
		},
		
	}
}
</script>

<style>

</style>

boot

  • WebSocketPusher
java 复制代码
package com.jeecg.activiti.gateway.modules.websocket;

import okhttp3.WebSocket;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.server.ServerEndpoint;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author mark
 * @description 消息推送
 * @date 2024-04-22
 */
@ServerEndpoint(value = "/ws/push/{userId}", encoders = WebSocketCustomEncoding.class)
@Component
public class WebSocketPusher {

    private final static Logger logger = LogManager.getLogger(WebSocket.class);

    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
     */

    private static AtomicInteger onlineCount = new AtomicInteger();

    /**
     * concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象
     */
    public static ConcurrentHashMap<String, WebSocketPusher> webSocketMap = new ConcurrentHashMap<>();

    /***
     * 功能描述:
     * concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象的参数体
     */
    public static ConcurrentHashMap<String, PushParams> webSocketParamsMap = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */

    private Session session;

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


    /**
     * 连接建立成功调用的方法
     * onOpen 和 onClose 方法分别被@OnOpen和@OnClose 所注解。他们定义了当一个新用户连接和断开的时候所调用的方法。
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        //加入map
        webSocketMap.put(userId, this);
        addOnlineCount();           //在线数加1
        logger.info("用户{}连接成功,当前在线人数为{}", userId, getOnlineCount());
        try {
            sendMessage(String.valueOf(this.session.getQueryString()));
        } catch (IOException e) {
            logger.error("IO异常");
        }
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        //从map中删除
        webSocketMap.remove(userId);
        subOnlineCount();           //在线数减1
        logger.info("用户{}关闭连接!当前在线人数为{}", userId, getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * onMessage 方法被@OnMessage所注解。这个注解定义了当服务器接收到客户端发送的消息时所调用的方法。
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("来自客户端用户:{} 消息:{}", userId, message);

        //群发消息
        for (String item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发生错误时调用
     *
     * @OnError
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(Object message) throws IOException, EncodeException {
        this.session.getBasicRemote().sendObject(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 通过userId向客户端发送消息
     */
    public void sendMessageByUserId(String userId, String message) throws IOException {
        logger.info("服务端发送消息到{},消息:{}", userId, message);

        if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            logger.error("用户{}不在线", userId);
        }

    }

    /**
     * 通过userId向客户端发送消息
     */
    public void sendMessageByUserId(String userId, Object message) throws IOException, EncodeException {
        logger.info("服务端发送消息到{},消息:{}", userId, message);
        if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            logger.error("用户{}不在线", userId);
        }
    }

    /**
     * 通过userId更新缓存的参数
     */
    public void changeParamsByUserId(String userId, PushParams pushParams) throws IOException, EncodeException {
        logger.info("ws用户{}请求参数更新,参数:{}", userId, pushParams.toString());
        webSocketParamsMap.put(userId, pushParams);
    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) throws IOException {
        for (String item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount.get();
    }

    public static synchronized void addOnlineCount() {
        onlineCount.getAndIncrement();
    }

    public static synchronized void subOnlineCount() {
        onlineCount.getAndDecrement();
    }
}
  • WebSocketCustomEncoding
less 复制代码
import com.alibaba.fastjson.JSON;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

/**
 * 在 websocket 中直接发送 obj 会有问题 - No encoder specified for object of class
 * 需要对 obj 创建解码类,实现 websocket 中的 Encoder.Text<>
 * */
public class WebSocketCustomEncoding implements Encoder.Text<Object> {
    /**
     * The Encoder interface defines how developers can provide a way to convert their
     * custom objects into web socket messages. The Encoder interface contains
     * subinterfaces that allow encoding algorithms to encode custom objects to:
     * text, binary data, character stream and write to an output stream.
     *
     * Encoder 接口定义了如何提供一种方法将定制对象转换为 websocket 消息
     * 可自定义对象编码为文本、二进制数据、字符流、写入输出流
     *  Text、TextStream、Binary、BinaryStream
     * */

    @Override
    public void init(EndpointConfig endpointConfig) {

    }

    @Override
    public void destroy() {

    }

    @Override
    public String encode(Object o) throws EncodeException {
        return JSON.toJSONString(o);
    }
}
  • PushParams
arduino 复制代码
import lombok.Data;

/**
 * 功能描述:
 *
 * @description: ws推送的参数结构
 * @Date: 2022/12/1
 */
@Data
public class PushParams {

    /**
     * 功能描述:
     * 类型
     */
    private String type;

    /**
     * 功能描述:
     * 开始时间
     */
    private String startTime;

    /**
     * 功能描述:
     * 结束时间
     */
    private String stopTime;
}

演示效果

用两台手机亲测, 可以实现类似聊天室的效果。 done!

相关推荐
这是个栗子3 分钟前
TypeScript(三)
前端·javascript·typescript·react
kvo7f2JTy8 分钟前
基于机器学习算法的web入侵检测系统设计与实现
前端·算法·机器学习
北风toto9 分钟前
前端CSS样式详细笔记
前端·css·笔记
nanfeiyan19 分钟前
git commit
前端
前端精髓3 小时前
移除 Effect 依赖
前端·javascript·react.js
码云之上3 小时前
从一个截图函数到一个 npm 包——pdf-snapshot 的诞生记
前端·node.js·github
码事漫谈3 小时前
AI提效,到底能强到什么程度?
前端·后端
IT_陈寒3 小时前
React hooks依赖数组这个坑差点把我埋了
前端·人工智能·后端
阿祖zu4 小时前
内容创作 AI 透明化声明倡议与项目开源
前端·后端·github
lpfasd1234 小时前
TypeScript + Cloudflare 全家桶部署项目全流程
前端·javascript·typescript