socket
注意:
Http 協(xié)議中雖然可以通過keep-alive機(jī)制使服務(wù)器在響應(yīng)結(jié)束后鏈接會保持一段時間匹颤,但最終還是會斷開惊畏,keep-alive機(jī)制主要是用于避免在同一臺服務(wù)器請求多個資源時頻繁創(chuàng)建鏈接,它本質(zhì)上是支持鏈接復(fù)用的技術(shù),而并非用于實(shí)時通信,讀者需要知道這兩者的區(qū)別。
WebSocket
協(xié)議本質(zhì)上是一個基于tcp
的協(xié)議铺董,它是先通過HTTP協(xié)議發(fā)起一條特殊的 http 請求進(jìn)行握手后,如果服務(wù)端支持WebSocket
協(xié)議禀晓,則會進(jìn)行協(xié)議升級精续。WebSocket
會使用 http 協(xié)議握手后創(chuàng)建的 tcp 鏈接,和 http 協(xié)議不同的是粹懒,WebSocket
的 tcp 鏈接是個長鏈接(不會斷開)重付,所以服務(wù)端與客戶端就可以通過此 TCP 連接進(jìn)行實(shí)時通信。
連接到由websocket.org提供的測試服務(wù)器凫乖。服務(wù)器將簡單地返回我們發(fā)送給它的相同消息确垫!
1、 socket基本使用步驟
1.1 在 pubspec.yaml
包文件中添加包文件名帽芽,并通過 flutter package get
下載包依賴
web_socket_channel: ^1.1.0
1.2 在需要使用 socket
的組件中引入包依賴文件
import 'package:web_socket_channel/io.dart';
1.3 連接到``WebSocket` 服務(wù)器
// 創(chuàng)建一個 WebSocketChannel 連接到 socket 服務(wù)器( ws://echo.websocket.org 是 websocket.org 提供的測試服務(wù)器)
IOWebSocketChannel channel = IOWebSocketChannel.connect('ws://echo.websocket.org');
1.4 監(jiān)聽來自服務(wù)器的消息
WebSocketChannel
提供了一個來自服務(wù)器的消息 Stream
删掀,它是一個異步的基礎(chǔ)類,提供了一種方法來監(jiān)聽來自數(shù)據(jù)源的異步事件导街,StreamBuilder
組件將連接到一個Stream
披泪, 并在每次收到消息時通知Flutter重新構(gòu)建界面。
// StreamBuilder 組件監(jiān)聽消息
new StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
print('socket接收的數(shù)據(jù)格式$snapshot');
print('判斷socket是否有數(shù)據(jù)${snapshot.hasData}');
print('socket數(shù)據(jù)${snapshot.data}');
return new Text(snapshot.hasData ? '${snapshot.data}' : '');
}
)
1.5 將數(shù)據(jù)發(fā)送到服務(wù)器
將數(shù)據(jù)發(fā)送到服務(wù)器搬瑰,測試服務(wù)器會給我們發(fā)送的數(shù)據(jù)
channel.sink.add('測試socket發(fā)送數(shù)據(jù)');
1.6 關(guān)閉 WebSocket
連接
// 組件銷毀的時候關(guān)閉 socket 連接
@override
void dispose(){
super.dispose();
// 關(guān)閉socket連接
channel.sink.close();
}
2付呕、 socket
提取公共文件封裝
2.1 lib
文件夾下新增 socket
文件夾计福,再 socket
文件夾下再建一個 socket.dart
文件
2.2 socket.dart
文件中封裝 socket
// 引入依賴包
import 'package:flutter/cupertino.dart'; // 使用 debugPrint 引入的包
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class SocketMessage{
WebSocketChannel channel;
// 創(chuàng)建一個WebSocketChannel,并連接到WebSocket服務(wù)器
void initSocket(){
print('初始化');
channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');
sendMessage();
// 發(fā)送數(shù)據(jù)給 socket 測試服務(wù)器徽职,為了能夠收到測試服務(wù)器返回的數(shù)據(jù)(正常情況下不需要)
channel.sink.add('發(fā)送測試消息');
}
// 監(jiān)聽socket服務(wù)器的狀態(tài),并對 成功佩厚、異常姆钉、斷開 進(jìn)行處理
void sendMessage() {
print('重連');
channel.stream.listen(onData,onError: onError, onDone: onDone );
}
// socket 鏈接斷開以后重新初始化 socket
void onDone()async{
debugPrint("Socket is closed");
initSocket();
}
// socket err 情況的處理
void onError(err){
debugPrint(err.runtimeType.toString());
WebSocketChannelException ex = err;
debugPrint(ex.message);
}
// 收到服務(wù)端推送的消息event
void onData(event){
print('收到消息:$event');
// channel.sink.add('aaaa'); // 發(fā)送消息給服務(wù)器(服務(wù)端需要再收到消息時回饋消息給服務(wù)端時菜蔬用,否在不需要)
}
// 關(guān)閉WebSocket連接
void dispose() {
print('關(guān)閉');
channel.sink.close();
}
}
2.3 在需要使用的組件中引入 socket.dart
文件
// 引入封裝的 socket.dart 文件
import 'package:項(xiàng)目名/socket/socket.dart';
// 在 initState 中初始化
void initState(){
super.initState();
// 初始化socket
SocketMessage().initSocket();
}
// 在組件銷毀時抄瓦,關(guān)閉WebSocket連接
void dispose(){
super.dispse();
// 關(guān)閉WebSocket連接
SocketMessage().dispose();
}
注意: 此處并沒有對接收到的數(shù)據(jù)進(jìn)行存取處理潮瓶,可以通過 本地存儲
、Provider存儲
钙姊、或者EventBus
處理毯辅,在相應(yīng)的組件中可以直接拿到推送的消息
3、socket
提取公共文件封裝 + EventBus
監(jiān)聽獲取數(shù)據(jù)
3.1 在 pubspec.yaml
包文件中添加包文件名煞额,并通過 flutter package get
下載包依賴
event_bus: ^1.1.1
3.2 在需要使用 eventBus
的組件中引入包依賴文件
import 'package:event_bus/event_bus.dart';
3.3 通常封裝一個 eventBus
事件總線的文件 event_bus.dart
// 引入 eventBus 包文件
import 'package:event_bus/event_bus.dart';
// 創(chuàng)建EventBus
EventBus eventBus = new EventBus();
// event 監(jiān)聽 socket 接收數(shù)據(jù)
class EventSocket{
// 想要接收的數(shù)據(jù)時什么類型的思恐,就定義相同類型的變量
dynamic obj;
EventSocket(this.obj);
}
3.4 socket + eventBus
事件,監(jiān)聽 socket
推送的消息到指定組件中
socket封裝部分:
// 引入包文件
import 'package:flutter/cupertino.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:new_flutter/utils/event_bus.dart';
class SocketMessage{
WebSocketChannel channel;
// 創(chuàng)建一個WebSocketChannel膊毁,并連接到WebSocket服務(wù)器
void initSocket(){
print('初始化 socket');
channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');
sendMessage();
// 發(fā)送數(shù)據(jù)給 socket 測試服務(wù)器胀莹,為了能夠收到測試服務(wù)器返回的數(shù)據(jù)(一般不需要)
channel.sink.add('發(fā)送測試消息');
}
// 將數(shù)據(jù)發(fā)送到服務(wù)器
void sendMessage() {
print('重連');
channel.stream.listen(onData,onError: onError, onDone: onDone );
}
// socket 鏈接斷開以后重新初始化 socket
void onDone()async{
debugPrint("Socket is closed");
initSocket();
}
// socket err 情況的處理
void onError(err){
debugPrint(err.runtimeType.toString());
WebSocketChannelException ex = err;
debugPrint(ex.message);
}
// 收到服務(wù)端推送的消息event
void onData(event){
print('收到消息:$event');
// eventBus 發(fā)送event_bus.dart文件中的定義的事件,參數(shù)傳遞事件定義中變量的類型即可
eventBus.fire(EventSocket({
'a':'b',
'c':'e'
}));
}
// 關(guān)閉WebSocket連接
void dispose() {
print('關(guān)閉');
channel.sink.close();
}
}
eventBus監(jiān)聽婚温、銷毀處理:
// 定義變量接收 eventBus 監(jiān)聽實(shí)例
var eventBusSocket;
@override
void initState(){
super.initState();
// 初始化socket
SocketMessage().initSocket();
// 注冊監(jiān)聽器描焰,訂閱 eventbus
eventBusSocket = eventBus.on<EventSocket>().listen((event) {
// event為 event.obj 即為 eventBus.dart 文件中定義的 EventSocket類中監(jiān)聽的數(shù)據(jù)
print(event.obj);
});
}
@override
void dispose(){
super.dispose();
// 取消訂閱
eventBusSocket.cancel();
}