如何解决Dart客户端与Java ServerSocket持久连接的数据收发异常?
问题解决方案
核心问题
你用Dart的WebSocket客户端连接了普通Java TCP Socket服务器,两者协议不匹配:
- Dart的
WebSocketChannel遵循WebSocket协议,连接时会发送握手请求,要求服务器返回符合规范的响应,同时后续数据以WebSocket帧格式传输。 - 你的Java服务器仅处理普通TCP文本流,既没完成WebSocket握手,也无法解析WebSocket帧,导致数据收发失败。
修正后的Java服务器代码
需要先处理WebSocket握手,再解析WebSocket帧来读取数据:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class WebSocketServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("Server started on port 8080"); while (true) { Socket socket = serverSocket.accept(); System.out.println("New client connected"); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); OutputStream out = socket.getOutputStream(); // 处理WebSocket握手 String line; String secWebSocketKey = null; while ((line = in.readLine()) != null) { System.out.println("Received: " + line); if (line.startsWith("Sec-WebSocket-Key:")) { secWebSocketKey = line.split(": ")[1].trim(); } if (line.isEmpty()) { break; } } // 生成握手响应 if (secWebSocketKey != null) { String acceptKey = generateAcceptKey(secWebSocketKey); String response = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: " + acceptKey + "\r\n\r\n"; out.write(response.getBytes()); out.flush(); System.out.println("Handshake completed"); } // 读取WebSocket帧数据 byte[] buffer = new byte[1024]; while (true) { int read = in.read(buffer); if (read == -1) break; // 解析WebSocket帧(简化版,仅处理文本帧) boolean fin = (buffer[0] & 0x80) != 0; int opcode = buffer[0] & 0x0F; if (opcode == 1) { // 文本帧 int payloadLength = buffer[1] & 0x7F; byte[] payload; if (payloadLength == 126) { payloadLength = ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF); payload = new byte[payloadLength]; System.arraycopy(buffer, 4, payload, 0, payloadLength); } else if (payloadLength == 127) { // 超大帧,这里省略处理 continue; } else { payload = new byte[payloadLength]; System.arraycopy(buffer, 2, payload, 0, payloadLength); } String message = new String(payload); System.out.println("Received message: " + message); // 发送响应(文本帧) sendWebSocketMessage(out, "test"); } else if (opcode == 8) { // 关闭帧 System.out.println("Client disconnected"); break; } } socket.close(); in.close(); out.close(); } } private static String generateAcceptKey(String key) { String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; String input = key + guid; try { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hash = md.digest(input.getBytes()); return Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } private static void sendWebSocketMessage(OutputStream out, String message) throws IOException { byte[] messageBytes = message.getBytes(); int length = messageBytes.length; ByteArrayOutputStream frame = new ByteArrayOutputStream(); frame.write(0x81); // FIN + 文本帧 if (length <= 125) { frame.write(length); } else if (length <= 65535) { frame.write(126); frame.write((length >> 8) & 0xFF); frame.write(length & 0xFF); } else { // 超大帧,省略处理 return; } frame.write(messageBytes); out.write(frame.toByteArray()); out.flush(); } }
Dart客户端代码调整
确保连接成功后再发送数据,避免因连接未完成导致的发送失败:
// 关闭旧连接 try { channel?.sink.close(); } catch (_) {} // 建立连接并监听状态 channel = WebSocketChannel.connect(Uri.parse(globals.serverUrl)); // 连接成功后发送消息 channel?.stream.listen( (data) { print('Received from server: $data'); }, onDone: () { print('Connection closed'); }, onError: (error) { print('Error: $error'); }, ); // 延迟确保握手完成,或等待连接就绪后发送 Future.delayed(const Duration(milliseconds: 100), () { channel?.sink.add("Test notification"); });
关键说明
- WebSocket协议需要完成握手流程,服务器必须返回包含
Sec-WebSocket-Accept的响应,否则客户端会抛出异常。 - WebSocket数据以帧为单位传输,不能用普通的
readLine()读取,需要解析帧结构才能获取实际消息内容。 - Java客户端之前能正常工作,是因为它和服务器用的都是普通TCP文本流,而Dart客户端用的是WebSocket协议,两者不兼容。
内容的提问来源于stack exchange,提问作者Konstanius EU
相关产品推荐
相关产品推荐

