uni-app和Spring Boot使用websocket实现一对一聊天功能

梁泰
2023-12-01

uni-app的API一部分是基于ECMA的还有一部分是由uniapp自己扩展的api,
其中websocket就是由uniapp自己扩展的api。
从创建一个 WebSocket (opens new window)连接,监听,发送等都跟传统的websocket有点不同。
这里跟大家分享的是web端进行通信的样例,其中小程序端通信需要按照官网的协议样例进行wss://www.example.com/socket,而web端则是ws://www.example.com/socket。

首先建立websocket连接

uni.connectSocket({
	url: 'wss://www.example.com/socket',
	data() {
		return {
			x: '',
			y: ''
		};
	},
	header: {
		'content-type': 'application/json'
	},
	protocols: ['protocol1'],
	method: 'GET'
});

监听WebSocket连接打开事件。

uni.onSocketOpen(function (res) {
  console.log('WebSocket连接已打开!');
});

通过 WebSocket 连接发送数据

 uni.sendSocketMessage({
      data: msg
    });

发送时特别注意数据格式,记得要转换数据格式
我贴一下发送的样例,这是一个方法,我是从真实项目中直接拉出来的

send() {
				if (!this.text) {
					return uni.showToast({
						title: "请输入内容"
					})
				} else {
					if (typeof(WebSocket) == "undefined") {
						console.log("您的浏览器不支持WebSocket");
					} else {
						console.log("您的浏览器支持WebSocket");
						// 组装待发送的消息 json
						// {"from": "zhang", "to": "admin", "text": "聊天文本"}
						let message = {
							from: this.user.username,
							to: this.userText,
							text: this.text
						}
						uni.sendSocketMessage(JSON.stringify(message)); // 将组装好的json发送给服务端,由服务端进行转发
						this.messages.push({
							user: this.user.username,
							text: this.text
						})
						// 构建消息内容,本人消息
						this.createContent(null, this.user.username, this.text)
						this.text = '';
					}
				}
			},

监听WebSocket接受到服务器的消息事件

uni.onSocketMessage(function (res) {
  console.log('收到服务器内容:' + res.data);
});

关闭 WebSocket 连接

uni.onSocketClose(function() {
						console.log("websocket已关闭");
					});

将websocket五大步骤抽离成函数形式,下面是样例代码

init() {  //将连接的操作和判断浏览器是否支持websocket抽离成函数
				// this.user = sessionStorage.getItem("user") ? JSON.parse(sessionStorage.getItem("user")) : {}

				// let username = this.user.username;
				let username = uni.getStorageSync("username");
				let _this = this;
				_this.text1 = "用户" + username + "已进入聊天"
				if (typeof(WebSocket) == "undefined") {    //WebSocket是浏览器自定义的无需多管,记住这个方法即可
					console.log("您的浏览器不支持WebSocket");
				} else {
					console.log("您的浏览器支持WebSocket");
					let socketUrl = "ws://localhost:8080/imserver/" + username;
					if (SocketTask != null) {
						SocketTask.close();
						SocketTask = null;
					}
					// 开启一个websocket服务
					console.log(socketUrl)
					uni.connectSocket({
						url: "ws://localhost:8080/imserver/" + username, //仅为示例,并非真实接口地址。
						complete: () => {

						},
						success(res) {
							console.log(res)
						},
						fail(err) {
							console.log(err)
						}

					});
					// console.log(SocketTask)
					// SocketTask.onOpen(function(res){
					// 	console.log("websockrt已打开")
					// });
					uni.onSocketOpen(function(res) {
						console.log("websockrt已打开")
					})
					//  浏览器端收消息,获得从服务端发送过来的文本消息
					uni.onSocketMessage(function(msg) {
						console.log("收到数据====" + msg.data)
						let data = JSON.parse(msg
							.data
						) // 对收到的json数据进行解析, 类似这样的: {"users": [{"username": "zhang"},{ "username": "admin"}]}
						if (data.users) { // 获取在线人员信息
							_this.users = data.users.filter(user => user.username !==
								username) // 获取当前连接的所有用户信息,并且排除自身,自己不会出现在自己的聊天列表里
						} else {
							// 如果服务器端发送过来的json数据 不包含 users 这个key,那么发送过来的就是聊天文本json数据
							//  // {"from": "zhang", "text": "hello"}
							if (data.from === _this.chatUser) {
								_this.messages.push(data)
								// 构建消息内容
								_this.createContent(data.from, null, data.text)
							}
						}
					});
					//关闭事件
					uni.onSocketClose(function() {
						console.log("websocket已关闭");
					});
					//发生了错误事件
					uni.onSocketError(function() {
						console.log("websocket发生了错误");
					});
				}
			}

