Hibok
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

453 行
11 KiB

  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:typed_data';
  4. import 'package:buffer/buffer.dart';
  5. import 'package:chat/data/UserData.dart';
  6. import 'package:chat/data/chat_data_mgr.dart';
  7. import 'package:chat/data/constants.dart';
  8. import 'package:chat/data/group_data_mgr.dart';
  9. import 'package:chat/generated/i18n.dart';
  10. import 'package:chat/proto/all.pbserver.dart';
  11. import 'package:chat/utils/net_state_util.dart';
  12. import 'package:chat/utils/screen.dart';
  13. import 'package:chat/utils/upload_util.dart';
  14. import 'package:oktoast/oktoast.dart';
  15. import 'package:protobuf/protobuf.dart';
  16. import 'package:web_socket_channel/io.dart';
  17. import 'HttpUtil.dart';
  18. import 'MessageMgr.dart';
  19. import 'TokenMgr.dart';
  20. import 'msgHandler.dart';
  21. const HeartInverval = 30000;
  22. const ReconnectCountLimit = 5; //重连次数限制
  23. const MinMsgmByteLen = 9;
  24. class NetWork {
  25. //私有构造函数
  26. NetWork._internal();
  27. //保存单例
  28. static NetWork _singleton = new NetWork._internal();
  29. //工厂构造函数
  30. factory NetWork() => _singleton;
  31. static IOWebSocketChannel channel;
  32. // enum ChatState {
  33. // connecting, //连接中
  34. // connected, //连接成功
  35. // connectFailed, //连接失败
  36. // logining, //登录中
  37. // logined, //登录成功
  38. // loginFailed //登录失败
  39. // }
  40. bool isLogin = false; //是否登录聊天服务器
  41. int heartOutCount = 0; //超时次数
  42. int reconnectCount = 0; //重连次数
  43. bool isConnecting = false; //避免重复连接
  44. bool isConnect = false;
  45. bool isNormalClose = false;
  46. NetStateBloc _stateBloc = NetStateBloc(); //监控网络状态
  47. Stream<int> get stream => _stateBloc.stream;
  48. Timer heartTimer;
  49. Timer sendTimer;
  50. Timer connectServerTimer; //尝试连接服务器
  51. Timer loginTimer; //登录
  52. // 计时器
  53. Timer iosBgTimer; //登录计时器
  54. bool isInit = false;
  55. String host;
  56. int port;
  57. static String serverIp;
  58. initNetWithServerInfo() {
  59. var data = {
  60. "userid": UserData().basicInfo.userId.toString(),
  61. };
  62. data['sign'] = TokenMgr().getSign(data);
  63. HttpUtil().post('/service/config', data: data).then((res) {
  64. print('获取聊天服务器地址成功');
  65. res = json.decode(res.toString());
  66. if (res['code'] == 0) {
  67. var config = res['data'][0];
  68. host = config['ip'];
  69. port = config['port'];
  70. print('host : $host port : $port');
  71. // host = '192.168.0.139';
  72. // port = 9091;
  73. serverIp = 'ws://' + host + ':' + port.toString();
  74. if (host == null || host.length == 0) {
  75. // showToast('server url error : $host : $port');
  76. } else {
  77. createWebSocket();
  78. _stateBloc.start();
  79. isInit = true;
  80. }
  81. }
  82. });
  83. }
  84. checkChatState() {
  85. print('检查聊天状态');
  86. if (!isConnect) {
  87. print('没有连接');
  88. _stateBloc.addState(ChatState.connectFailed);
  89. } else if (!isLogin) {
  90. print('没有登录');
  91. _stateBloc.addState(ChatState.loginFailed);
  92. }
  93. }
  94. onData(dynamic message) {
  95. //任何一条消息都作为心跳包处理
  96. receiveHeart();
  97. if (message is String) {
  98. print(message);
  99. } else if (message is Uint8List) {
  100. var buffReader = ByteDataReader(endian: Endian.big);
  101. buffReader.add(message);
  102. int comId = buffReader.readInt16();
  103. int msgId = buffReader.readInt16();
  104. buffReader.readInt8();
  105. int msgLen = buffReader.readUint32();
  106. if (message.length < msgLen) {
  107. print('数据没有接受完毕');
  108. return;
  109. }
  110. int pbLen = msgLen - MinMsgmByteLen;
  111. var content;
  112. if (pbLen > 0) {
  113. content = buffReader.read(pbLen);
  114. }
  115. if (comId == ComId.Heart) {
  116. if (msgId == 3) {
  117. onConnect();
  118. }
  119. } else if (comId == ComId.Login) {
  120. if (msgId == 2) {
  121. //登陆成功回应
  122. var msgContent = LoginAccountRes.fromBuffer(content);
  123. if (msgContent.errorCode == 0) {
  124. UserData().isTranslateUser =msgContent.isTransHKInter; //是否是翻译人员
  125. MessageMgr().emit(MessageMgr.TRANSLATE_HK_IS_RECEIVED_ORDER,msgContent.isReceipt);//是否开启接单
  126. print('登录成功');
  127. isLogin = true;
  128. _stateBloc.addState(ChatState.logined);
  129. loginTimer?.cancel();
  130. MsgHandler.flushCacheMsg();
  131. UploadUtil().setUploadUrl(msgContent.httpAddr);
  132. GroupInfoMgr().checkGroupValid();
  133. } else {
  134. _stateBloc.addState(ChatState.loginFailed);
  135. print('登录失败${msgContent.errorCode}');
  136. showToast(I18n.of(Constants.getCurrentContext()).fail);
  137. }
  138. } else if (msgId == 3) {
  139. //用户登出,其他设备替换
  140. isLogin = false;
  141. var msgContent = PushUserOutLogin.fromBuffer(content);
  142. print('登出原因:${msgContent.errorCode}');
  143. MessageMgr().emit('Login Out');
  144. }
  145. } else {
  146. MsgHandler.handlerServerMsg(comId, msgId, content);
  147. }
  148. } else {
  149. print("Unknown datatype recieved : " + message.runtimeType.toString());
  150. }
  151. }
  152. onConnect() {
  153. print('连接服务器成功');
  154. connectServerTimer?.cancel();
  155. isNormalClose = false;
  156. isConnect = true;
  157. isConnecting = false;
  158. heartOutCount = 0;
  159. reconnectCount = 0;
  160. _stateBloc.addState(ChatState.connected);
  161. login();
  162. startHeartTimer();
  163. }
  164. reLogin() {
  165. if (isConnect) {
  166. login();
  167. } else {
  168. initNetWithServerInfo();
  169. }
  170. }
  171. void sendMsg(int comId, int msgId, [GeneratedMessage pb]) {
  172. //序列化pb对象
  173. Uint8List pbBody;
  174. int pbLen = 0;
  175. if (pb != null) {
  176. pbBody = pb.writeToBuffer();
  177. pbLen = pbBody.length;
  178. }
  179. //包头部分
  180. var msgLen = pbLen + MinMsgmByteLen; //整体长度
  181. var buffWriter = ByteDataWriter(bufferLength: msgLen, endian: Endian.big);
  182. buffWriter.writeInt16(comId);
  183. buffWriter.writeInt16(msgId);
  184. buffWriter.writeInt8(0x24);
  185. buffWriter.writeUint32(msgLen);
  186. if (pbLen > 0) {
  187. buffWriter.write(pbBody);
  188. }
  189. var finalMsg = buffWriter.toBytes();
  190. //给服务器发消息
  191. try {
  192. if (channel != null && channel.closeCode == null && isConnect) {
  193. // print("发送消息");
  194. channel.sink.add(finalMsg);
  195. } else {
  196. reconnect();
  197. print('channel 为空,无法发送消息');
  198. }
  199. } catch (e) {
  200. print("send捕获异常e=${e.toString()}");
  201. }
  202. }
  203. startHeartTimer() {
  204. heartOutCount = 0;
  205. heartTimer?.cancel();
  206. print('开始心跳计时器');
  207. heartTimer = Timer.periodic(Duration(milliseconds: HeartInverval), (t) {
  208. sendHeart();
  209. });
  210. }
  211. receiveHeart() {
  212. //print('收到心跳${DateTime.now()}');
  213. heartOutCount = 0;
  214. sendTimer?.cancel();
  215. }
  216. sendHeart() {
  217. if (heartOutCount < 3) {
  218. // print('发送心跳${DateTime.now()}');
  219. sendMsg(ComId.Heart, 1);
  220. sendTimer = Timer(Duration(milliseconds: HeartInverval), () {
  221. //超时了,次数加1
  222. heartOutCount += 1;
  223. print('心跳超时$heartOutCount次');
  224. });
  225. } else {
  226. print('心跳次数大于 $heartOutCount');
  227. close();
  228. // createWebSocket();
  229. }
  230. }
  231. close() {
  232. print('清除连接状态');
  233. if (isConnecting) {
  234. print('连接状态,返回');
  235. return;
  236. }
  237. isConnecting = false;
  238. isConnect = false;
  239. isLogin = false;
  240. heartOutCount = 0;
  241. sendTimer?.cancel();
  242. heartTimer?.cancel();
  243. loginTimer?.cancel();
  244. print('清除计时器');
  245. connectServerTimer?.cancel();
  246. if (channel != null) {
  247. channel.sink.close();
  248. channel = null;
  249. }
  250. }
  251. reallyClose() {
  252. if(iosBgTimer==null){
  253. debugPrint('#### ios bg 没有计时器--计时器你启动');
  254. iosBgTimer = Timer(Duration(seconds: 20), () async{
  255. debugPrint('#### ios bg ios后台关闭socket连接');
  256. isNormalClose=true;
  257. isConnecting = false;
  258. isConnect = false;
  259. isLogin = false;
  260. heartOutCount = 0;
  261. sendTimer?.cancel();
  262. heartTimer?.cancel();
  263. loginTimer?.cancel();
  264. connectServerTimer?.cancel();
  265. if (channel != null) {
  266. await channel.sink.close();
  267. channel = null;
  268. }
  269. });
  270. }else{
  271. debugPrint('#### ios bg 已有计时器');
  272. }
  273. }
  274. checkConnect() {
  275. debugPrint(' ios bg 取消计时器');
  276. iosBgTimer?.cancel();
  277. iosBgTimer=null;
  278. if (isInit && !isConnect && !isConnecting) {
  279. reconnect();
  280. } else {
  281. if (!isLogin) {
  282. login();
  283. }
  284. }
  285. }
  286. createWebSocket() {
  287. if (isConnecting) {
  288. return;
  289. }
  290. if (channel != null) {
  291. channel.sink.close();
  292. channel = null;
  293. }
  294. connectServerTimer?.cancel();
  295. print('尝试连接服务器');
  296. isConnecting = true;
  297. _stateBloc.addState(ChatState.connecting);
  298. reconnectCount += 1;
  299. if (reconnectCount > ReconnectCountLimit) {
  300. print('重连数次超出');
  301. _stateBloc.addState(ChatState.connectFailed);
  302. isConnecting = false;
  303. close();
  304. return;
  305. }
  306. try {
  307. channel = IOWebSocketChannel.connect(serverIp);
  308. channel.stream.listen(
  309. onData,
  310. onDone: onDone,
  311. onError: onError,
  312. );
  313. } catch (e) {
  314. // showToast('连接错误${e.toString()}');
  315. isConnecting = false;
  316. //_stateBloc.addState(ChatState.connectFailed);
  317. }
  318. }
  319. login() {
  320. var myId = UserData().basicInfo.userId;
  321. if (myId == null) {
  322. return;
  323. }
  324. var data = {
  325. "userid": UserData().basicInfo.userId,
  326. };
  327. LoginAccountReq req = LoginAccountReq.create();
  328. req.userId = data['userid'];
  329. req.sigin = TokenMgr().getSign(data);
  330. print('发送登陆消息');
  331. loginTimer = Timer(Duration(milliseconds: 3000), () {
  332. _stateBloc.addState(ChatState.loginFailed);
  333. });
  334. sendMsg(ComId.Login, 1, req);
  335. _stateBloc.addState(ChatState.logining);
  336. }
  337. singOut() {
  338. print('用户登出');
  339. //清缓存
  340. ChatDataMgr().logout();
  341. MsgHandler.clear();
  342. isNormalClose = true;
  343. close();
  344. }
  345. onError(error) {
  346. isConnecting = false;
  347. print('ws error $error');
  348. }
  349. reconnect() {
  350. close();
  351. reconnectCount = 0;
  352. if (host == null || host.length == 0) {
  353. initNetWithServerInfo();
  354. } else {
  355. createWebSocket();
  356. }
  357. }
  358. onDone() {
  359. print('ws channel closed${DateTime.now()}');
  360. close();
  361. if (!isNormalClose ) {
  362. print('无法连接聊天服务器,5秒后重新连接');
  363. connectServerTimer = Timer(Duration(seconds: 5), () {
  364. createWebSocket();
  365. });
  366. }
  367. }
  368. }