websocket使用sendObject产生的问题

2022-11-02 16:31:10 浏览数 (1)

1. 问题

当在使用WebSocket服务端向客户端发送消息时使用sendObject的方式传递信息出现了以下这个错误

代码语言:javascript复制
javax.websocket.EncodeException: No encoder specified for object of class

2. 原因

这个错误的原因是当我们使用sendObject方式传递信息时需要指定对应的编码器对传递信息进行编码,编码器的设置就在@ServerEndpoint注解上。

原先的@ServerEndpoint注解内容:

代码语言:javascript复制
@ServerEndpoint("/api/websocket/client/{clientId}")

只设定了默认的value值,编码器需要加入encoders属性,如下:

代码语言:javascript复制
@ServerEndpoint(value = "/api/websocket/client/{clientId}",encoders = {ServerEncoder.class})

3. 解决方案

@ServerEndpoint注解上加入指定编码器类:

代码语言:javascript复制
@ServerEndpoint(value = "/api/websocket/client/{clientId}",encoders = {ServerEncoder.class})

encoders的属性是个数组,所以可以指定多个编码器编码器的泛型需要指定,如果发送消息的实体结构没有适合的编码器则会报上面的错误。

3.1 HashMap编码类

这是一个HashMap的编码类,将HashMap转为了序列化后的JSON字符串,核心就是encode方法,只需要返回Object序列化后的json字符串就行,这里使用的fastjson,也可以自定义其他序列化工具来进行序列化操作。

代码语言:javascript复制
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import java.util.HashMap;

/**
 * @author YUSHENGDADA
 * @title: ServerEncoder
 * @projectName v2_lab
 * @description: WebSocket编码器
 * @date 2022/8/22 0022上午 11:42
 */
public class HashMapEncoder implements Encoder.Text<HashMap> {
    private static final Logger log = LoggerFactory.getLogger(HashMapEncoder.class);

    /**
     * 这里的参数 hashMap 要和  Encoder.Text<T>保持一致
     * @param hashMap
     * @return
     * @throws EncodeException
     */
    @Override
    public String encode(HashMap hashMap) throws EncodeException {
        /*
         * 这里是重点,只需要返回Object序列化后的json字符串就行
         * 你也可以使用gosn,fastJson来序列化。
         * 这里我使用fastjson
         */
        try {
            return JSONObject.toJSONString(hashMap);
        }catch (Exception e){
            log.info("ServerEncoder编码异常:{}",e.getMessage());
        }
        return null;
    }

    @Override
    public void init(EndpointConfig endpointConfig) {
        //可忽略
    }

    @Override
    public void destroy() {
        //可忽略
    }
}
3.2 实体编码类

在应用场景中直接使用HashMap传递参数还是不太优雅不好维护,所以这里再提供一个实体编码类,消息传递实体可以固定,业务实体作为其中data数据的内容

3.2.1 消息实体
代码语言:javascript复制
@Data
public class BaseInfoModel<T> {

    private String code;
    private String msg;
    private T data;
    
    public static <T> BaseResponseMessage success(T data) {
        BaseResponseMessage baseResponseMessage = new BaseResponseMessage();
        baseResponseMessage.code = "0";
        baseResponseMessage.msg = "成功";
        baseResponseMessage.data = data;
        return baseResponseMessage;
    }
}
3.2.2 实体编码类
代码语言:javascript复制
import com.an.websocket.model.client.BaseResponseMessage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;

import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

/**
 * @author YUSHENGDADA
 * @title: BaseModelEncoder
 * @projectName v2_lab
 * @description: 实体编码器
 * @date 2022/8/22 0022下午 14:15
 */
public class BaseModelEncoder  implements Encoder.Text<BaseResponseMessage> {
    @Override
    public String encode(BaseResponseMessage baseResponseMessage) throws EncodeException {
        try {
            JsonMapper jsonMapper = new JsonMapper();
            return jsonMapper.writeValueAsString(baseResponseMessage);

        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void init(EndpointConfig endpointConfig) {

    }

    @Override
    public void destroy() {

    }
}
3.2.3 注解配置

实体跟实体编码器配置好后加入到注解的属性上,因为是数组直接HashMap的编码器后加入即可。

代码如下:

代码语言:javascript复制
@ServerEndpoint(value = "/api/websocket/client/{clientId}",encoders = {HashMapEncoder.class, BaseModelEncoder.class})

发送消息的代码

代码语言:javascript复制
@OnMessage
public void onMessage(String message, Session session,@PathParam("clientId") String clientId){
    UserMessageModel userMessageModel = JSONObject.parseObject(message, UserMessageModel.class);
    log.info("客户端:{} 发送到客户端:{},消息内容:{}",clientId,userMessageModel.getAcceptId(),userMessageModel.getMessage());
    webSocketClientMap.get(userMessageModel.getAcceptId()).sendMessage(BaseResponseMessage.success(userMessageModel));
}

private void  sendMessage(Object message){
    try {
        this.infoSession.getBasicRemote().sendObject(message);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (EncodeException e) {
        throw new RuntimeException(e);
    }
}
3.2.4 验证效果

在客户端将收到的消息打印了出来,丑了点,凑合看,结构没问题就行

0 人点赞