超详细—Flutter 带请求头的websocket 测试用例(token验证、心跳发送、超详细日志输出、断线重连)

直接上代码:

/// @Author: Hui_Loading 3080811164@qq.com
/// @Date: 2025-02-22 12:42:38
/// @LastEditors: Hui_Loading 3080811164@qq.com
/// @LastEditTime: 2025-02-23 18:51:43
/// @FilePath: lib/Tools/Websocket/temp.dart
/// @Description: 这是websocket链接测试

import 'dart:async';
import 'dart:io';


import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

import 'dart:convert';

import '../Login_status_review/login_prefs.dart'; // 用于 JSON 编码

class MyApp_socketTest extends StatelessWidget {
  const MyApp_socketTest({super.key});

  @override
  Widget build(BuildContext context) {
    const title = 'WebSocket 连接测试';
    return const MaterialApp(
      title: title,
      home: MyHomePage(title: title),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}


///添加了心跳机制
class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _controller = TextEditingController();
  WebSocketChannel? _channel;
  bool _isConnected = false;
  bool _isConnecting = false;
  late Stream? _broadcastStream;
  int _reconnectAttempt = 0; // 记录重连次数
  Timer? _heartbeatTimer;

  @override
  void initState() {
    super.initState();
    print("[WebSocket] 初始化状态");
    _connectWebSocket();
  }

  Future<void> _connectWebSocket() async {
    if (_isConnecting || _isConnected) {
      print("[WebSocket] 连接已存在或正在连接中,取消重复连接");
      return;
    }

    print("[WebSocket] 开始连接尝试 (第${_reconnectAttempt + 1}次)");
    setState(() => _isConnecting = true);

    try {
      final String token = await LoginPrefs.getToken() ?? 'null';
      print("[WebSocket] 获取到Token: ${token.substring(0, 15)}..."); // 避免打印完整token

      final uri = Uri.parse('ws://xxxxxxxxxxxx?x-token=${token}');
      print("[WebSocket] 正在连接至: ${uri.toString().replaceAll(token, "[TOKEN]")}"); // 隐藏敏感信息

      final startTime = DateTime.now();
      final webSocket = await WebSocket.connect(
        uri.toString(),
        headers: {'x-token': token},
      ).timeout(const Duration(seconds: 10)); // 添加超时控制

      print("[WebSocket] 连接建立成功,耗时: ${DateTime.now().difference(startTime).inMilliseconds}ms");
      print("[WebSocket] 协议版本: ${webSocket.protocol}");

      _channel = IOWebSocketChannel(webSocket);
      _broadcastStream = _channel?.stream.asBroadcastStream();

      setState(() {
        _isConnected = true;
        _isConnecting = false;
        _reconnectAttempt = 0; // 重置重连计数器
      });

      print("[WebSocket] 连接状态更新: connected=$_isConnected");
      print("[WebSocket] 开启消息监听...");

      _broadcastStream?.listen(
            (data) {
          print("[WebSocket] <<< 接收消息 [${DateTime.now().toIso8601String()}]");
          print("消息原始数据: $data");
          try {
            final jsonData = jsonDecode(data);
            print("解析后的JSON: $jsonData");
            if (jsonData['type'] == 'pong') {
              _handleHeartbeatResponse(jsonData); // 处理心跳响应
            } else {
              Fluttertoast.showToast(msg: "收到消息: ${jsonData['msgContent'] ?? data}");
            }
          } catch (e) {
            print("消息解析失败: $e");
          }
        },
        onError: (error) {
          print("[WebSocket] !!! 流错误 [${DateTime.now().toIso8601String()}]");
          print("错误详情: $error");
          if (error is WebSocketException) {
            print("WebSocket异常详情:");
            print("状态码: ${error.hashCode}");
            print("原因: ${error.message}");
          }
          _handleDisconnect();
        },
        onDone: () {
          print("[WebSocket] ### 连接关闭 [${DateTime.now().toIso8601String()}]");
          print("关闭原因: ${webSocket.closeReason}");
          print("关闭代码: ${webSocket.closeCode}");
          _handleDisconnect();
        },
        cancelOnError: true,
      );

      // 启动心跳定时器
      _startHeartbeat();

    } on WebSocketException catch (e) {
      print("[WebSocket] !!! WebSocket异常: ${e.toString()}");
      print("状态码: ${e.hashCode}");
      print("原因: ${e.message}");
      _handleDisconnect();
    } on TimeoutException {
      print("[WebSocket] !!! 连接超时");
      _handleDisconnect();
    } catch (e, stackTrace) {
      print("[WebSocket] !!! 连接异常: ${e.toString()}");
      print("堆栈跟踪: $stackTrace");
      _handleDisconnect();
    } finally {
      if (!_isConnected) {
        _reconnectAttempt++;
        print("[WebSocket] 将在5秒后尝试第$_reconnectAttempt次重连...");
        await Future.delayed(const Duration(seconds: 5));
        _connectWebSocket();
      }
    }
  }

  void _startHeartbeat() {
    if (_heartbeatTimer == null) {
      _heartbeatTimer = Timer.periodic(const Duration(seconds: 10), (timer) {
        if (_isConnected && _channel?.sink != null) {
          final heartbeatPayload = jsonEncode({'type': 'ping'});
          print("[WebSocket] >>> 发送心跳 [${DateTime.now().toIso8601String()}]");
          _channel!.sink.add(heartbeatPayload);
        }
      });
    }
  }

  void _handleHeartbeatResponse(dynamic data) {
    print("[WebSocket] 心跳响应已处理: $data");
  }

  void _handleDisconnect() {
    if (_isConnected || _isConnecting) {
      print("[WebSocket] 处理断开连接...");
      print("当前连接状态: connected=$_isConnected, connecting=$_isConnecting");
      setState(() {
        _isConnected = false;
        _isConnecting = false;
      });
      _channel?.sink.close();
      _channel = null;
    }
    if (_heartbeatTimer != null) {
      _heartbeatTimer?.cancel();
      _heartbeatTimer = null;
    }
    print("[WebSocket] 连接资源已释放");
  }

  void _sendMessage() {
    if (!_isConnected || _channel?.sink == null) {
      print("[WebSocket] 发送失败:连接不可用");
      return;
    }

    final message = _controller.text;
    if (message.isEmpty) {
      print("[WebSocket] 发送失败:消息内容为空");
      return;
    }

    final String _targetId = 'your Id';
    final String _userId = LoginPrefs.getUserID() ?? "null";

    final payload = {
      "targetId": _targetId,
      "source": "user",
      "msgContent": message,
      "userId": _userId,
      "type": "text",
    };

    try {
      print("[WebSocket] >>> 发送消息 [${DateTime.now().toIso8601String()}]");
      print("消息原始内容: $message");
      print("完整Payload: $payload");

      final jsonString = jsonEncode(payload);
      print("序列化后的JSON: $jsonString");

      _channel!.sink.add(jsonString);
      print("[WebSocket] 消息发送成功");
    } catch (e, stackTrace) {
      print("[WebSocket] !!! 消息发送失败");
      print("错误详情: $e");
      print("堆栈跟踪: $stackTrace");
    }

    _controller.clear();
  }

  @override
  void dispose() {
    print("[WebSocket] 组件销毁,释放资源");
    _channel?.sink.close();
    _heartbeatTimer?.cancel();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print("[UI] 构建界面,当前状态: connected=$_isConnected, connecting=$_isConnecting");
    return Scaffold(
      appBar: AppBar(
        title: Row(
          children: [
            Text(widget.title),
            Icon(
              _isConnected ? Icons.cloud_done : Icons.cloud_off,
              color: _isConnected ? Colors.green : Colors.red,
            ),
          ],
        ),
      ),
      body: _isConnected
          ? _buildConnectedUI()
          : Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_isConnecting) CircularProgressIndicator(),
            Text(_isConnecting ? '正在连接中...' : '连接已断开'),
          ],
        ),
      ),
      floatingActionButton: _isConnected
          ? FloatingActionButton(
        onPressed: _sendMessage,
        tooltip: '发送消息',
        child: const Icon(Icons.send),
      )
          : null,
    );
  }

  Widget _buildConnectedUI() {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        children: [
          TextFormField(
            controller: _controller,
            decoration: const InputDecoration(labelText: '发送一条文字消息'),
          ),
          const SizedBox(height: 24),
          Expanded(
            child: StreamBuilder(
              stream: _broadcastStream,
              builder: (context, snapshot) {
                print("[StreamBuilder] 重建,状态: ${snapshot.connectionState}");

                if (snapshot.hasError) {
                  print("[StreamBuilder] 包含错误: ${snapshot.error}");
                  return Center(
                    child: Text('错误: ${snapshot.error}'),
                  );
                }

                if (snapshot.connectionState == ConnectionState.done) {
                  return const Center(child: Text('连接已关闭'));
                }

                return ListView(
                  children: [
                    if (snapshot.hasData && snapshot.data is String) ...[
                      const SizedBox(height: 16),
                      Text('收到消息: ${snapshot.data}'),
                    ],
                  ],
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

如有任何问题,欢迎私信或发送邮件讨论,我的邮箱:

3080811164@qq.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值