Spring脚踩websocket“帧不完整,正在重置输入缓冲区...”

时间:2019-03-01 06:39:20

标签: spring-websocket stomp

我将spring stomp websocket用作msg-push服务器端点。当我使用网络浏览器连接到时,浏览器和服务器都可以正常工作。但是最近,当我使用Cocos creator的应用模拟器连接到服务器时,它始终无法正确连接。服务器上没有错误消息,客户端也没有提示。调试时,我发现模拟器的连接请求只能被握手拦截器拦截,而不能进入ClientInboundChannelInterceptor。

经过研究,我发现了TRACE级别的一些错误日志,如下所示:

2019-03-01 11:34:09.433  INFO [msg-push,76064c86f7a1571f,76064c86f7a1571f,true] 5300 --- [nio-9006-exec-1] c.x.m.s.i.ClientHandshakeInterceptor     : before handshake --> http://172.18.3.39:9005/stomp-ws
2019-03-01 11:34:09.456  INFO [msg-push,76064c86f7a1571f,76064c86f7a1571f,true] 5300 --- [nio-9006-exec-1] c.x.m.s.i.ClientHandshakeInterceptor     : after handshake --> websocket
2019-03-01 11:34:09.487 TRACE [msg-push,,,] 5300 --- [nio-9006-exec-1] o.s.messaging.simp.stomp.StompDecoder    : Incomplete frame, resetting input buffer...
2019-03-01 11:34:09.487 TRACE [msg-push,,,] 5300 --- [nio-9006-exec-1] o.s.w.s.m.StompSubProtocolHandler        : Incomplete STOMP frame content received in session StandardWebSocketSession[id=0, uri=/stomp-ws], bufferSize=95, bufferSizeLimit=65536.

看来org.spring framework.messaging.simp.stomp.StompDecoder类的解码方法存在一些问题。该方法的代码如下:

/**
 * Decode a single STOMP frame from the given {@code buffer} into a {@link Message}.
 */
@Nullable
private Message<byte[]> decodeMessage(ByteBuffer byteBuffer, @Nullable MultiValueMap<String, String> headers) {
    Message<byte[]> decodedMessage = null;
    skipLeadingEol(byteBuffer);

    // Explicit mark/reset access via Buffer base type for compatibility
    // with covariant return type on JDK 9's ByteBuffer...
    Buffer buffer = byteBuffer;
    buffer.mark();

    String command = readCommand(byteBuffer);
    if (command.length() > 0) {
        StompHeaderAccessor headerAccessor = null;
        byte[] payload = null;
        if (byteBuffer.remaining() > 0) {
            StompCommand stompCommand = StompCommand.valueOf(command);
            headerAccessor = StompHeaderAccessor.create(stompCommand);
            initHeaders(headerAccessor);
            readHeaders(byteBuffer, headerAccessor);
            payload = readPayload(byteBuffer, headerAccessor);
        }
        if (payload != null) {
            if (payload.length > 0) {
                StompCommand stompCommand = headerAccessor.getCommand();
                if (stompCommand != null && !stompCommand.isBodyAllowed()) {
                    throw new StompConversionException(stompCommand +
                            " shouldn't have a payload: length=" + payload.length + ", headers=" + headers);
                }
            }
            headerAccessor.updateSimpMessageHeadersFromStompHeaders();
            headerAccessor.setLeaveMutable(true);
            decodedMessage = MessageBuilder.createMessage(payload, headerAccessor.getMessageHeaders());
            if (logger.isTraceEnabled()) {
                logger.trace("Decoded " + headerAccessor.getDetailedLogMessage(payload));
            }
        }
        else {
            logger.trace("Incomplete frame, resetting input buffer...");
            if (headers != null && headerAccessor != null) {
                String name = NativeMessageHeaderAccessor.NATIVE_HEADERS;
                @SuppressWarnings("unchecked")
                MultiValueMap<String, String> map = (MultiValueMap<String, String>) headerAccessor.getHeader(name);
                if (map != null) {
                    headers.putAll(map);
                }
            }
            buffer.reset();
        }
    }
    else {
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.createForHeartbeat();
        initHeaders(headerAccessor);
        headerAccessor.setLeaveMutable(true);
        decodedMessage = MessageBuilder.createMessage(HEARTBEAT_PAYLOAD, headerAccessor.getMessageHeaders());
        if (logger.isTraceEnabled()) {
            logger.trace("Decoded " + headerAccessor.getDetailedLogMessage(null));
        }
    }

    return decodedMessage;
}

我们可以看到有效载荷== null,那么错误日志就会发生:

logger.trace("Incomplete frame, resetting input buffer...");

以上情况将导致无法正确处理方法org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient()所收到的消息帧。由于代码messages = decoder.decode(byteBuffer);将返回一个空列表,因此我收到了日志Incomplete STOMP frame content ...

部分代码如下:

List<Message<byte[]>> messages;
    try {
        ByteBuffer byteBuffer;
        if (webSocketMessage instanceof TextMessage) {
            byteBuffer = ByteBuffer.wrap(((TextMessage) webSocketMessage).asBytes());
        }
        else if (webSocketMessage instanceof BinaryMessage) {
            byteBuffer = ((BinaryMessage) webSocketMessage).getPayload();
        }
        else {
            return;
        }

        BufferingStompDecoder decoder = this.decoders.get(session.getId());
        if (decoder == null) {
            throw new IllegalStateException("No decoder for session id '" + session.getId() + "'");
        }

        messages = decoder.decode(byteBuffer);
        if (messages.isEmpty()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Incomplete STOMP frame content received in session " +
                        session + ", bufferSize=" + decoder.getBufferSize() +
                        ", bufferSizeLimit=" + decoder.getBufferSizeLimit() + ".");
            }
            return;
        }
    }

我不知道为什么消息帧会被解码为空,谁可以帮助我?

补充:

这样的客户端连接代码:

var client = Stomp.client("ws://172.18.3.39:9005/msgpush/stomp-ws/");

var headers = {
    login: 'mylogin',
    passcode: 'mypasscode',
};
client.connect(headers, connectCallback);

开发环境:

springboot 2.0.3.RELEASE

0 个答案:

没有答案