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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:57:19