|
|
@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:bot_toast/bot_toast.dart';
|
|
|
|
|
|
|
|
import 'package:power_logger/power_logger.dart';
|
|
|
|
|
|
|
|
import 'package:web_socket_channel/io.dart';
|
|
|
|
|
|
|
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const String baseUri = 'wss://test.kaidalai.cn/websocket/app';
|
|
|
|
|
|
|
|
enum SOCKETSTATUS {
|
|
|
|
|
|
|
|
CONNECTED, //已连接
|
|
|
|
|
|
|
|
BREAKOFF, //已断开
|
|
|
|
|
|
|
|
CLOSED, //已关闭
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WebSocketUtil {
|
|
|
|
|
|
|
|
static final WebSocketUtil _socket = WebSocketUtil._();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//内部构造函数
|
|
|
|
|
|
|
|
WebSocketUtil._();
|
|
|
|
|
|
|
|
//单例模式
|
|
|
|
|
|
|
|
factory WebSocketUtil() => _socket;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IOWebSocketChannel? _webSocket;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///用户设置不同的服务器地址
|
|
|
|
|
|
|
|
String _user = 'admin';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///连接状态
|
|
|
|
|
|
|
|
SOCKETSTATUS _socketStatus = SOCKETSTATUS.CLOSED;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///心跳计时器;
|
|
|
|
|
|
|
|
Timer? _heartTimer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///心跳间隔
|
|
|
|
|
|
|
|
Duration _heartDuration = Duration(seconds: 30);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///重连计数器
|
|
|
|
|
|
|
|
int _reconnectCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///重连最大次数
|
|
|
|
|
|
|
|
int _reconnectTimes = 30;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///重连计时器
|
|
|
|
|
|
|
|
Timer? _reconnectTimer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///连接错误回调
|
|
|
|
|
|
|
|
Function(dynamic e)? onError;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///开启回调
|
|
|
|
|
|
|
|
Function? onStart;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///接收消息回调
|
|
|
|
|
|
|
|
Function(String message)? onReceiveMes;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///关闭连接回调;
|
|
|
|
|
|
|
|
Function? onClosed;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///控制台输出
|
|
|
|
|
|
|
|
bool _consolePrint = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///注册websocket
|
|
|
|
|
|
|
|
void initWebSocket(
|
|
|
|
|
|
|
|
{Duration? heartDuration,
|
|
|
|
|
|
|
|
bool? consolePrint,
|
|
|
|
|
|
|
|
Function? onStart,
|
|
|
|
|
|
|
|
Function(String message)? onReceiveMes,
|
|
|
|
|
|
|
|
Function? onClosed,
|
|
|
|
|
|
|
|
Function(dynamic e)? onError}) {
|
|
|
|
|
|
|
|
this.onStart = onStart;
|
|
|
|
|
|
|
|
this.onReceiveMes = onReceiveMes;
|
|
|
|
|
|
|
|
this.onClosed = onClosed;
|
|
|
|
|
|
|
|
this.onError = onError;
|
|
|
|
|
|
|
|
if (consolePrint != null) {
|
|
|
|
|
|
|
|
this._consolePrint = consolePrint;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (heartDuration != null) {
|
|
|
|
|
|
|
|
this._heartDuration = heartDuration;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
print('——————————webSocket init ——————————');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///设置用户
|
|
|
|
|
|
|
|
void setUser(String user) {
|
|
|
|
|
|
|
|
this._user = user;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///开启websocket
|
|
|
|
|
|
|
|
void startWebSocket() {
|
|
|
|
|
|
|
|
closeWebSocket();
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
_webSocket = IOWebSocketChannel.connect(Uri.parse('$baseUri/$_user'));
|
|
|
|
|
|
|
|
print('webSocket已连接服务器:$baseUri/$_user');
|
|
|
|
|
|
|
|
_socketStatus = SOCKETSTATUS.CONNECTED;
|
|
|
|
|
|
|
|
endReconnect();
|
|
|
|
|
|
|
|
onStart?.call();
|
|
|
|
|
|
|
|
_webSocket!.stream.listen(
|
|
|
|
|
|
|
|
(event) => webSocketReceiveMessage(event as String),
|
|
|
|
|
|
|
|
onError: webSocketOnError,
|
|
|
|
|
|
|
|
onDone: webSocketClosed);
|
|
|
|
|
|
|
|
initHeartBeat();
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
BotToast.showText(text: 'webSocket连接失败');
|
|
|
|
|
|
|
|
onError?.call(e);
|
|
|
|
|
|
|
|
LoggerData.addData(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//接收消息回调
|
|
|
|
|
|
|
|
webSocketReceiveMessage(message) {
|
|
|
|
|
|
|
|
if (message == '心跳正常') {
|
|
|
|
|
|
|
|
_dPrint('心跳正常————————${DateTime.now()}');
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
onReceiveMes?.call(message);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//关闭连接回调
|
|
|
|
|
|
|
|
webSocketClosed() {
|
|
|
|
|
|
|
|
closeWebSocket();
|
|
|
|
|
|
|
|
onClosed?.call();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//连接出错回调
|
|
|
|
|
|
|
|
webSocketOnError(e) {
|
|
|
|
|
|
|
|
WebSocketChannelException ex = e;
|
|
|
|
|
|
|
|
_socketStatus = SOCKETSTATUS.BREAKOFF;
|
|
|
|
|
|
|
|
onError?.call(ex.message);
|
|
|
|
|
|
|
|
print('——————连接断开,开始重连');
|
|
|
|
|
|
|
|
startReconnect();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//启动重连计时
|
|
|
|
|
|
|
|
void startReconnect() {
|
|
|
|
|
|
|
|
endReconnect();
|
|
|
|
|
|
|
|
_reconnectTimer = Timer.periodic(Duration(milliseconds: 5000), (timer) {
|
|
|
|
|
|
|
|
_reconnectCount++;
|
|
|
|
|
|
|
|
print('——————第${_reconnectCount}次重连');
|
|
|
|
|
|
|
|
startWebSocket();
|
|
|
|
|
|
|
|
if (_reconnectCount >= _reconnectTimes) {
|
|
|
|
|
|
|
|
print('——————重连失败');
|
|
|
|
|
|
|
|
closeWebSocket();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//重置重连计时
|
|
|
|
|
|
|
|
void endReconnect() {
|
|
|
|
|
|
|
|
_reconnectTimer?.cancel();
|
|
|
|
|
|
|
|
_reconnectTimer = null;
|
|
|
|
|
|
|
|
_reconnectCount = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///初始化心跳
|
|
|
|
|
|
|
|
void initHeartBeat() {
|
|
|
|
|
|
|
|
destoryHeart();
|
|
|
|
|
|
|
|
_heartTimer = Timer.periodic(_heartDuration, (timer) {
|
|
|
|
|
|
|
|
sentHeart();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//发送心跳
|
|
|
|
|
|
|
|
void sentHeart() {
|
|
|
|
|
|
|
|
sendMessage('heartbeat');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///销毁心跳
|
|
|
|
|
|
|
|
void destoryHeart() {
|
|
|
|
|
|
|
|
_heartTimer?.cancel();
|
|
|
|
|
|
|
|
_heartTimer = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///关闭websocket
|
|
|
|
|
|
|
|
void closeWebSocket() {
|
|
|
|
|
|
|
|
if (_webSocket != null) {
|
|
|
|
|
|
|
|
_webSocket!.sink.close();
|
|
|
|
|
|
|
|
print('——————websocket连接已关闭');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
endReconnect();
|
|
|
|
|
|
|
|
destoryHeart();
|
|
|
|
|
|
|
|
_socketStatus = SOCKETSTATUS.CLOSED;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///向websocket服务器发送消息
|
|
|
|
|
|
|
|
void sendMessage(message) {
|
|
|
|
|
|
|
|
if (_webSocket != null) {
|
|
|
|
|
|
|
|
switch (_socketStatus) {
|
|
|
|
|
|
|
|
case SOCKETSTATUS.CONNECTED:
|
|
|
|
|
|
|
|
_dPrint('发送中:' + message);
|
|
|
|
|
|
|
|
_webSocket!.sink.add(message);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCKETSTATUS.CLOSED:
|
|
|
|
|
|
|
|
print('连接已关闭');
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCKETSTATUS.BREAKOFF:
|
|
|
|
|
|
|
|
print('发送失败');
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//封装print
|
|
|
|
|
|
|
|
void _dPrint(dynamic data) {
|
|
|
|
|
|
|
|
if (this._consolePrint) {
|
|
|
|
|
|
|
|
print(data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|