netty-socketio event事件接收参数时json字符串转java bean报错

socketio依赖及版本

关键代码

Message javaBean

@Data
public class MyMessage {
    private String username;
    private Integer age;
    private String content;
}

server端 代码片段

// 监听事件
@OnEvent("message")
public void onMessage(SocketIOClient client, MyMessage message) {
    log.info("接收消息: sessionId={}, message={}", client.getSessionId(), message.toString());
}

client-js端 代码片段

var msg = {
    username: "icetea",
    age: 18,
    content: "my name is icetea."
};
socket.emit('message', msg);

client-java端 代码片段

MyMessage msg = new MyMessage("icetea", 18, "my name is icetea.");
socket.emit("message", msg); // 直接传javaBean不行
// 或者 socket.emit("message", JSON.toJSONString(msg)); javaBean转为json string也不行

问题描述

使用socketio js客户端时可以正常解析参数,但使用 socketio java客户端发送java bean消息时,netty-socketio无法解析传来的参数,并且将javabean转换成json字符串也会报错

报错详情

ERROR 11236 — [ntLoopGroup-3-3] c.c.socketio.JsonSupportWrapper: Can’t read value: [“message-model”,“MyMessage(username=icetea, age=18, content=my name is icetea.)”] for type: class com.corundumstudio.socketio.protocol.Event

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of life.icetea.test.nettysocketio.domain.MyMessage (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (‘MyMessage(username=icetea, age=19, content=my name is icetea.)’)
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 18]

问题解析

查看netty-socketio官方仓库的issues发现有相同的问题,https://github.com/mrniko/netty-socketio/issues/258, 里面有人说是client传参序列化的问题,并且给出了解决方案。如下:

参考1:https://github.com/mrniko/netty-socketio/issues/258#issuecomment-417607871

//Android client use json parser is "org.json",when emit message,you should like this:
ChatEvent data = new ChatEvent(); 
mSocket.emit("message", new JSONObject(data));

// 该方法中的org.json的JSONObject构造只能传递一个map类型

参考2:https://github.com/mrniko/netty-socketio/issues/258#issuecomment-449638424

//try and convert the object you are trying to send to a string using GSON library
mSocket.emit("message", new JSONObject(new Gson().toJson(data)));

// 该方法使用Gson所以需要引入Gson依赖

参考3: https://github.com/mrniko/netty-socketio/issues/258#issuecomment-465497609

此回答解释了为什么js-client会成功而java-client会失败的原因,因为java-client在序列化参数时存在问题。

参考后得出的解决方法

参考前两个回答,都是将javaBean转成对应依赖(Gson和org.json)的Json对象,因为我的项目使用的是fastJson,而fastJson也有对应的JSONObject,所以照葫芦画瓢,示例如下:

MyMessage msg = new MyMessage("icetea", 18, "my name is icetea.");
socket.emit("message", JSON.toJSON(msg));
// 该方法能够让server正常接收参数

总结

找到解决方法,然后来详细总结一下为什么在socket.emit()方法中直接传递javabean或者传递json的字符串不行,debug找到socketio-java-client底层序列化参数的源码片段如下:

// io.socket.client.Socket#emit(java.lang.String, java.lang.Object[], io.socket.client.Ack)
// ······
// 要发送的参数都放到了org.json.JSONArray对象
JSONArray jsonArgs = new JSONArray();
jsonArgs.put(event);
if (args != null) {
    for (Object arg : args) {
        jsonArgs.put(arg);
    }
}
// 将JSONArray参数封装成packet
Packet<JSONArray> packet = new Packet<JSONArray>(Parser.EVENT, jsonArgs);
// ······

从以上片段看出要发送的参数都放到了org.json.JSONArray对象中并封装成Packet,然后再往下debug找到了将参数encode成String的方法,代码片段如下:

// io.socket.parser.IOParser.Encoder#encodeAsString
// ······
private String encodeAsString(Packet obj) {
    StringBuilder str = new StringBuilder("" + obj.type);
    // ······
    if (obj.data != null) {
        str.append(obj.data);
    }
    // ······
    return str.toString();
}
// 

