项目中需要使用即时通讯模块,决定采用socket.io作为服务端模块,客户端采用socket.io-client-java。在开发过程中,测试环境均正常,但发布到服务器上时客户端却报告如下异常:
null,parser error
io.socket.engineio.client.EngineIOException: server error
at io.socket.engineio.client.Socket.onPacket(Socket.java:507)
at io.socket.engineio.client.Socket.access$1000(Socket.java:31)
at io.socket.engineio.client.Socket$5.call(Socket.java:313)
at io.socket.emitter.Emitter.emit(Emitter.java:117)
at io.socket.engineio.client.Transport.onPacket(Transport.java:134)
at io.socket.engineio.client.transports.Polling.access$700(Polling.java:17)
at io.socket.engineio.client.transports.Polling$2.call(Polling.java:124)
at io.socket.engineio.parser.Parser.decodePayload(Parser.java:184)
at io.socket.engineio.client.transports.Polling._onData(Polling.java:132)
at io.socket.engineio.client.transports.Polling.onData(Polling.java:101)
at io.socket.engineio.client.transports.PollingXHR$5$1.run(PollingXHR.java:109)
at io.socket.thread.EventThread$2.run(EventThread.java:80)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
EVENT_ERROR:[io.socket.engineio.client.EngineIOException: server error]
同时发现当返回数据中不包含中文时一切都正常。一开始以为是服务环境有问题,捣腾了好久,什么都改的跟测试环境一样,还是无法解决。于是决定调试客户端,发现服务器数据其实是返回给了客户端(早点打开客户端日志,也就没前面的瞎倒腾的了)在源码中发现在此处跑出了异常:
public static void decodePayload(String data, DecodePayloadCallback<String> callback) {
if (data == null || data.length() == 0) {
callback.call(err, 0, 1);
return;
}
StringBuilder length = new StringBuilder();
for (int i = 0, l = data.length(); i < l; i++) {
char chr = data.charAt(i);
if (':' != chr) {
length.append(chr);
} else {
int n;
try {
n = Integer.parseInt(length.toString());
} catch (NumberFormatException e) {
callback.call(err, 0, 1);
return;
}
String msg;
try {
msg = data.substring(i + 1, i + 1 + n);
} catch (IndexOutOfBoundsException e) {
callback.call(err, 0, 1);
return;
}
if (msg.length() != 0) {
Packet<String> packet = decodePacket(msg, true);//就是这句话抛出了 UTF8Exception(INVALID_CONTINUATION_BYTE)异常
if (err.type.equals(packet.type) && err.data.equals(packet.data)) {
callback.call(err, 0, 1);
return;
}
boolean ret = callback.call(packet, i + n, l);
if (!ret) return;
}
i += n;
length = new StringBuilder();
}
}
if (length.length() > 0) {
callback.call(err, 0, 1);
}
}
decodePacket方法如下
public static Packet<String> decodePacket(String data, boolean utf8decode) {
int type;
try {
type = Character.getNumericValue(data.charAt(0));
} catch (IndexOutOfBoundsException e) {
type = -1;
}
if (utf8decode) {
try {
data = UTF8.decode(data);
} catch (UTF8Exception e) {
return err;
}
}
if (type < 0 || type >= packetslist.size()) {
return err;
}
if (data.length() > 1) {
return new Packet<String>(packetslist.get(type), data.substring(1));
} else {
return new Packet<String>(packetslist.get(type));
}
}
通过简单分析了源码发现socket-client-java底层采用了websocket(依赖okhttp),PollingXHR(简单的理解为一种轮询),在websocket这条处理线路上decodePacket(String data, boolean utf8decode) 中的utf8decode为false,在polling上却是true。
调试过程发现在测试环境时数据居然走的是websocket,正式环境数据却是从polling过来,发现数据不需要UTF.decode(data)已经是正确,一解码反而产生了错误,于是下载源码修改utf8decode值为false,问题解决。
虽然问题好像暂时解决但还是有如下疑问:
有时间再认真看看源码吧!