Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

445 rivejä
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/LoadingDialog.dart';
  12. import 'package:chat/utils/net_state_util.dart';
  13. import 'package:chat/utils/screen.dart';
  14. import 'package:chat/utils/upload_util.dart';
  15. import 'package:oktoast/oktoast.dart';
  16. import 'package:protobuf/protobuf.dart';
  17. import 'package:web_socket_channel/io.dart';
  18. import 'HttpUtil.dart';
  19. import 'MessageMgr.dart';
  20. import 'TokenMgr.dart';
  21. import 'msgHandler.dart';
  22. const HeartInverval = 30000;
  23. const ReconnectCountLimit = 5; //重连次数限制
  24. const MinMsgmByteLen = 9;
  25. class NetWork {
  26. //私有构造函数
  27. NetWork._internal();
  28. //保存单例
  29. static NetWork _singleton = new NetWork._internal();
  30. //工厂构造函数
  31. factory NetWork() => _singleton;
  32. static IOWebSocketChannel channel;
  33. // enum ChatState {
  34. // connecting, //连接中
  35. // connected, //连接成功
  36. // connectFailed, //连接失败
  37. // logining, //登录中
  38. // logined, //登录成功
  39. // loginFailed //登录失败
  40. // }
  41. bool isLogin = false; //是否登录聊天服务器
  42. int heartOutCount = 0; //超时次数
  43. int reconnectCount = 0; //重连次数
  44. bool isConnecting = false; //避免重复连接
  45. bool isConnect = false;
  46. bool isNormalClose = false;
  47. NetStateBloc _stateBloc = NetStateBloc(); //监控网络状态
  48. Stream<int> get stream => _stateBloc.stream;
  49. Timer heartTimer;
  50. Timer sendTimer;
  51. Timer connectServerTimer; //尝试连接服务器
  52. Timer loginTimer; //登录计时器
  53. // 计时器
  54. Timer iosBgTimer; //登录计时器
  55. bool isInit = false;
  56. String host;
  57. int port;
  58. static String serverIp;
  59. initNetWithServerInfo() {
  60. var data = {
  61. "userid": UserData().basicInfo.userId.toString(),
  62. };
  63. data['sign'] = TokenMgr().getSign(data);
  64. HttpUtil().post('/service/config', data: data).then((res) {
  65. print('获取聊天服务器地址成功');
  66. res = json.decode(res.toString());
  67. if (res == null) {
  68. return;
  69. }
  70. if (res['code'] == 0) {
  71. var config = res['data'][0];
  72. host = config['ip'];
  73. port = config['port'];
  74. print('host : $host port : $port');
  75. // host = '192.168.0.139';
  76. // port = 9091;
  77. serverIp = 'ws://' + host + ':' + port.toString();
  78. if (host == null || host.length == 0) {
  79. // showToast('server url error : $host : $port');
  80. } else {
  81. createWebSocket();
  82. _stateBloc.start();
  83. isInit = true;
  84. }
  85. }
  86. });
  87. }
  88. checkChatState() {
  89. print('检查聊天状态');
  90. if (!isConnect) {
  91. print('没有连接');
  92. _stateBloc.addState(ChatState.connectFailed);
  93. } else if (!isLogin) {
  94. print('没有登录');
  95. _stateBloc.addState(ChatState.loginFailed);
  96. }
  97. }
  98. onData(dynamic message) {
  99. //任何一条消息都作为心跳包处理
  100. receiveHeart();
  101. if (message is String) {
  102. print(message);
  103. } else if (message is Uint8List) {
  104. var buffReader = ByteDataReader(endian: Endian.big);
  105. buffReader.add(message);
  106. int comId = buffReader.readInt16();
  107. int msgId = buffReader.readInt16();
  108. buffReader.readInt8();
  109. int msgLen = buffReader.readUint32();
  110. if (message.length < msgLen) {
  111. print('数据没有接受完毕');
  112. return;
  113. }
  114. int pbLen = msgLen - MinMsgmByteLen;
  115. var content;
  116. if (pbLen > 0) {
  117. content = buffReader.read(pbLen);
  118. }
  119. if (comId == ComId.Heart) {
  120. if (msgId == 3) {
  121. onConnect();
  122. }
  123. } else if (comId == ComId.Login) {
  124. if (msgId == 2) {
  125. //登陆成功回应
  126. var msgContent = LoginAccountRes.fromBuffer(content);
  127. if (msgContent.errorCode == 0) {
  128. print('登录成功');
  129. isLogin = true;
  130. _stateBloc.addState(ChatState.logined);
  131. loginTimer?.cancel();
  132. MsgHandler.flushCacheMsg();
  133. UploadUtil().setUploadUrl(msgContent.httpAddr);
  134. GroupInfoMgr().checkGroupValid();
  135. } else {
  136. _stateBloc.addState(ChatState.loginFailed);
  137. print('登录失败${msgContent.errorCode}');
  138. showToast(I18n.of(LoadingManage.context).fail);
  139. }
  140. } else if (msgId == 3) {
  141. //用户登出,其他设备替换
  142. isLogin = false;
  143. var msgContent = PushUserOutLogin.fromBuffer(content);
  144. print('登出原因:${msgContent.errorCode}');
  145. MessageMgr().emit('Login Out');
  146. }
  147. } else {
  148. MsgHandler.handlerServerMsg(comId, msgId, content);
  149. }
  150. } else {
  151. print("Unknown datatype recieved : " + message.runtimeType.toString());
  152. }
  153. }
  154. onConnect() {
  155. print('连接服务器成功');
  156. connectServerTimer?.cancel();
  157. isNormalClose = false;
  158. isConnect = true;
  159. isConnecting = false;
  160. heartOutCount = 0;
  161. reconnectCount = 0;
  162. _stateBloc.addState(ChatState.connected);
  163. login();
  164. startHeartTimer();
  165. }
  166. reLogin() {
  167. if (isConnect) {
  168. login();
  169. } else {
  170. initNetWithServerInfo();
  171. }
  172. }
  173. void sendMsg(int comId, int msgId, [GeneratedMessage pb]) {
  174. //序列化pb对象
  175. Uint8List pbBody;
  176. int pbLen = 0;
  177. if (pb != null) {
  178. pbBody = pb.writeToBuffer();
  179. pbLen = pbBody.length;
  180. }
  181. //包头部分
  182. var msgLen = pbLen + MinMsgmByteLen; //整体长度
  183. var buffWriter = ByteDataWriter(bufferLength: msgLen, endian: Endian.big);
  184. buffWriter.writeInt16(comId);
  185. buffWriter.writeInt16(msgId);
  186. buffWriter.writeInt8(0x24);
  187. buffWriter.writeUint32(msgLen);
  188. if (pbLen > 0) {
  189. buffWriter.write(pbBody);
  190. }
  191. var finalMsg = buffWriter.toBytes();
  192. //给服务器发消息
  193. try {
  194. if (channel != null && channel.closeCode == null && isConnect) {
  195. // print("发送消息");
  196. channel.sink.add(finalMsg);
  197. } else {
  198. reconnect();
  199. print('channel 为空,无法发送消息');
  200. }
  201. } catch (e) {
  202. print("send捕获异常e=${e.toString()}");
  203. }
  204. }
  205. startHeartTimer() {
  206. heartOutCount = 0;
  207. heartTimer?.cancel();
  208. print('开始心跳计时器');
  209. heartTimer = Timer.periodic(Duration(milliseconds: HeartInverval), (t) {
  210. sendHeart();
  211. });
  212. }
  213. receiveHeart() {
  214. //print('收到心跳${DateTime.now()}');
  215. heartOutCount = 0;
  216. sendTimer?.cancel();
  217. }
  218. sendHeart() {
  219. if (heartOutCount < 3) {
  220. // print('发送心跳${DateTime.now()}');
  221. sendMsg(ComId.Heart, 1);
  222. sendTimer = Timer(Duration(milliseconds: HeartInverval), () {
  223. //超时了,次数加1
  224. heartOutCount += 1;
  225. print('心跳超时$heartOutCount次');
  226. });
  227. } else {
  228. print('心跳次数大于 $heartOutCount');
  229. close();
  230. // createWebSocket();
  231. }
  232. }
  233. close() {
  234. print('清除连接状态');
  235. if (isConnecting) {
  236. print('连接状态,返回');
  237. return;
  238. }
  239. isConnecting = false;
  240. isConnect = false;
  241. isLogin = false;
  242. heartOutCount = 0;
  243. sendTimer?.cancel();
  244. heartTimer?.cancel();
  245. loginTimer?.cancel();
  246. print('清除计时器');
  247. connectServerTimer?.cancel();
  248. if (channel != null) {
  249. channel.sink.close();
  250. channel = null;
  251. }
  252. }
  253. reallySingOut() {
  254. print('用户登出');
  255. //清缓存
  256. isNormalClose = true;
  257. reallyClose();
  258. }
  259. reallyClose() async{
  260. print('清除连接状态');
  261. isNormalClose=true;
  262. isConnecting = false;
  263. isConnect = false;
  264. isLogin = false;
  265. heartOutCount = 0;
  266. sendTimer?.cancel();
  267. heartTimer?.cancel();
  268. loginTimer?.cancel();
  269. print('清除计时器');
  270. connectServerTimer?.cancel();
  271. if (channel != null) {
  272. var result = await channel.sink.close();
  273. print('result: $result');
  274. channel = null;
  275. }
  276. }
  277. checkConnect() {
  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. }