前言Permalink
Flutter簡介
Flutter 是 Google推出并開源的移動(dòng)應(yīng)用開發(fā)框架,主打跨平臺、高保真、高性能。開發(fā)者可以通過 Dart語言開發(fā) App,一套代碼同時(shí)運(yùn)行在 iOS 和 Android平臺。 Flutter提供了豐富的組件、接口,開發(fā)者可以很快地為 Flutter添加 native擴(kuò)展。同時(shí) Flutter還使用 Native引擎渲染視圖,這無疑能為用戶提供良好的體驗(yàn)。
WebSocket簡介
Http協(xié)議是無狀態(tài)的,只能由客戶端主動(dòng)發(fā)起,服務(wù)端再被動(dòng)響應(yīng),服務(wù)端無法向客戶端主動(dòng)推送內(nèi)容,并且一旦服務(wù)器響應(yīng)結(jié)束,鏈接就會(huì)斷開(見注解部分),所以無法進(jìn)行實(shí)時(shí)通信。WebSocket協(xié)議正是為解決客戶端與服務(wù)端實(shí)時(shí)通信而產(chǎn)生的技術(shù),現(xiàn)在已經(jīng)被主流瀏覽器支持,所以對于Web開發(fā)者來說應(yīng)該比較熟悉了,F(xiàn)lutter也提供了專門的包來支持WebSocket協(xié)議。
注意:Http協(xié)議中雖然可以通過keep-alive機(jī)制使服務(wù)器在響應(yīng)結(jié)束后鏈接會(huì)保持一段時(shí)間,但最終還是會(huì)斷開,keep-alive機(jī)制主要是用于避免在同一臺服務(wù)器請求多個(gè)資源時(shí)頻繁創(chuàng)建鏈接,它本質(zhì)上是支持鏈接復(fù)用的技術(shù),而并非用于實(shí)時(shí)通信,讀者需要知道這兩者的區(qū)別。
WebSocket協(xié)議本質(zhì)上是一個(gè)基于tcp的協(xié)議,它是先通過HTTP協(xié)議發(fā)起一條特殊的http請求進(jìn)行握手后,如果服務(wù)端支持WebSocket協(xié)議,則會(huì)進(jìn)行協(xié)議升級。WebSocket會(huì)使用http協(xié)議握手后創(chuàng)建的tcp鏈接,和http協(xié)議不同的是,WebSocket的tcp鏈接是個(gè)長鏈接(不會(huì)斷開),所以服務(wù)端與客戶端就可以通過此TCP連接進(jìn)行實(shí)時(shí)通信。有關(guān)WebSocket協(xié)議細(xì)節(jié),讀者可以看RFC文檔,下面我們重點(diǎn)看看Flutter中如何使用WebSocket。
話不多說,直接擼代碼Permalink
添加依賴:
web_socket_channel: ^1.1.0 # WebSocket
新建web_socket_utility.dart工具類:
import 'dart:async';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
/// WebSocket地址
const String _SOCKET_URL = 'ws://121.40.165.18:8800';
/// WebSocket狀態(tài)
enum SocketStatus {
SocketStatusConnected, // 已連接
SocketStatusFailed, // 失敗
SocketStatusClosed, // 連接關(guān)閉
}
class WebSocketUtility {
/// 單例對象
static WebSocketUtility _socket;
/// 內(nèi)部構(gòu)造方法,可避免外部暴露構(gòu)造函數(shù),進(jìn)行實(shí)例化
WebSocketUtility._();
/// 獲取單例內(nèi)部方法
factory WebSocketUtility() {
// 只能有一個(gè)實(shí)例
if (_socket == null) {
_socket = new WebSocketUtility._();
}
return _socket;
}
IOWebSocketChannel _webSocket; // WebSocket
SocketStatus _socketStatus; // socket狀態(tài)
Timer _heartBeat; // 心跳定時(shí)器
num _heartTimes = 3000; // 心跳間隔(毫秒)
num _reconnectCount = 60; // 重連次數(shù),默認(rèn)60次
num _reconnectTimes = 0; // 重連計(jì)數(shù)器
Timer _reconnectTimer; // 重連定時(shí)器
Function onError; // 連接錯(cuò)誤回調(diào)
Function onOpen; // 連接開啟回調(diào)
Function onMessage; // 接收消息回調(diào)
/// 初始化WebSocket
void initWebSocket({Function onOpen, Function onMessage, Function onError}) {
this.onOpen = onOpen;
this.onMessage = onMessage;
this.onError = onError;
openSocket();
}
/// 開啟WebSocket連接
void openSocket() {
closeSocket();
_webSocket = IOWebSocketChannel.connect(_SOCKET_URL);
print('WebSocket連接成功: $_SOCKET_URL');
// 連接成功,返回WebSocket實(shí)例
_socketStatus = SocketStatus.SocketStatusConnected;
// 連接成功,重置重連計(jì)數(shù)器
_reconnectTimes = 0;
if (_reconnectTimer != null) {
_reconnectTimer.cancel();
_reconnectTimer = null;
}
onOpen();
// 接收消息
_webSocket.stream.listen((data) => webSocketOnMessage(data),
onError: webSocketOnError, onDone: webSocketOnDone);
}
/// WebSocket接收消息回調(diào)
webSocketOnMessage(data) {
onMessage(data);
}
/// WebSocket關(guān)閉連接回調(diào)
webSocketOnDone() {
print('closed');
reconnect();
}
/// WebSocket連接錯(cuò)誤回調(diào)
webSocketOnError(e) {
WebSocketChannelException ex = e;
_socketStatus = SocketStatus.SocketStatusFailed;
onError(ex.message);
closeSocket();
}
/// 初始化心跳
void initHeartBeat() {
destroyHeartBeat();
_heartBeat =
new Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {
sentHeart();
});
}
/// 心跳
void sentHeart() {
sendMessage('{"module": "HEART_CHECK", "message": "請求心跳"}');
}
/// 銷毀心跳
void destroyHeartBeat() {
if (_heartBeat != null) {
_heartBeat.cancel();
_heartBeat = null;
}
}
/// 關(guān)閉WebSocket
void closeSocket() {
if (_webSocket != null) {
print('WebSocket連接關(guān)閉');
_webSocket.sink.close();
destroyHeartBeat();
_socketStatus = SocketStatus.SocketStatusClosed;
}
}
/// 發(fā)送WebSocket消息
void sendMessage(message) {
if (_webSocket != null) {
switch (_socketStatus) {
case SocketStatus.SocketStatusConnected:
print('發(fā)送中:' + message);
_webSocket.sink.add(message);
break;
case SocketStatus.SocketStatusClosed:
print('連接已關(guān)閉');
break;
case SocketStatus.SocketStatusFailed:
print('發(fā)送失敗');
break;
default:
break;
}
}
}
/// 重連機(jī)制
void reconnect() {
if (_reconnectTimes < _reconnectCount) {
_reconnectTimes++;
_reconnectTimer =
new Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {
openSocket();
});
} else {
if (_reconnectTimer != null) {
print('重連次數(shù)超過最大次數(shù)');
_reconnectTimer.cancel();
_reconnectTimer = null;
}
return;
}
}
}
使用方法Permalink
import 'package:my_app/utils/web_socket_utility.dart';
WebSocketUtility().initWebSocket(onOpen: () {
WebSocketUtility().initHeartBeat();
}, onMessage: (data) {
print(data);
}, onError: (e) {
print(e);
});
轉(zhuǎn)自:https://ricardolsw.github.io/blog/Flutter-WebSocket%E5%B0%81%E8%A3%85-%E5%AE%9E%E7%8E%B0%E5%BF%83%E8%B7%B3-%E9%87%8D%E8%BF%9E%E6%9C%BA%E5%88%B6/文章來源:http://www.zghlxwxcb.cn/news/detail-682664.html
更新dart版本后的代碼:文章來源地址http://www.zghlxwxcb.cn/news/detail-682664.html
import 'dart:async';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:kkview_kuaichuan/config.dart';
/// WebSocket狀態(tài)
enum SocketStatus {
socketStatusConnected, // 已連接
socketStatusFailed, // 失敗
socketStatusClosed, // 連接關(guān)閉
}
class WebSocketUtility {
/// 單例對象
static final WebSocketUtility _socket = WebSocketUtility._internal();
/// 內(nèi)部構(gòu)造方法,可避免外部暴露構(gòu)造函數(shù),進(jìn)行實(shí)例化
WebSocketUtility._internal();
/// 獲取單例內(nèi)部方法
factory WebSocketUtility() {
return _socket;
}
late WebSocketChannel _webSocket; // WebSocket
SocketStatus? _socketStatus; // socket狀態(tài)
Timer? _heartBeat; // 心跳定時(shí)器
final int _heartTimes = 30000; // 心跳間隔(毫秒)
final int _reconnectCount = 2; // 重連次數(shù),默認(rèn)60次
int _reconnectTimes = 0; // 重連計(jì)數(shù)器
Timer? _reconnectTimer; // 重連定時(shí)器
late Function onError; // 連接錯(cuò)誤回調(diào)
late Function onOpen; // 連接開啟回調(diào)
late Function onMessage; // 接收消息回調(diào)
/// 初始化WebSocket
void initWebSocket({required Function onOpen, required Function onMessage, required Function onError}) {
this.onOpen = onOpen;
this.onMessage = onMessage;
this.onError = onError;
openSocket();
}
/// 開啟WebSocket連接
void openSocket() {
// closeSocket();
_webSocket = WebSocketChannel.connect(Uri.parse(SIGNALSERVERURL));
print('WebSocket連接成功: $SIGNALSERVERURL');
// 連接成功,返回WebSocket實(shí)例
_socketStatus = SocketStatus.socketStatusConnected;
// 連接成功,重置重連計(jì)數(shù)器
_reconnectTimes = 0;
if (_reconnectTimer != null) {
_reconnectTimer?.cancel();
_reconnectTimer = null;
}
onOpen();
// 接收消息
_webSocket.stream.listen((data) => webSocketOnMessage(data),
onError: webSocketOnError, onDone: webSocketOnDone);
}
/// WebSocket接收消息回調(diào)
webSocketOnMessage(data) {
onMessage(data);
}
/// WebSocket關(guān)閉連接回調(diào)
webSocketOnDone() {
print('webSocketOnDone closed');
_socketStatus = SocketStatus.socketStatusClosed;
reconnect();
}
/// WebSocket連接錯(cuò)誤回調(diào)
webSocketOnError(e) {
WebSocketChannelException ex = e;
_socketStatus = SocketStatus.socketStatusFailed;
onError(ex.message);
closeSocket();
}
/// 初始化心跳
void initHeartBeat() {
destroyHeartBeat();
_heartBeat =
Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {
sentHeart();
});
}
/// 心跳
void sentHeart() {
sendMessage('{"module": "HEART_CHECK", "message": "請求心跳"}');
}
/// 銷毀心跳
void destroyHeartBeat() {
if (_heartBeat != null) {
_heartBeat?.cancel();
_heartBeat = null;
}
}
/// 關(guān)閉WebSocket
void closeSocket() {
print('WebSocket連接關(guān)閉');
_webSocket.sink.close();
destroyHeartBeat();
_socketStatus = SocketStatus.socketStatusClosed;
}
/// 發(fā)送WebSocket消息
void sendMessage(message) {
switch (_socketStatus) {
case SocketStatus.socketStatusConnected:
print('發(fā)送中:$message');
_webSocket.sink.add(message);
break;
case SocketStatus.socketStatusClosed:
print('連接已關(guān)閉');
break;
case SocketStatus.socketStatusFailed:
print('發(fā)送失敗');
break;
default:
break;
}
}
/// 重連機(jī)制
void reconnect() {
if (_reconnectTimes < _reconnectCount) {
_reconnectTimes++;
_reconnectTimer =
Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {
openSocket();
});
} else {
if (_reconnectTimer != null) {
print('重連次數(shù)超過最大次數(shù)');
_reconnectTimer?.cancel();
_reconnectTimer = null;
}
return;
}
}
get socketStatus => _socketStatus;
get webSocketCloseCode => _webSocket.closeCode;
}
到了這里,關(guān)于Flutter:WebSocket封裝-實(shí)現(xiàn)心跳、重連機(jī)制的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!