You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java WebSocket服务器发送数据时出现无效帧头错误

Android WebSocket服务器发送消息时出现"Invalid frame header"错误

我正在开发一款Android(Java)应用,用于创建WebSocket服务器,连接建立一切正常。但向浏览器WebSocket客户端发送消息时遇到问题:

发送消息的代码:

OutputStream writer = client.getOutputStream();
// FIN OPCODE 1000 0001
writer.write(0x81);

// message length
writer.write(0x5);

writer.write('H');
writer.write('e');
writer.write('l');
writer.write('l');
writer.write('o');
writer.flush();

出现错误:failed: Invalid frame header

用Wireshark抓包后发现数据中多了0x0A(换行符),正确应该发送的帧头及内容是:

  • 0x81 -> FIN & OPCODE (10000001)
  • 0x05 -> 消息长度
  • 0x48 0x65 0x6c 0x6c 0x6f -> Hello

更多相关代码

读取客户端请求头部

private String readClientInputStream() {
    String str = null;
    try {
        InputStream in = client.getInputStream();
        byte[] buffer = new byte[1024];
        int countBytesRead = in.read(buffer);
        if (countBytesRead > 0) {
            str = new String(buffer, 0, countBytesRead);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    return str;
}

发送WebSocket握手响应

public void sendWebSocketHandShake(String str) {
    String[] result = str.split("Sec-WebSocket-Key: ", -1)[1].split("\\n");
    String key = makeKeyAccept(result[0]);
    StringBuilder handshake_str = new StringBuilder();
    handshake_str.append("HTTP/1.1 101 Switching Protocols\n");
    handshake_str.append("Upgrade: websocket\n");
    handshake_str.append("Connection: Upgrade\n");
    handshake_str.append("Sec-WebSocket-Accept: ").append(key).append("\n");
    handshake_str.append("\n");
    try {
        OutputStream out = client.getOutputStream();
        byte[] b = handshake_str.toString().getBytes(StandardCharsets.UTF_8);
        out.write(b, 0, b.length);
        out.flush();

    } catch (IOException e) {
        e.printStackTrace();
    }

}

生成Sec-WebSocket-Accept密钥

public String makeKeyAccept(String key) {
    String _acceptKey = key.trim() + WEBSOCKET_KEY_MAGIC;
    MessageDigest digest = null;
    String base64 = null;
    try {
        digest = MessageDigest.getInstance("SHA-1");
        digest.update(_acceptKey.getBytes(StandardCharsets.UTF_8));
        byte[] encoded = android.util.Base64.encode(digest.digest(), 0, digest.getDigestLength(), android.util.Base64.DEFAULT);
        base64 = new String(encoded, 0, encoded.length, StandardCharsets.UTF_8);
    } catch (Exception ignore){}

    return base64;
}

问题原因与解决方案

核心问题

在makeKeyAccept方法中,使用android.util.Base64.DEFAULT编码会自动添加换行符(0x0A),这个换行符会残留到输出流中。当后续发送WebSocket帧时,残留的换行符会被作为帧的第一个字节发送,导致浏览器收到无效的帧头。

修复步骤

  1. 替换Base64编码模式:将android.util.Base64.DEFAULT改为android.util.Base64.NO_WRAP,避免编码后添加换行符。修改后的代码:

    public String makeKeyAccept(String key) {
        String _acceptKey = key.trim() + WEBSOCKET_KEY_MAGIC;
        MessageDigest digest = null;
        String base64 = null;
        try {
            digest = MessageDigest.getInstance("SHA-1");
            digest.update(_acceptKey.getBytes(StandardCharsets.UTF_8));
            // 使用NO_WRAP模式避免添加换行符
            byte[] encoded = android.util.Base64.encode(digest.digest(), android.util.Base64.NO_WRAP);
            base64 = new String(encoded, StandardCharsets.UTF_8);
        } catch (Exception ignore){}
    
        return base64;
    }
    
  2. 重置连接测试:由于之前的连接输出流中已有残留换行符,需要重新建立WebSocket连接验证修复效果。

额外建议

  • 发送WebSocket帧必须严格遵循RFC 6455规范,确保帧头、长度、负载的格式完全正确。
  • 操作字节流后需确认输出状态,避免残留数据影响后续通信。

内容的提问来源于stack exchange,提问作者obeid salem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 21:48:22