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.
 
 
 
 
 
 

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