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!

相关推荐
BigYe程普14 分钟前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H32 分钟前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍34 分钟前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai39 分钟前
网站开发的发展(后端路由/前后端分离/前端路由)
前端
流烟默1 小时前
Vue中watch监听属性的一些应用总结
前端·javascript·vue.js·watch
2401_857297911 小时前
招联金融2025校招内推
java·前端·算法·金融·求职招聘
茶卡盐佑星_1 小时前
meta标签作用/SEO优化
前端·javascript·html
Ink1 小时前
从底层看 path.resolve 实现
前端·node.js
金灰1 小时前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5
茶卡盐佑星_1 小时前
说说你对es6中promise的理解?
前端·ecmascript·es6