Flutter使用web_socket_channel遇连接提前关闭的HttpException求助
解决Flutter WebSocket连接报错及实时聊天实现方案
问题背景
首次在Flutter项目中使用web_socket_channel:2.4.0包测试WebSocket时,出现Unhandled Exception: HttpException: Connection closed before full header was received错误,同时触发WebSocketChannelException。需求是通过WebSocket持续调用聊天API,实现新消息实时更新界面。
错误信息
D/EGL_emulation( 8164): app_time_stats: avg=20620.87ms min=20620.87ms max=20620.87ms count=1 E/flutter ( 8164): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: HttpException: Connection closed before full header was received, uri = http://192.168.5.104:8080/api/v1/language/get-all-active-languages E/flutter ( 8164): E/flutter ( 8164): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: WebSocketChannelException: WebSocketChannelException: HttpException: Connection closed before full header was received, uri = http://192.168.5.104:8080/api/v1/language/get-all-active-languages E/flutter ( 8164): #0 new IOWebSocketChannel._withoutSocket.<anonymous closure> (package:web_socket_channel/io.dart:119:24) E/flutter ( 8164): #1 Stream.handleError.<anonymous closure> (dart:async/stream.dart:923:16) E/flutter ( 8164): #2 _HandleErrorStream._handleError (dart:async/stream_pipe.dart:269:17) E/flutter ( 8164): #3 _ForwardingStreamSubscription._handleError (dart:async/stream_pipe.dart:157:13) E/flutter ( 8164): #4 _RootZone.runBinaryGuarded (dart:async/zone.dart:1606:10) E/flutter ( 8164): #5 _BufferingStreamSubscription._sendError.sendError (dart:async/stream_impl.dart:358:15) E/flutter ( 8164): #6 _BufferingStreamSubscription._sendError (dart:async/stream_impl.dart:376:7) E/flutter ( 8164): #7 _BufferingStreamSubscription._addError (dart:async/stream_impl.dart:280:7) E/flutter ( 8164): #8 _SyncStreamControllerDispatch._sendError (dart:async/stream_controller.dart:788:19) E/flutter ( 8164): #9 _StreamController._addError (dart:async/stream_controller.dart:666:7) E/flutter ( 8164): #10 _RootZone.runBinaryGuarded (dart:async/zone.dart:1606:10) E/flutter ( 8164): #11 _BufferingStreamSubscription._sendError.sendError (dart:async/stream_impl.dart:358:15) E/flutter ( 8164): #12 _BufferingStreamSubscription._sendError (dart:async/stream_impl.dart:376:7) E/flutter ( 8164): #13 _BufferingStreamSubscription._addError (dart:async/stream_impl.dart:280:7) E/flutter ( 8164): #14 _SyncStreamControllerDispatch._sendError (dart:async/stream_controller.dart:788:19) E/flutter ( 8164): #15 _StreamController._addError (dart:async/stream_controller.dart:666:7) E/flutter ( 8164): #16 new Stream.fromFuture.<anonymous closure> (dart:async/stream.dart:245:18) E/flutter ( 8164): #17 _RootZone.runBinary (dart:async/zone.dart:1666:54) E/flutter ( 8164): #18 _FutureListener.handleError (dart:async/future_impl.dart:171:22) E/flutter ( 8164): #19 Future._propagateToListeners.handleError (dart:async/future_impl.dart:852:47) E/flutter ( 8164): #20 Future._propagateToListeners (dart:async/future_impl.dart:873:13) E/flutter ( 8164): #21 Future._completeError (dart:async/future_impl.dart:649:5) E/flutter ( 8164): #22 Future._asyncCompleteError.<anonymous closure> (dart:async/future_impl.dart:739:7) E/flutter ( 8164): #23 _microtaskLoop (dart:async/schedule_microtask.dart:40:21) E/flutter ( 8164): #24 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
测试代码
final channel = IOWebSocketChannel.connect( Uri.parse( 'ws://192.168.5.104:8080/api/v1/language/get-all-active-languages'), headers: { "Content-Type": "application/json", }, ); streamListener() { channel.stream.listen((message) { print(message); }); } @override void initState() { streamListener(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( body: StreamBuilder( stream: channel.stream, builder: (context, snapshot) { return Text(snapshot.hasData ? '${snapshot.data}' : 'sdadsa'); }, ), ); }
实际聊天API及输出
API地址:{{localUrl}}/chat/get-chat-history/66031ebc40dbc657b9f815e5
{ "success": true, "statusCode": 200, "message": "Operation Successful", "statusMessage": "Success", "data": { "chatHistory": [ { "_id": "6653535805176abed962fc2f", "sender": { "profileImage": { "url": "https://res.cloudinary.com/dc1duf3lc/image/upload/v1714981559/profiles/hhx8wjwo6pcumey8vikj.jpg", "public_id": "profiles/hhx8wjwo6pcumey8vikj", "isUploaded": true }, "_id": "66388ab79b98ca0c2c1c5ca3", "firstName": "test", "lastName": "test" }, "receiver": { "profileImage": { "url": "https://res.cloudinary.com/dc1duf3lc/image/upload/v1711480357/profiles/g8sgdg7fk5gvy7urvwui.jpg", "public_id": "profiles/g8sgdg7fk5gvy7urvwui", "isUploaded": true }, "_id": "66031ebc40dbc657b9f815e5", "firstName": "Test", "lastName": "Test farmer" }, "content": "test message", "createdAt": "2024-05-26T15:20:56.393Z", "updatedAt": "2024-05-26T15:20:56.393Z", "__v": 0 }, { "_id": "6653534005176abed962fc28", "sender": { "profileImage": { "url": "https://res.cloudinary.com/dc1duf3lc/image/upload/v1714981559/profiles/hhx8wjwo6pcumey8vikj.jpg", "public_id": "profiles/hhx8wjwo6pcumey8vikj", "isUploaded": true }, "_id": "66388ab79b98ca0c2c1c5ca3", "firstName": "test", "lastName": "test" }, "receiver": { "profileImage": { "url": "https://res.cloudinary.com/dc1duf3lc/image/upload/v1711480357/profiles/g8sgdg7fk5gvy7urvwui.jpg", "public_id": "profiles/g8sgdg7fk5gvy7urvwui", "isUploaded": true }, "_id": "66031ebc40dbc657b9f815e5", "firstName": "Test", "lastName": "Test farmer" }, "content": "test message", "createdAt": "2024-05-26T15:20:32.971Z", "updatedAt": "2024-05-26T15:20:32.971Z", "__v": 0 } ] } }
解决方案
1. 修正WebSocket端点错误
你当前连接的是普通HTTP接口,WebSocket需要后端提供专门的WS/WSS协议端点,不是HTTP接口。需要和后端确认聊天功能对应的WebSocket路径,格式类似:ws://192.168.5.104:8080/api/v1/chat/connect/66031ebc40dbc657b9f815e5。
2. 优化连接配置与错误处理
- 移除不必要的
Content-Type请求头,WebSocket握手不需要该字段; - 为Stream添加错误监听,避免未捕获异常导致崩溃:
channel.stream.listen( (message) { print(message); // 处理消息更新UI }, onError: (error) { print('WebSocket错误: $error'); // 可选:添加自动重连逻辑 }, onDone: () { print('连接已关闭'); // 可选:添加重连逻辑 }, );
3. 实时聊天完整实现流程
- 初始化历史记录:先通过HTTP接口
{{localUrl}}/chat/get-chat-history/66031ebc40dbc657b9f815e5获取历史聊天记录,初始化界面; - 连接WebSocket:连接后端提供的聊天WebSocket端点,监听新消息推送;
- 更新UI:收到后端推送的新消息后,更新本地聊天列表并刷新界面。
4. 修正后的示例代码
import 'package:flutter/material.dart'; import 'package:web_socket_channel/io.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; class ChatScreen extends StatefulWidget { const ChatScreen({super.key}); @override State<ChatScreen> createState() => _ChatScreenState(); } class _ChatScreenState extends State<ChatScreen> { late WebSocketChannel channel; List<dynamic> chatHistory = []; @override void initState() { super.initState(); // 1. 获取历史聊天记录(需实现HTTP请求逻辑) _fetchChatHistory(); // 2. 连接WebSocket(替换为后端实际WS端点) channel = IOWebSocketChannel.connect( Uri.parse('ws://192.168.5.104:8080/api/v1/chat/connect/66031ebc40dbc657b9f815e5'), ); _listenToWebSocket(); } void _fetchChatHistory() async { // 示例HTTP请求逻辑,需引入http包 // final response = await http.get(Uri.parse('http://192.168.5.104:8080/chat/get-chat-history/66031ebc40dbc657b9f815e5')); // if (response.statusCode == 200) { // setState(() { // chatHistory = jsonDecode(response.body)['data']['chatHistory']; // }); // } } void _listenToWebSocket() { channel.stream.listen( (message) { // 假设后端推送的是新消息JSON字符串,解析后更新列表 setState(() { chatHistory.insert(0, message); // 最新消息插入到列表顶部 }); }, onError: (error) { print('WebSocket错误: $error'); }, onDone: () { print('WebSocket连接关闭'); }, ); } @override void dispose() { channel.sink.close(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('聊天')), body: ListView.builder( reverse: true, itemCount: chatHistory.length, itemBuilder: (context, index) { final message = chatHistory[index]; return ListTile( title: Text(message['content']), subtitle: Text(message['sender']['firstName']), ); }, ), ); } }
5. 后端配合要求
- 确保WebSocket服务正常运行,支持按聊天ID建立连接;
- 新消息产生时,主动推送给对应聊天的在线客户端;
- 实现WebSocket握手、心跳检测、断开重连等基础逻辑。
内容的提问来源于stack exchange,提问作者Ahmed Raza
相关产品推荐
相关产品推荐

