import 'dart:async'; import 'dart:convert'; import 'dart:core'; import 'dart:core'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:huixiang/im/Proto.dart'; import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/im/out/auth.pb.dart'; import 'package:huixiang/im/out/message.pb.dart'; import 'package:huixiang/main.dart'; import 'package:shared_preferences/shared_preferences.dart'; class SocketClient { //47.93.216.24:9090 线上 192.168.10.129:9090 测试 final String ip = kDebugMode ? '192.168.10.129' : '47.93.216.24'; final num port = 9090; Socket _socket; SharedPreferences _shared; Timer timer; bool get heartbeatActive => timer != null && timer.isActive; connect() async { _shared = await SharedPreferences.getInstance(); if (_socket != null) { reconnect(); return; } showDebugToast("socket-connect .... "); await Socket.connect(ip, port).then((value) { debugPrint("socket-connect-$ip"); _socket = value; _socket.listen((data) { print(data); print("socket-listen"); Proto proto = Proto.fromBytes(data); MsgData dataResult = MsgData.fromBuffer(proto.body); print('收到来自:${dataResult.from},消息内容: ${utf8.decode(dataResult.data)} '); Map messageMap = createMessage(userId, utf8.decode(dataResult.data), msgType: dataResult.type.value, userId: dataResult.from); if (callbacks[dataResult.from] != null) { messageMap["state"] = 1; } hxDatabase.insert(messageMap).then((value) { messageMap["id"] = value; Message message = Message.fromJson(messageMap); if (callbacks[dataResult.from] != null) { callbacks[dataResult.from].call(message); /// user conversation callback } callbacks[userId]?.call(message); /// user self conversation list callback }); }, onError: (Object error, StackTrace stackTrace) { debugPrint("socket-listen-error: $error, stackTrace: $stackTrace"); showDebugToast("socket-listen-error: $error, stackTrace: $stackTrace"); reconnect(); }, onDone: () { debugPrint("socket-listen-down: down"); }); authRequest(_shared.getString("token")); heartbeat(); }).catchError((error) { debugPrint("socket-connect-error: $error"); showDebugToast("socket-connect-error: $error"); _socket = null; reconnect(); }); } showDebugToast(text) { if (kDebugMode) { SmartDialog.showToast(text, alignment: Alignment.center); } } Proto heartbeatData() { DateTime dateTime = DateTime.now(); int millisecondsTime = dateTime.millisecondsSinceEpoch; Uint8List data = utf8.encode(jsonEncode({"heartbeat": millisecondsTime})); MsgData msgData = MsgData(from: userId, type: MsgType.TEXT, data: data); final proto2 = Proto(3, 1, msgData.writeToBuffer()); debugPrint("heartbeat: ${dateTime.toString()}"); return proto2; } heartbeat() { cancelTimer(); timer = Timer.periodic(const Duration(milliseconds: 30000), (timer) { if(!checkSocket()) { timer.cancel(); return; } sendHeartBeatAndCheckSocket(); }); } /// send heartBeat and check socket is connected /// send error: reconnect, /// else check Timer.periodic isActive , /// if not active: Timer.periodic send heartBeat sendHeartBeatAndCheckSocket() { final proto2 = heartbeatData(); try { _socket.add(proto2.toBytes()); if (!socketClient.heartbeatActive) { heartbeat(); debugPrint("socket-periodic-send-heart-beat"); } } catch (e) { debugPrint("socket-send-error: ${e.toString()}"); showDebugToast("socket-send-error: ${e.toString()}"); reconnect(); } } cancelTimer() { if (timer != null && timer.isActive) { timer.cancel(); timer = null; } } reconnect() { dispose(); connect(); } Map callbacks = {}; addCallback(String userId, callback) { callbacks[userId] = callback; } removeCallback(String userId) { callbacks.remove(userId); } dispose() { if (_socket != null) { _socket.close(); _socket = null; } } authRequest(String token) { if (!checkSocket()) { return; } final authReq = AuthReq() ..uid = userId ..token = token; final authReqBytes = authReq.writeToBuffer(); final proto = Proto(1, 1, authReqBytes); // 假设 operation 和 seqId 为 1 final protoBytes = proto.toBytes(); _socket.add(protoBytes); } Future sendMessage(String toId, String content) async { Map message = createMessage(toId, content, userId: userId); int id = await hxDatabase.insert(message).catchError((error) { debugPrint("insertMessage: $error"); }); if (!checkSocket()) { hxDatabase.update({"id": id, "state": 3}).catchError((error) { debugPrint("insertMessage: ${error.toString()}"); }); message["id"] = id; message["state"] = 3; return Message.fromJson(message); } message["id"] = id; Uint8List data = utf8.encode(content); MsgData msgData = MsgData(to: toId, from: userId, type: MsgType.TEXT, data: data); final proto2 = Proto(5, 1, msgData.writeToBuffer()); try { _socket.add(proto2.toBytes()); } catch (e) { hxDatabase.update({"id": id, "state": 3}).catchError((error) { debugPrint("insertMessage: ${error.toString()}"); }); debugPrint("socket-send-error: ${e.toString()}"); showDebugToast("socket-send-error: ${e.toString()}"); reconnect(); } debugPrint("sendMessage: ${message["id"]}"); return Message.fromJson(message); } checkSocket() { if (_socket == null) { reconnect(); return false; } return true; } String get userId => _shared.getString("userId"); }