从以上片段看出,Packet有被解析成一串字符串,且要传送的参数是字符串中的一部分,然后往下debug就是直接send方法。到此参数封装结束。那么从以上分析得出,java-client的参数封装和JSONArray这个类有很大关系,所以可以测试一下JSONArray的toString方法看看展示出的结果是什么样子:

public static void main(String[] args) {
    MyMessage msg = new MyMessage("icetea", 18, "my name is icetea.");

    // 直接put java bean
    JSONArray jsonArray = new JSONArray();
    jsonArray.put("message");
    jsonArray.put(msg);
    System.out.println(jsonArray);
    
    // 转成fastJson的JSONObject
    JSONArray jsonArray1 = new JSONArray();
    jsonArray1.put("message");
    jsonArray1.put(JSON.toJSON(msg));
    System.out.println(jsonArray1);

    // 使用Map测试一下,因为fastJson的JSONObject其实就是一个Map
    JSONArray jsonArray2 = new JSONArray();
    jsonArray2.put("message");
    HashMap<String, Object> map = new HashMap<>();
    map.put("username", "icetea");
    map.put("age", 18);
    map.put("content", "my name is icetea.");
    jsonArray2.put(map);
    System.out.println(jsonArray2);

    // 将javaBean转成json string测试
    JSONArray jsonArray3 = new JSONArray();
    jsonArray3.put("message");
    jsonArray3.put(JSON.toJSONString(msg));
    System.out.println(jsonArray3);
}

// 输出如下:
["message","MyMessage(username=icetea, age=18, content=my name is icetea.)"]
["message",{"age":18,"content":"my name is icetea.","username":"icetea"}]
["message",{"age":18,"content":"my name is icetea.","username":"icetea"}]
["message","{\"age\":18,\"content\":\"my name is icetea.\",\"username\":\"icetea\"}"]

从测试结果可以看出,第二种方式(转成JSONObject)和第三种方式(使用Map封装参数)得出的结果符合json字符串的标准,且能够被netty-socketio反序列化成javaBean。

总结的出另一种方式
// 注意:使用map方式属性必须都是基本类型,不能嵌套有javaBean,否则还会报错,所以推荐使用JSONObject方式
Map<String, Object> msg = new HashMap<>();
msg.put("username", "icetea");
msg.put("age", 18);
msg.put("content", "my name is icetea.");
socket.emit("message", msg);
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty-socketio是一个用于构建实通信应用程序的开源框架。它基于Netty框架,通过使用WebSocket协议来实现异步、高性能的网络通信。Netty-socketio具有以下特点: 1. 强大的异步处理能力:Netty-socketio采用事件驱动的方式处理客户端请求,能够高效地处理大量并发连接。 2. 完善的消息传递机制:Netty-socketio提供了灵活的消息传递方式,支持广播、点对点、房间等不同的消息发送方式,满足不同场景下的通信需求。 3. 多协议支持:Netty-socketio不仅支持WebSocket协议,还支持其他常用的协议,如TCP、HTTP等,便于与现有的系统集成。 4. 可扩展性强:Netty-socketio提供了丰富的拓展接口,用户可以根据自己的需求定制和扩展框架的功能。 5. 易于使用:Netty-socketio提供了简洁的API和丰富的文档,可以快速上手使用,并提供了相应的示例代码,方便开发者学习和理解。 对于客服应用来说,Netty-socketio作为一个实通信框架,可以用于构建在线客服聊天系统。通过使用Netty-socketio,我们可以实现客户与客服人员之间的实消息传递,支持文字、图片、文件等多种类型的消息。客户可以通过网页或移动端应用与客服人员进行沟通,实解决问题,提升用户体验。 Netty-socketio提供了强大的异步处理能力和全双工通信机制,能够处理大量并发连接,并保持连接的稳定性和可靠性。同,它的多协议支持和可扩展性强的特点,使得我们可以根据自己的业务需求进行定制和拓展,满足不同客服场景下的通信需求。 总之,Netty-socketio作为一个强大的实通信框架,为客服应用提供了一种高效、稳定的解决方案,帮助企业构建更好的客服系统,并提升客户的满意度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值