Python连接服务器正常,Kotlin Ktor报Unsupported byte code错误
问题描述
我正在连接一台返回二进制数据的Web服务器,使用Python的Socket代码可以正常接收并输出结果:
from struct import unpack_from import socket HOST = "127.0.0.1" PORT = 12345 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) while True: data = s.recv(7000) print(f"type: {type(data)}, size: {len(data)}, data: {data[0:10]}")
输出结果:
type: <class 'bytes'>, size: 6985, data: b'\x00\x00\x1bE\x00\x00\x00\x00\x10\x00'
但改用Ktor的WebSocket客户端代码连接时,却抛出异常 Exception: Unsupported byte code, first byte is 0x92:
import io.ktor.client.* import io.ktor.client.plugins.websocket.* import io.ktor.http.* import io.ktor.websocket.* import io.ktor.util.* import kotlinx.coroutines.* fun main() { val client = HttpClient { install(WebSockets) } runBlocking { client.webSocket(method = HttpMethod.Get, host = "127.0.0.1", port = 12345) { while(true) { val othersMessage = incoming.receive() as? Frame.Binary ?: continue println(othersMessage.readBytes()) } } } client.close() println("Connection closed. Goodbye!") }
解决思路
核心原因:协议不匹配
Python代码用的是普通TCP Socket直接连接,而Ktor代码用的是WebSocket协议。WebSocket是基于HTTP的升级协议,需要先完成握手(发送包含Upgrade: websocket的HTTP请求),如果你的服务器只是普通的TCP二进制服务,不支持WebSocket握手流程,那么它返回的二进制数据会被Ktor的WebSocket解析器错误地当成WebSocket帧来处理,自然触发解析异常。调试与验证步骤
- 抓包对比数据流:用Wireshark分别捕获两种连接的数据包,查看Ktor是否发送了WebSocket握手请求,以及服务器的响应内容。如果服务器返回的不是WebSocket握手响应,而是直接返回二进制数据,那肯定是协议不匹配。
- 确认服务器类型:明确服务器提供的是普通TCP服务还是WebSocket服务。可以用curl发送WebSocket握手请求测试:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" ws://127.0.0.1:12345,如果返回101 Switching Protocols,说明是WebSocket服务;否则就是普通TCP服务。 - 检查WebSocket帧格式:如果服务器确实是WebSocket服务,那需要验证返回的帧是否符合RFC6455规范。异常中的
0x92是帧的第一个字节,二进制为10010010:最高位是FIN位(1表示单帧完成),低4位Opcode是0x2(二进制帧),但中间的掩码位(第8位)如果是1,说明帧带了掩码,但服务器发送给客户端的帧不应该带掩码,这会导致Ktor解析失败。
修复方案
- 如果是普通TCP服务:放弃WebSocket客户端,改用Kotlin的
java.net.Socket直接建立TCP连接,逻辑和Python代码对齐,示例代码大致如下:import java.net.Socket import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext fun main() = runBlocking { withContext(Dispatchers.IO) { Socket("127.0.0.1", 12345).use { socket -> val inputStream = socket.getInputStream() val buffer = ByteArray(7000) while (true) { val bytesRead = inputStream.read(buffer) if (bytesRead == -1) break val data = buffer.copyOfRange(0, bytesRead) println("type: ${data::class}, size: ${data.size}, data: ${data.take(10).joinToString(",") { "%02x".format(it) }}") } } } } - 如果是WebSocket服务:检查服务器端的WebSocket帧生成逻辑,确保发送给客户端的帧没有添加掩码,并且帧的长度字段、Opcode等都符合RFC6455规范。
- 如果是普通TCP服务:放弃WebSocket客户端,改用Kotlin的
内容的提问来源于stack exchange,提问作者jake wong
相关产品推荐
相关产品推荐

