I'm writing a Websocket client in Java, using javax.websocket API, and org.glassfish.tyrus
as the implementation.
Everything usually works, but sometimes, when I'm receiving very large strings, the connection closes with a mysterious 'Illegal UTF-8 Sequence' as the close reason.
log.info("Ws closed cuz: "
+ reason.getCloseCode() + " , "
+ reason.getReasonPhrase() + " , "
+ reason.toString());
Output:
INFO: Ws closed cuz: NOT_CONSISTENT , Illegal UTF-8 Sequence ,
CloseReason[1007,Illegal UTF-8 Sequence]
I'm guessing that either the string was too large, or the string contained any characters which aren't UTF-8 compatible.
Is there a way to get any more info on the actual string / packet / frame which causes this issue? Or, if there's a way to tell tyrus to ignore any encoding issues and just pass me the raw string and let me handle it?
If not, is there another java websockets client which does the bare bones work of transmitting the strings over socket and doesn't do any validation, and just lets me handle the responses?
Appreciate any feedback.
The following is just a guess.
(1) On the server side, the large string is split into one text frame and one or more following continuation frames. Technically, the original large string is converted into a byte array and then the byte array is split into multiple sub byte arrays. The sub arrays are set to frames one by one (= Each frame contains one sub byte array).
(2) Although there is no guarantee that each sub byte array is a valid UTF-8 sequence, validity check is performed either on the server side or on the client side. If so, it's a bug of Tyrus.
WebSocketListener
of nv-websocket-client has callback methods in frame granularity such as onFrame
, onTextFrame
, onContinuationFrame
and others (note that onTextMessage
and onTextFrame
are different), so you can examine the byte array of each frame there.
WebSocket websocket = new WebSocketFactory()
.createSocket("ws://...")
.addListener(new WebSocketAdapter() {
@Override
public void onFrame(WebSocket ws, WebSocketFrame frame) {
// If the frame is a text frame with FIN bit cleared, or
// if the frame is a continuation frame.
if ((frame.isTextFrame() && frame.getFin() == false) ||
frame.isContinuationFrame()) {
// The payload of the frame. There is no guarantee
// that this byte array is a valid UTF-8 sequence.
byte[] payload = frame.getPayload();
// Check whether the payload is a valid UTF-8 sequence
// if you want to.
checkPayload(payload);
}
}
})
.connect();
Why don't you use nv-websocket-client to examine what is happening in your WebSocket connection?
Yeah, setting a breakpoint, I saw that a Utf8DecodeException or something was being thrown by Tyrus / Glassfish, but couldn't see the exact frame which caused it. I switched to your library, and everything seems to be working well so far, using onTextMessage. Thanks
Is there a way to set the exception handler for any exceptions that might occur in the WebsocketAdapter methods?
WebSocketListener
has someonXxxError()
methods such asonFrameError
andonSendError
. Among such methods,onError()
is a special one. It is always called before any otheronXxxError()
is called. For example, in the implementation ofrun()
method ofReadingThread
andWritingThread
,Throwable
is caught andonError()
andonUnexpectedError()
are called in this order. So, although there is no means to register a custom exception handler, you can handle all error cases inonError()
. SeeWebSocketError
for possible error cases.where exactly do you specify WebsocketListener?
WebSocketAdapter
is an empty implementation ofWebSocketListener
.