mirror of
https://github.com/Linloir/Simple-TCP-Client.git
synced 2025-12-18 17:28:11 +08:00
Improvements:
- Auto Reconnect
This commit is contained in:
parent
ba59d23484
commit
a758929b46
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 14:02:28
|
* @Date : 2022-10-13 14:02:28
|
||||||
* @LastEditTime : 2022-10-21 23:31:43
|
* @LastEditTime : 2022-10-22 01:20:14
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ class HomeCubit extends Cubit<HomeState> {
|
|||||||
await for(var response in tcpRepository.responseStreamBroadcast) {
|
await for(var response in tcpRepository.responseStreamBroadcast) {
|
||||||
if(response.type == TCPResponseType.fetchMessage) {
|
if(response.type == TCPResponseType.fetchMessage) {
|
||||||
if(response.status == TCPResponseStatus.ok) {
|
if(response.status == TCPResponseStatus.ok) {
|
||||||
response as FetchMessageResponse;
|
// response as FetchMessageResponse;
|
||||||
localServiceRepository.storeMessages(response.messages);
|
// localServiceRepository.storeMessages(response.messages);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 10:56:02
|
* @Date : 2022-10-11 10:56:02
|
||||||
* @LastEditTime : 2022-10-21 22:47:38
|
* @LastEditTime : 2022-10-22 01:22:58
|
||||||
* @Description : Local Service Repository
|
* @Description : Local Service Repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@ -54,6 +57,22 @@ class LocalServiceRepository {
|
|||||||
);
|
);
|
||||||
'''
|
'''
|
||||||
);
|
);
|
||||||
|
await txn.execute(
|
||||||
|
'''
|
||||||
|
create table msgimgs (
|
||||||
|
msgmd5 text primary key,
|
||||||
|
imgmd5 text not null
|
||||||
|
);
|
||||||
|
'''
|
||||||
|
);
|
||||||
|
await txn.execute(
|
||||||
|
'''
|
||||||
|
create table imgs (
|
||||||
|
imgmd5 text primary key,
|
||||||
|
dir text not null
|
||||||
|
);
|
||||||
|
'''
|
||||||
|
);
|
||||||
});
|
});
|
||||||
// await db.execute(
|
// await db.execute(
|
||||||
// '''
|
// '''
|
||||||
@ -105,11 +124,25 @@ class LocalServiceRepository {
|
|||||||
Future<void> storeMessages(List<Message> messages) async {
|
Future<void> storeMessages(List<Message> messages) async {
|
||||||
await _database.transaction((txn) async {
|
await _database.transaction((txn) async {
|
||||||
for(var message in messages) {
|
for(var message in messages) {
|
||||||
await txn.insert(
|
if(message.type == MessageType.image) {
|
||||||
'msgs',
|
//store image first
|
||||||
message.jsonObject,
|
storeImage(
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace
|
image: base64.decode(message.contentDecoded),
|
||||||
);
|
msgmd5: message.contentmd5
|
||||||
|
);
|
||||||
|
await txn.insert(
|
||||||
|
'msgs',
|
||||||
|
message.jsonObject..['content'] = "",
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await txn.insert(
|
||||||
|
'msgs',
|
||||||
|
message.jsonObject,
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -135,6 +168,20 @@ class LocalServiceRepository {
|
|||||||
for(var rawMessage in rawMessages) {
|
for(var rawMessage in rawMessages) {
|
||||||
var message = Message.fromJSONObject(jsonObject: rawMessage);
|
var message = Message.fromJSONObject(jsonObject: rawMessage);
|
||||||
if(message.contentDecoded.toLowerCase().contains(pattern.toLowerCase())) {
|
if(message.contentDecoded.toLowerCase().contains(pattern.toLowerCase())) {
|
||||||
|
//Since history page does not show message
|
||||||
|
//There is no need to fetch message here
|
||||||
|
// if(message.type == MessageType.image) {
|
||||||
|
// var image = await fetchImage(msgmd5: message.contentmd5);
|
||||||
|
// if(image != null) {
|
||||||
|
// alikeMessages.add(message.copyWith(
|
||||||
|
// content: base64.encode(image),
|
||||||
|
// ));
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// //TODO: do something
|
||||||
|
// }
|
||||||
|
// }
|
||||||
alikeMessages.add(message);
|
alikeMessages.add(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,6 +207,8 @@ class LocalServiceRepository {
|
|||||||
messages.add([]);
|
messages.add([]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
//Since message page does not show message
|
||||||
|
//There is no need to fetch message here
|
||||||
messages.add([Message.fromJSONObject(jsonObject: queryResult[0])]);
|
messages.add([Message.fromJSONObject(jsonObject: queryResult[0])]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,7 +234,18 @@ class LocalServiceRepository {
|
|||||||
limit: num,
|
limit: num,
|
||||||
offset: position
|
offset: position
|
||||||
);
|
);
|
||||||
return queryResult.map((e) => Message.fromJSONObject(jsonObject: e)).toList();
|
List<Message> messages = [];
|
||||||
|
for(var result in queryResult) {
|
||||||
|
var message = Message.fromJSONObject(jsonObject: result);
|
||||||
|
if(message.type == MessageType.image) {
|
||||||
|
var image = await fetchImage(msgmd5: message.contentmd5);
|
||||||
|
if(image != null) {
|
||||||
|
message = message.copyWith(content: base64.encode(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File?> findFile({required String filemd5, required String fileName}) async {
|
Future<File?> findFile({required String filemd5, required String fileName}) async {
|
||||||
@ -340,4 +400,59 @@ class LocalServiceRepository {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> storeImage({required List<int> image, required String msgmd5}) async {
|
||||||
|
var md5Output = AccumulatorSink<Digest>();
|
||||||
|
ByteConversionSink md5Input = md5.startChunkedConversion(md5Output);
|
||||||
|
md5Input.add(image);
|
||||||
|
md5Input.close();
|
||||||
|
var imagemd5 = md5Output.events.single.toString();
|
||||||
|
//Write to image library
|
||||||
|
var documentPath = (await getApplicationDocumentsDirectory()).path;
|
||||||
|
await Directory('$documentPath/LChatClient/imgs').create();
|
||||||
|
var permanentFilePath = '$documentPath/LChatClient/imgs/$imagemd5';
|
||||||
|
var imageFile = await File(permanentFilePath).create();
|
||||||
|
imageFile.writeAsBytes(image);
|
||||||
|
await _database.transaction((txn) async {
|
||||||
|
txn.insert(
|
||||||
|
'msgimgs',
|
||||||
|
{
|
||||||
|
'msgmd5': msgmd5,
|
||||||
|
'imgmd5': imagemd5
|
||||||
|
},
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace
|
||||||
|
);
|
||||||
|
txn.insert(
|
||||||
|
'imgs',
|
||||||
|
{
|
||||||
|
'imgmd5': imagemd5,
|
||||||
|
'dir': permanentFilePath
|
||||||
|
},
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<int>?> fetchImage({required String msgmd5}) async {
|
||||||
|
var imageQueryResult = await _database.query(
|
||||||
|
'msgimgs natural join imgs',
|
||||||
|
where: 'msgimgs.msgmd5 = ?',
|
||||||
|
whereArgs: [
|
||||||
|
msgmd5
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'imgs.dir as dir'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
if(imageQueryResult.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var path = imageQueryResult[0]['dir'] as String;
|
||||||
|
var image = File(path);
|
||||||
|
if(!await image.exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var imageContent = await image.readAsBytes();
|
||||||
|
return imageContent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 09:42:05
|
* @Date : 2022-10-11 09:42:05
|
||||||
* @LastEditTime : 2022-10-19 10:41:43
|
* @LastEditTime : 2022-10-22 17:46:28
|
||||||
* @Description : TCP repository
|
* @Description : TCP repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -26,13 +26,25 @@ class TCPRepository {
|
|||||||
required int remotePort
|
required int remotePort
|
||||||
}): _socket = socket, _remoteAddress = remoteAddress, _remotePort = remotePort {
|
}): _socket = socket, _remoteAddress = remoteAddress, _remotePort = remotePort {
|
||||||
Future(() async {
|
Future(() async {
|
||||||
try{
|
while(true) {
|
||||||
await for(var response in _socket) {
|
try{
|
||||||
_pullResponse(response);
|
await for(var response in _socket!) {
|
||||||
await Future.delayed(const Duration(microseconds: 0));
|
_pullResponse(response);
|
||||||
|
await Future.delayed(const Duration(microseconds: 0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch(e) {
|
||||||
|
_socket?.close();
|
||||||
|
_socket = null;
|
||||||
|
while(true) {
|
||||||
|
try{
|
||||||
|
_socket = await Socket.connect(remoteAddress, remotePort);
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {
|
|
||||||
_socket.close();
|
|
||||||
}
|
}
|
||||||
// _responseRawStreamController.close();
|
// _responseRawStreamController.close();
|
||||||
// _payloadPullStreamController.close();
|
// _payloadPullStreamController.close();
|
||||||
@ -42,10 +54,40 @@ class TCPRepository {
|
|||||||
});
|
});
|
||||||
//This future never ends, would that be bothersome?
|
//This future never ends, would that be bothersome?
|
||||||
Future(() async {
|
Future(() async {
|
||||||
await for(var request in _requestStreamController.stream) {
|
TCPRequest? failedRequest;
|
||||||
await _socket.addStream(request.stream);
|
while(true) {
|
||||||
|
try{
|
||||||
|
if(failedRequest != null) {
|
||||||
|
await Future.doWhile(() async {
|
||||||
|
await Future.delayed(const Duration(microseconds: 0));
|
||||||
|
return _socket == null;
|
||||||
|
});
|
||||||
|
await _socket!.addStream(failedRequest.stream);
|
||||||
|
}
|
||||||
|
await for(var request in _requestStreamController.stream) {
|
||||||
|
failedRequest = request;
|
||||||
|
await Future.doWhile(() async {
|
||||||
|
await Future.delayed(const Duration(microseconds: 0));
|
||||||
|
return _socket == null;
|
||||||
|
});
|
||||||
|
await _socket!.addStream(request.stream);
|
||||||
|
failedRequest = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
_socket?.close();
|
||||||
|
_socket = null;
|
||||||
|
while(true) {
|
||||||
|
try{
|
||||||
|
_socket = await Socket.connect(remoteAddress, remotePort);
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).onError((error, stackTrace) {_socket.close();});
|
});
|
||||||
Future(() async {
|
Future(() async {
|
||||||
var responseQueue = StreamQueue(_responseRawStreamController.stream);
|
var responseQueue = StreamQueue(_responseRawStreamController.stream);
|
||||||
var payloadQueue = StreamQueue(_payloadRawStreamController.stream);
|
var payloadQueue = StreamQueue(_payloadRawStreamController.stream);
|
||||||
@ -56,14 +98,22 @@ class TCPRepository {
|
|||||||
}
|
}
|
||||||
responseQueue.cancel();
|
responseQueue.cancel();
|
||||||
payloadQueue.cancel();
|
payloadQueue.cancel();
|
||||||
}).onError((error, stackTrace) {_socket.close();});
|
}).onError((error, stackTrace) {_socket?.close();});
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<TCPRepository> create({
|
static Future<TCPRepository> create({
|
||||||
required String serverAddress,
|
required String serverAddress,
|
||||||
required int serverPort
|
required int serverPort
|
||||||
}) async {
|
}) async {
|
||||||
var socket = await Socket.connect(serverAddress, serverPort);
|
Socket socket;
|
||||||
|
while(true) {
|
||||||
|
try{
|
||||||
|
socket = await Socket.connect(serverAddress, serverPort);
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
return TCPRepository._internal(
|
return TCPRepository._internal(
|
||||||
socket: socket,
|
socket: socket,
|
||||||
remoteAddress: serverAddress,
|
remoteAddress: serverAddress,
|
||||||
@ -78,7 +128,7 @@ class TCPRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Socket _socket;
|
Socket? _socket;
|
||||||
final String _remoteAddress;
|
final String _remoteAddress;
|
||||||
final int _remotePort;
|
final int _remotePort;
|
||||||
|
|
||||||
@ -359,7 +409,7 @@ class TCPRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dispose() async {
|
void dispose() async {
|
||||||
await _socket.flush();
|
await _socket?.flush();
|
||||||
await _socket.close();
|
await _socket?.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user