一,程序入口绑定url
void main() async {
...
WebSocketManager().url = XXConfig.webSocketAlphaUrl;
...
runApp(config);
}
二,登录成功后注册WebSocket,比如在TabPage中
@override
void initState() {
super.initState();
...
// 创建链接
if (context.mounted) {
WebSocketManager().connect(context: context);
}
}
@override
void dispose() {
super.dispose();
WebSocketManager().close();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
print("应用回到前台");
WebSocketManager().checkConnect();
} else if (state == AppLifecycleState.paused) {
print("应用退到后台");
} else if (state == AppLifecycleState.detached) {
print("应用退出");
}
}
三,WebSocketManager中实现逻辑
心跳间隔3秒;
断连后重连,重连间隔3秒,次数最大10次,达到最大时倒计时100秒后重连;
发送特定连接前判断连接状态;
只维持一个计时器
class WebSocketManager {
static WebSocketManager? _instance;
static WebSocketChannel? _channel;
String? roomId = "";
String url = "";
///timer
int _countdown = 3; // 心跳计时数
int _reCountdown = 0; // 重连计时数
static Timer? _countdownTimer; // 计时器
/// 重连次数上限
final int MAX_RECONNECT_ATTEMPTS = 10;
/// 重连次数
int _reconnectAttempts = 0;
bool lockReconnect = false;
String? token = "";
BuildContext? context;
factory WebSocketManager() {
_instance ??= WebSocketManager._();
return _instance!;
}
WebSocketManager._();
///判断是否断开,断开重连
void checkConnect() {
if (_channel == null) {
_reconnectAttempts = 0;
reConnect();
}
}
/// 连接
void connect({BuildContext? context}) {
if (context != null) {
///初次链接
this.context = context;
token = UserManager.instance.userInfo?.token;
}
/// 判断是否已经连接
if (_channel != null) {
print("已经连接,请勿重新连接");
return;
}
/// 连接
_channel = WebSocketChannel.connect(
Uri.parse(url),
);
/// 开始计时器
startRecordTime();
_countdown = 3;
/// 收到消息
onMessage.listen((message) {
_reconnectAttempts = 0;
// 处理接收到的消息
handleMessage(message);
}, onError: (error) {
print("连接服务器失败error=$error");
reConnect();
} ,onDone: () {
print("重连");
reConnect();
});
}
/// 重连
void reConnect() {
if (_reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
print("重连次数已达上限");
_reCountdown = 100;
_reconnectAttempts = 0;
return;
}
if (lockReconnect) {
return;
}
lockReconnect = true;
_reCountdown = 3;
}
void reConnectAction() {
// 关闭之前的连接
close();
// 重连次数加一
_reconnectAttempts++;
//重连
connect();
lockReconnect = false;
print("重连次数:${_reconnectAttempts}");
}
// 发送心跳
void sendLink() {
if ((token?.length ?? 0) > 0) {
Map<String, dynamic> map = {
"Authorization": "Bearer $token",
"code": 10000,
"data": {"prescriptionOrderId": roomId ?? ""}
};
_channel?.sink.add(jsonEncode(map));
}
}
// 进入处方编辑
void sendEnterEdit(String id, BuildContext context) {
if (_channel == null) {
connect();
sendEnterEdit(id, context);
return;
}
var token = UserManager.instance.userInfo?.token;
Map<String, dynamic> map = {
"Authorization": "Bearer $token",
"code": 10101,
"data": {"prescriptionOrderId": id}
};
roomId = id;
_channel?.sink.add(jsonEncode(map));
print("-=-=======map${map}");
}
// 离开房间
void sendLeaveEdit( BuildContext context) {
if (_channel == null) {
connect();
sendLeaveEdit(context);
return;
}
if (roomId == "") {
return;
}
var token = UserManager.instance.userInfo?.token;
Map<String, dynamic> map = {
"Authorization": "Bearer $token",
"code": 10102,
"data": {"prescriptionOrderId": roomId}
};
roomId = "";
_channel?.sink.add(jsonEncode(map));
print("-=-=-========-=-=-=-=map: ${map}");
}
// 处理接收到的消息
void handleMessage(dynamic message) {
print(DateTime.now().toString().substring(11, 19)+ "------------------心跳------:" + message.toString());
WebSocketBean bean = WebSocketBean.fromJson(jsonDecode(message));
/// 正常则心跳
if (bean.code == 100001) {
_countdown = 3;
} else if (bean.code == 10000) {
reConnect();
} else if (bean.code == 100002) {
if (context != null) {
if (context!.mounted) {
UserManager.instance.clearCache();
context!.removeAndPush(LoginRouterName.login);
}
}
} else {
XxEventBus.instance().fire(WebSocketNotification(bean));
}
}
///开始心跳
void startRecordTime() {
if (_countdownTimer != null) {
return;
}
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_countdown <= 1) {
sendLink();
} else {
_countdown -= 1;
}
if (_reCountdown == 1) {
reConnectAction();
_reCountdown = 0;
} else if (_reCountdown > 1) {
_reCountdown -= 1;
}
});
}
Stream<dynamic> get onMessage => _channel?.stream ?? Stream.empty();
void close() {
_channel?.sink.close();
_countdownTimer?.cancel();
_countdownTimer = null;
_channel = null;
}
}
四,退出登录时close
WebSocketManager().close();
五,wss:需要过滤签名认证
Android:
在android/app/src/main/res/xml/下创建network_security_config.xml文件
network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">wss://ws_ass_alpha.xiaoroujiankang.com</domain>
<domain includeSubdomains="true">wss://ws_ass.xiaoroujiankang.com</domain>
</domain-config>
</network-security-config>
AndroidManifest.xml文件中添加
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="*****"
android:networkSecurityConfig="@xml/network_security_config"
...










网友评论