服务端编码

配置WebSocketStompConfig,建立config包,在config文件夹新建WebSocketStompConfig类

package com.gpnu.cn.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**

 * 开启WebSocket支持

 * @author zhengkai
 */
@Configuration
public class WebSocketStompConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter()
    {
        return new ServerEndpointExporter();
    }

}

新建utils包,在utils包下新建WebSocket类

package com.gpnu.cn.utils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * WebSocketServer
 *
 * @author zhengkai.blog.csdn.net
 */

@Component
@ServerEndpoint("/websocket/{username}")
@Log4j2
public class WebSocket  {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 在线人数
     */
    public static int onlineNumber = 0;
    /**
     * 以用户的姓名为key,WebSocket为对象保存起来
     */
    private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
    /**
     * 会话
     */
    private Session session;
    /**
     * 用户名称
     */
    private String username;

    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        onlineNumber++;
        logger.info("现在来连接的客户id:" + session.getId() + "用户名:" + username);
        this.username = username;
        this.session = session;
        logger.info("有新连接加入! 当前在线人数" + onlineNumber);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            //先给所有人发送通知,说我上线了
            Map<String, Object> map1 = new HashMap<>();
            map1.put("messageType", 1);
            map1.put("username", username);
            sendMessageAll(JSON.toJSONString(map1), username);

            //把自己的信息加入到map当中去
            clients.put(username, this);
            //给自己发一条消息:告诉自己现在都有谁在线
            Map<String, Object> map2 =new HashMap<>();
            map2.put("messageType", 3);
            //移除掉自己
            Set<String> set = clients.keySet();
            map2.put("onlineUsers", set);
            sendMessageTo(JSON.toJSONString(map2), username);
        } catch (IOException e) {
            logger.info(username + "上线的时候通知所有人发生了错误");
        }


    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("服务端发生了错误" + error.getMessage());
        //error.printStackTrace();
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
        onlineNumber--;
        //webSockets.remove(this);
        clients.remove(username);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String, Object> map1 = new HashMap<>();
            map1.put("messageType", 2);
            map1.put("onlineUsers", clients.keySet());
            map1.put("username", username);
            sendMessageAll(JSON.toJSONString(map1), username);
        } catch (IOException e) {
            logger.info(username + "下线的时候通知所有人发生了错误");
        }
        logger.info("有连接关闭! 当前在线人数" + onlineNumber);
    }

    /**
     * 收到客户端的消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            logger.info("来自客户端消息:" + message + "客户端的id是:" + session.getId());
            JSONObject jsonObject = JSON.parseObject(message);
            String textMessage = jsonObject.getString("message");
            String fromusername = jsonObject.getString("username");
            String tousername = jsonObject.getString("to");
            //如果不是发给所有,那么就发给某一个人
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String, Object> map1 = new HashMap<>();
            map1.put("messageType", 4);
            map1.put("textMessage", textMessage);
            map1.put("fromusername", fromusername);
            if (tousername.equals("All")) {
                map1.put("tousername", "所有人");
                sendMessageAll(JSON.toJSONString(map1), fromusername);
            } else {
                map1.put("tousername", tousername);
                sendMessageTo(JSON.toJSONString(map1), tousername);
            }
        } catch (Exception e) {
            logger.info("发生了错误了");
        }

    }


    public void sendMessageTo(String message, String ToUserName) throws IOException {
        for (WebSocket item : clients.values()) {
            if (item.username.equals(ToUserName)) {
                item.session.getAsyncRemote().sendText(message);
                break;
            }
        }
    }

    public void sendMessageAll(String message, String FromUserName) throws IOException {
        for (WebSocket item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineNumber;
    }

}
 类似资料: