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

Flutter连接Laravel Reverb遇WebSocket报错:Pong回复超时

解决Flutter连接Laravel Reverb时的"Pong reply not received in time"错误

错误原因

  • 未响应服务器Ping帧:Laravel Reverb基于Pusher协议运行,会定期向客户端发送Ping帧,要求客户端回复Pong。若Flutter客户端未监听并回复这些Ping,服务器会判定连接超时,抛出4201错误。
  • 默认WebSocket通道不兼容Pusher心跳机制:IOWebSocketChannel默认未实现Pusher风格的Ping/Pong自动交互,需要手动处理。
  • 连接闲置超时:客户端长时间无数据发送时,服务器的心跳检测机制会触发超时判定。

修复方案

1. 监听Ping帧并主动回复Pong

修改代码,添加Ping帧监听与Pong回复逻辑:

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

class ReverbTestWidget extends StatefulWidget {
  const ReverbTestWidget({super.key});

  @override
  State<ReverbTestWidget> createState() => _ReverbTestWidgetState();
}

class _ReverbTestWidgetState extends State<ReverbTestWidget> {
  late IOWebSocketChannel channel;

  @override
  void initState() {
    super.initState();
    _initWebSocket();
  }

  void _initWebSocket() {
    channel = IOWebSocketChannel.connect(
      'ws://localhost:8080/app/wdkuwjwxw78hbp6sax2c',
    );

    channel.stream.listen(
      (message) {
        // 处理字符串消息
        if (message is String) {
          print('收到消息: $message');
        }
        // 处理二进制Ping帧
        else if (message is List<int>) {
          final frame = WebSocketFrame.fromBytes(message);
          // WebSocket Ping帧的操作码为9
          if (frame.opcode == 9) {
            // 回复Pong帧(操作码为10)
            channel.sink.add(WebSocketFrame.pong(frame.payload).toBytes());
          }
        }
      },
      onError: (error) {
        print('连接错误: $error');
        _reconnect();
      },
      onDone: () {
        print('连接已关闭');
        _reconnect();
      },
    );

    // 订阅频道
    channel.sink.add(json.encode({
      "event": "pusher:subscribe",
      "data": {"channel": "home"},
    }));
  }

  // 重连逻辑
  void _reconnect() async {
    await Future.delayed(const Duration(seconds: 3));
    if (mounted) {
      setState(() {
        _initWebSocket();
      });
    }
  }

  @override
  void dispose() {
    channel.sink.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Reverb 连接测试')),
      body: StreamBuilder(
        stream: channel.stream,
        builder: (context, snapshot) {
          return Center(
            child: Text(snapshot.hasData ? '消息: ${snapshot.data}' : '等待连接...'),
          );
        },
      ),
    );
  }
}

2. 调整Laravel Reverb超时配置(可选)

若需要延长超时时间,修改config/reverb.php中的参数:

return [
    // 其他配置...
    'options' => [
        'ping_interval' => 60, // 调整Ping发送间隔(单位:秒)
        'ping_timeout' => 30,  // 调整Pong等待超时时间(单位:秒)
    ],
];

修改后重启Reverb服务生效。

3. 手动发送心跳维持连接

若自动Ping/Pong处理存在问题,可定期向服务器发送心跳事件:

// 在_initWebSocket方法中添加定时心跳
Timer.periodic(const Duration(seconds: 30), (timer) {
  if (mounted && channel.sink != null) {
    channel.sink.add(json.encode({
      "event": "pusher:ping",
      "data": {},
    }));
  }
});

关键注意点

  • Pusher协议的Ping/Pong为二进制帧,需监听List<int>类型消息进行处理,而非字符串。
  • 重连逻辑可避免连接意外断开后用户需手动重启应用,提升稳定性。
  • 超时参数调整需平衡资源消耗与连接稳定性,过长间隔可能导致连接被中间件(如Nginx)主动断开。

内容的提问来源于stack exchange,提问作者Solayman Mousa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:38:17