mirror of
https://github.com/Linloir/Simple-TCP-Server.git
synced 2025-12-18 16:38:12 +08:00
fix bugs, synchronous payload reception method adopted
This commit is contained in:
parent
3eb76d80d9
commit
00fd34e0f1
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,5 +2,7 @@
|
|||||||
.dart_tool/
|
.dart_tool/
|
||||||
.packages
|
.packages
|
||||||
|
|
||||||
|
.tmp/
|
||||||
|
|
||||||
# Conventional directory for build output.
|
# Conventional directory for build output.
|
||||||
build/
|
build/
|
||||||
|
|||||||
1
02e74f10e0327ad868d138f2b4fdd6f0
Normal file
1
02e74f10e0327ad868d138f2b4fdd6f0
Normal file
@ -0,0 +1 @@
|
|||||||
|
27
|
||||||
BIN
0604473ff30c614d2045ebee1b9e110e
Normal file
BIN
0604473ff30c614d2045ebee1b9e110e
Normal file
Binary file not shown.
@ -1,155 +1,160 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-06 15:44:16
|
* @Date : 2022-10-06 15:44:16
|
||||||
* @LastEditTime : 2022-10-08 23:57:37
|
* @LastEditTime : 2022-10-09 18:00:40
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:tcp_server/database.dart';
|
import 'package:tcp_server/database.dart';
|
||||||
import 'package:tcp_server/requesthandler.dart';
|
import 'package:tcp_server/requesthandler.dart';
|
||||||
import 'package:tcp_server/tcpcontroller/controller.dart';
|
import 'package:tcp_server/tcpcontroller/controller.dart';
|
||||||
|
import 'package:tcp_server/tcpcontroller/payload/message.dart';
|
||||||
import 'package:tcp_server/tcpcontroller/request.dart';
|
import 'package:tcp_server/tcpcontroller/request.dart';
|
||||||
import 'package:tcp_server/tcpcontroller/response.dart';
|
import 'package:tcp_server/tcpcontroller/response.dart';
|
||||||
|
|
||||||
void main(List<String> arguments) async {
|
void main(List<String> arguments) async {
|
||||||
|
//Create tmp folder
|
||||||
|
await Directory('${Directory.current.path}/.tmp').create();
|
||||||
await DataBaseHelper().initialize();
|
await DataBaseHelper().initialize();
|
||||||
var tokenMap = <int, Socket>{};
|
var tokenMap = <int, TCPController>{};
|
||||||
var socketMap = <Socket, Future<int>>{};
|
var controllerMap = <TCPController, Future<int>>{};
|
||||||
var listenSocket = await ServerSocket.bind('127.0.0.1', 20706);
|
var listenSocket = await ServerSocket.bind('127.0.0.1', 20706);
|
||||||
listenSocket.listen(
|
listenSocket.listen(
|
||||||
(socket) {
|
(socket) {
|
||||||
var controller = TCPController(socket: socket);
|
var controller = TCPController(socket: socket);
|
||||||
controller.stream.listen((request) async {
|
controller.inStream.listen((request) async {
|
||||||
|
print('[L] ${request.toJSON}');
|
||||||
if(request.tokenID == null) {
|
if(request.tokenID == null) {
|
||||||
if(socketMap[socket] == null) {
|
if(controllerMap[controller] == null) {
|
||||||
socketMap[socket] = (() async => (await DataBaseHelper().createToken()))();
|
controllerMap[controller] = (() async => (await DataBaseHelper().createToken()))();
|
||||||
}
|
}
|
||||||
request.tokenID = await socketMap[socket];
|
request.tokenID = await controllerMap[controller];
|
||||||
var tokenResponse = TCPResponse(
|
var tokenResponse = TCPResponse(
|
||||||
type: RequestType.token,
|
type: ResponseType.token,
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: {
|
body: {
|
||||||
"tokenid": request.tokenID
|
"tokenid": request.tokenID
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await socket.addStream(tokenResponse.stream);
|
controller.outStream.add(tokenResponse);
|
||||||
}
|
}
|
||||||
tokenMap[request.tokenID!] = tokenMap[request.tokenID!] ?? socket;
|
tokenMap[request.tokenID!] = tokenMap[request.tokenID!] ?? controller;
|
||||||
switch(request.requestType) {
|
switch(request.requestType) {
|
||||||
case RequestType.checkState: {
|
case RequestType.checkState: {
|
||||||
var response = await onCheckState(request, socket);
|
var response = await onCheckState(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.register: {
|
case RequestType.register: {
|
||||||
var response = await onRegister(request, socket);
|
var response = await onRegister(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.login: {
|
case RequestType.login: {
|
||||||
var response = await onLogin(request, socket);
|
var response = await onLogin(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.logout: {
|
case RequestType.logout: {
|
||||||
var response = await onLogout(request, socket);
|
var response = await onLogout(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.profile: {
|
case RequestType.profile: {
|
||||||
var response = await onFetchProfile(request, socket);
|
var response = await onFetchProfile(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.modifyProfile: {
|
case RequestType.modifyProfile: {
|
||||||
var response = await onModifyProfile(request, socket);
|
var response = await onModifyProfile(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.modifyPassword: {
|
case RequestType.modifyPassword: {
|
||||||
var response = await onModifyPassword(request, socket);
|
var response = await onModifyPassword(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.sendMessage: {
|
case RequestType.sendMessage: {
|
||||||
//Forword Message
|
//Forword Message
|
||||||
var message = request.body['message'] as Map<String, Object?>;
|
var message = Message.fromJSONObject(request.body);
|
||||||
await DataBaseHelper().setFetchHistoryFor(
|
await DataBaseHelper().setFetchHistoryFor(
|
||||||
tokenID: request.tokenID,
|
tokenID: request.tokenID,
|
||||||
newTimeStamp: message['timestamp'] as int
|
newTimeStamp: message.timestamp
|
||||||
);
|
);
|
||||||
var originUserID = message['userid'] as int;
|
var originUserID = message.senderID;
|
||||||
var onlineDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: originUserID);
|
var onlineDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: originUserID);
|
||||||
for(var device in onlineDevices) {
|
for(var device in onlineDevices) {
|
||||||
if(device == request.tokenID) {
|
if(device == request.tokenID) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var targetSocket = tokenMap[device];
|
var targetController = tokenMap[device];
|
||||||
targetSocket?.write(jsonEncode({
|
var forwardResponse = TCPResponse(
|
||||||
'response': 'FORWARDMSG',
|
type: ResponseType.forwardMessage,
|
||||||
'body': {
|
status: ResponseStatus.ok,
|
||||||
"message": message
|
body: message.jsonObject
|
||||||
}
|
);
|
||||||
}));
|
targetController?.outStream.add(forwardResponse);
|
||||||
//Update Fetch Histories
|
//Update Fetch Histories
|
||||||
await DataBaseHelper().setFetchHistoryFor(
|
await DataBaseHelper().setFetchHistoryFor(
|
||||||
tokenID: device,
|
tokenID: device,
|
||||||
newTimeStamp: message['timestamp'] as int
|
newTimeStamp: message.timestamp
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
var targetUserID = message['targetid'] as int;
|
var targetUserID = message.receiverID;
|
||||||
var targetDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: targetUserID);
|
var targetDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: targetUserID);
|
||||||
for(var device in targetDevices) {
|
for(var device in targetDevices) {
|
||||||
//Forward to socket
|
//Forward to socket
|
||||||
var targetSocket = tokenMap[device];
|
var targetController = tokenMap[device];
|
||||||
targetSocket?.write(jsonEncode({
|
var forwardResponse = TCPResponse(
|
||||||
'response': 'FORWARDMSG',
|
type: ResponseType.forwardMessage,
|
||||||
'body': {
|
status: ResponseStatus.ok,
|
||||||
"message": message
|
body: message.jsonObject
|
||||||
}
|
);
|
||||||
}));
|
targetController?.outStream.add(forwardResponse);
|
||||||
//Update Fetch Histories
|
//Update Fetch Histories
|
||||||
await DataBaseHelper().setFetchHistoryFor(
|
await DataBaseHelper().setFetchHistoryFor(
|
||||||
tokenID: device,
|
tokenID: device,
|
||||||
newTimeStamp: message['timestamp'] as int
|
newTimeStamp: message.timestamp
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
var response = await onSendMessage(request, socket);
|
var response = await onSendMessage(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.fetchMessage: {
|
case RequestType.fetchMessage: {
|
||||||
var response = await onFetchMessage(request, socket);
|
var response = await onFetchMessage(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.fetchFile: {
|
case RequestType.fetchFile: {
|
||||||
var response = await onFetchFile(request, socket);
|
var response = await onFetchFile(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.searchUser: {
|
case RequestType.searchUser: {
|
||||||
var response = await onSearchUser(request, socket);
|
var response = await onSearchUser(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.fetchContact: {
|
case RequestType.fetchContact: {
|
||||||
var response = await onFetchContact(request, socket);
|
var response = await onFetchContact(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.unknown: {
|
case RequestType.unknown: {
|
||||||
var response = await onUnknownRequest(request, socket);
|
var response = await onUnknownRequest(request, socket);
|
||||||
await socket.addStream(response.stream);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
print('[E] Drop out of switch case');
|
print('[E] Drop out of switch case');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Clear temp file
|
||||||
|
request.payload?.delete();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-06 16:15:01
|
* @Date : 2022-10-06 16:15:01
|
||||||
* @LastEditTime : 2022-10-08 23:54:36
|
* @LastEditTime : 2022-10-09 17:59:13
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -467,7 +467,8 @@ class DataBaseHelper {
|
|||||||
{
|
{
|
||||||
'msgmd5': msg.md5encoded,
|
'msgmd5': msg.md5encoded,
|
||||||
'filemd5': fileMd5
|
'filemd5': fileMd5
|
||||||
}
|
},
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 20:52:48
|
* @Date : 2022-10-08 20:52:48
|
||||||
* @LastEditTime : 2022-10-08 23:39:59
|
* @LastEditTime : 2022-10-09 13:39:02
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -18,13 +18,13 @@ Future<TCPResponse> onCheckState(TCPRequest request, Socket socket) async {
|
|||||||
try {
|
try {
|
||||||
var userInfo = await DataBaseHelper().checkLoginState(tokenID: request.tokenID);
|
var userInfo = await DataBaseHelper().checkLoginState(tokenID: request.tokenID);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: userInfo.jsonObject
|
body: userInfo.jsonObject
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -39,13 +39,13 @@ Future<TCPResponse> onRegister(TCPRequest request, Socket socket) async {
|
|||||||
tokenID: request.tokenID
|
tokenID: request.tokenID
|
||||||
);
|
);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: newUserInfo.jsonObject
|
body: newUserInfo.jsonObject
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -59,13 +59,13 @@ Future<TCPResponse> onLogin(TCPRequest request, Socket socket) async {
|
|||||||
tokenID: request.tokenID
|
tokenID: request.tokenID
|
||||||
);
|
);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: userInfo.jsonObject
|
body: userInfo.jsonObject
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -76,12 +76,12 @@ Future<TCPResponse> onLogout(TCPRequest request, Socket socket) async {
|
|||||||
try {
|
try {
|
||||||
await DataBaseHelper().logOut(tokenID: request.tokenID);
|
await DataBaseHelper().logOut(tokenID: request.tokenID);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -92,13 +92,13 @@ Future<TCPResponse> onFetchProfile(TCPRequest request, Socket socket) async {
|
|||||||
try {
|
try {
|
||||||
var userInfo = await DataBaseHelper().fetchUserInfoViaToken(tokenID: request.tokenID);
|
var userInfo = await DataBaseHelper().fetchUserInfoViaToken(tokenID: request.tokenID);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: userInfo.jsonObject
|
body: userInfo.jsonObject
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -112,12 +112,12 @@ Future<TCPResponse> onModifyPassword(TCPRequest request, Socket socket) async {
|
|||||||
tokenID: request.tokenID
|
tokenID: request.tokenID
|
||||||
);
|
);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok
|
status: ResponseStatus.ok
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -131,13 +131,13 @@ Future<TCPResponse> onModifyProfile(TCPRequest request, Socket socket) async {
|
|||||||
tokenID: request.tokenID
|
tokenID: request.tokenID
|
||||||
);
|
);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: newUserInfo.jsonObject
|
body: newUserInfo.jsonObject
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -153,17 +153,18 @@ Future<TCPResponse> onSendMessage(TCPRequest request, Socket socket) async {
|
|||||||
fileMd5: message.fileMd5
|
fileMd5: message.fileMd5
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
//Store message
|
||||||
await DataBaseHelper().storeMessage(
|
await DataBaseHelper().storeMessage(
|
||||||
msg: message,
|
msg: message,
|
||||||
fileMd5: message.fileMd5
|
fileMd5: message.fileMd5
|
||||||
);
|
);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: RequestType.sendMessage,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -174,15 +175,15 @@ Future<TCPResponse> onFetchMessage(TCPRequest request, Socket socket) async {
|
|||||||
try {
|
try {
|
||||||
var messages = await DataBaseHelper().fetchMessagesFor(tokenID: request.tokenID);
|
var messages = await DataBaseHelper().fetchMessagesFor(tokenID: request.tokenID);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: {
|
body: {
|
||||||
'messages': messages.map((e) => e.jsonObject)
|
'messages': messages.map((e) => e.jsonObject).toList()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -194,13 +195,13 @@ Future<TCPResponse> onFetchFile(TCPRequest request, Socket socket) async {
|
|||||||
var filePath = await DataBaseHelper().fetchFilePath(msgMd5: request.body['msgmd5'] as String);
|
var filePath = await DataBaseHelper().fetchFilePath(msgMd5: request.body['msgmd5'] as String);
|
||||||
var file = File(filePath);
|
var file = File(filePath);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
payload: file
|
payload: file
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -211,13 +212,13 @@ Future<TCPResponse> onSearchUser(TCPRequest request, Socket socket) async {
|
|||||||
try {
|
try {
|
||||||
var userInfo = await DataBaseHelper().fetchUserInfoViaUsername(username: request.body['username'] as String);
|
var userInfo = await DataBaseHelper().fetchUserInfoViaUsername(username: request.body['username'] as String);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: userInfo.jsonObject
|
body: userInfo.jsonObject
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -228,12 +229,12 @@ Future<TCPResponse> onAddContact(TCPRequest request, Socket socket) async {
|
|||||||
try {
|
try {
|
||||||
await DataBaseHelper().addContact(tokenID: request.tokenID, userID: request.body['userid'] as int);
|
await DataBaseHelper().addContact(tokenID: request.tokenID, userID: request.body['userid'] as int);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok
|
status: ResponseStatus.ok
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -246,7 +247,7 @@ Future<TCPResponse> onFetchContact(TCPRequest request, Socket socket) async {
|
|||||||
var pendingContacts = await DataBaseHelper().fetchPendingContacts(tokenID: request.tokenID);
|
var pendingContacts = await DataBaseHelper().fetchPendingContacts(tokenID: request.tokenID);
|
||||||
var requestingContacts = await DataBaseHelper().fetchRequestingContacts(tokenID: request.tokenID);
|
var requestingContacts = await DataBaseHelper().fetchRequestingContacts(tokenID: request.tokenID);
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: {
|
body: {
|
||||||
"contacts": contacts.map((e) => e.jsonObject),
|
"contacts": contacts.map((e) => e.jsonObject),
|
||||||
@ -256,7 +257,7 @@ Future<TCPResponse> onFetchContact(TCPRequest request, Socket socket) async {
|
|||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: exception.toString()
|
errInfo: exception.toString()
|
||||||
);
|
);
|
||||||
@ -265,7 +266,7 @@ Future<TCPResponse> onFetchContact(TCPRequest request, Socket socket) async {
|
|||||||
|
|
||||||
Future<TCPResponse> onUnknownRequest(TCPRequest request, Socket socket) async {
|
Future<TCPResponse> onUnknownRequest(TCPRequest request, Socket socket) async {
|
||||||
return TCPResponse(
|
return TCPResponse(
|
||||||
type: request.requestType,
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.err,
|
status: ResponseStatus.err,
|
||||||
errInfo: 'Unkown request'
|
errInfo: 'Unkown request'
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 15:10:04
|
* @Date : 2022-10-08 15:10:04
|
||||||
* @LastEditTime : 2022-10-08 23:11:24
|
* @LastEditTime : 2022-10-09 17:55:26
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -10,12 +10,15 @@ import 'dart:io';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:tcp_server/tcpcontroller/request.dart';
|
import 'package:tcp_server/tcpcontroller/request.dart';
|
||||||
|
import 'package:tcp_server/tcpcontroller/response.dart';
|
||||||
|
|
||||||
class TCPController {
|
class TCPController {
|
||||||
final Socket socket;
|
final Socket socket;
|
||||||
|
|
||||||
//Stores the incoming bytes of the TCP connection temporarily
|
//Stores the incoming bytes of the TCP connection temporarily
|
||||||
final Uint8List buffer = Uint8List(0);
|
final List<int> buffer = [];
|
||||||
|
//Stores the fetched require temporarily
|
||||||
|
List<int> _requestBytes = [];
|
||||||
|
|
||||||
//Byte length for json object
|
//Byte length for json object
|
||||||
int requestLength = 0;
|
int requestLength = 0;
|
||||||
@ -23,19 +26,26 @@ class TCPController {
|
|||||||
int payloadLength = 0;
|
int payloadLength = 0;
|
||||||
|
|
||||||
//Construct a stream which emits events on intact requests
|
//Construct a stream which emits events on intact requests
|
||||||
StreamController<Uint8List> _requestStreamController = StreamController()..close();
|
StreamController<List<int>> _requestStreamController = StreamController()..close();
|
||||||
|
|
||||||
//Construct a payload stream which forward the incoming byte into temp file
|
//Construct a payload stream which forward the incoming byte into temp file
|
||||||
StreamController<Uint8List> _payloadStreamController = StreamController()..close();
|
StreamController<List<int>> _payloadStreamController = StreamController()..close();
|
||||||
|
|
||||||
//Provide a request stream for caller functions to listen on
|
//Provide a request stream for caller functions to listen on
|
||||||
final StreamController<TCPRequest> _streamController = StreamController();
|
final StreamController<TCPRequest> _inStreamController = StreamController();
|
||||||
Stream<TCPRequest> get stream => _streamController.stream;
|
Stream<TCPRequest> get inStream => _inStreamController.stream;
|
||||||
|
|
||||||
|
//Provide a post stream for caller functions to push to
|
||||||
|
final StreamController<TCPResponse> _outStreamController = StreamController();
|
||||||
|
StreamSink<TCPResponse> get outStream => _outStreamController.sink;
|
||||||
|
|
||||||
TCPController({
|
TCPController({
|
||||||
required this.socket
|
required this.socket
|
||||||
}) {
|
}) {
|
||||||
socket.listen(socketHandler);
|
socket.listen(socketHandler);
|
||||||
|
_outStreamController.stream.listen((response) async {
|
||||||
|
await socket.addStream(response.stream);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Listen to the incoming stream and emits event whenever there is a intact request
|
//Listen to the incoming stream and emits event whenever there is a intact request
|
||||||
@ -44,30 +54,35 @@ class TCPController {
|
|||||||
buffer.addAll(fetchedData);
|
buffer.addAll(fetchedData);
|
||||||
//Consume buffer until it's not enough for first 8 byte of a message
|
//Consume buffer until it's not enough for first 8 byte of a message
|
||||||
while(true) {
|
while(true) {
|
||||||
if(requestLength == 0 && payloadLength == 0) {
|
if(requestLength == 0 && payloadLength == 0 && _payloadStreamController.isClosed) {
|
||||||
//New request
|
//New request
|
||||||
if(buffer.length > 8) {
|
if(buffer.length >= 8) {
|
||||||
//Buffered data has more than 8 bytes, enough to read request length and body length
|
//Buffered data has more than 8 bytes, enough to read request length and body length
|
||||||
requestLength = buffer.sublist(0, 4).buffer.asByteData().getInt32(0);
|
requestLength = Uint8List.fromList(buffer.sublist(0, 4)).buffer.asInt32List()[0];
|
||||||
payloadLength = buffer.sublist(4, 8).buffer.asByteData().getInt32(0);
|
payloadLength = Uint8List.fromList(buffer.sublist(4, 8)).buffer.asInt32List()[0];
|
||||||
//Clear the length indicator bytes
|
//Clear the length indicator bytes
|
||||||
buffer.removeRange(0, 8);
|
buffer.removeRange(0, 8);
|
||||||
//Create temp file to read payload (might be huge)
|
//Create temp file to read payload (might be huge)
|
||||||
var tempFile = File('./temp${DateTime.now().microsecondsSinceEpoch}.temp')..createSync();
|
var tempFile = File('${Directory.current.path}/.tmp/${DateTime.now().microsecondsSinceEpoch}')..createSync();
|
||||||
//Initialize payload transmission controller
|
//Initialize payload transmission controller
|
||||||
_payloadStreamController = StreamController();
|
_payloadStreamController = StreamController();
|
||||||
//Bind file to stream
|
//Bind file to stream
|
||||||
_payloadStreamController.stream.listen((data) {
|
_payloadStreamController.stream.listen(
|
||||||
tempFile.writeAsBytes(data, mode: FileMode.append);
|
(data) {
|
||||||
});
|
tempFile.writeAsBytesSync(data, mode: FileMode.append, flush: true);
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
//Payload definetely ends after request is buffered
|
||||||
|
//Therefore transmit the request and payload to stream here
|
||||||
|
_inStreamController.add(TCPRequest(_requestBytes, tempFile));
|
||||||
|
}
|
||||||
|
);
|
||||||
//Bind request construction on stream
|
//Bind request construction on stream
|
||||||
_requestStreamController = StreamController();
|
_requestStreamController = StreamController();
|
||||||
_requestStreamController.stream.listen((requestBytes) {
|
_requestStreamController.stream.listen((requestBytes) {
|
||||||
//When request stream is closed by controller
|
//When request stream is closed by controller
|
||||||
var request = TCPRequest(requestBytes, tempFile);
|
//Request is intact, save to _request temporarily
|
||||||
_payloadStreamController.done.then((_) {
|
_requestBytes = requestBytes;
|
||||||
_streamController.add(request);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -80,7 +95,7 @@ class TCPController {
|
|||||||
//Currently awaiting full transmission
|
//Currently awaiting full transmission
|
||||||
if(requestLength > 0) {
|
if(requestLength > 0) {
|
||||||
//Currently processing on a request
|
//Currently processing on a request
|
||||||
if(buffer.length > requestLength) {
|
if(buffer.length >= requestLength) {
|
||||||
//Got intact request json
|
//Got intact request json
|
||||||
//Emit request buffer through stream
|
//Emit request buffer through stream
|
||||||
_requestStreamController.add(buffer.sublist(0, requestLength));
|
_requestStreamController.add(buffer.sublist(0, requestLength));
|
||||||
@ -101,7 +116,7 @@ class TCPController {
|
|||||||
if(buffer.length >= payloadLength) {
|
if(buffer.length >= payloadLength) {
|
||||||
//Last few bytes to emit
|
//Last few bytes to emit
|
||||||
//Send the last few bytes to stream
|
//Send the last few bytes to stream
|
||||||
_payloadStreamController.add(buffer.sublist(0, payloadLength));
|
_payloadStreamController.add(Uint8List.fromList(buffer.sublist(0, payloadLength)));
|
||||||
//Clear buffer
|
//Clear buffer
|
||||||
buffer.removeRange(0, payloadLength);
|
buffer.removeRange(0, payloadLength);
|
||||||
//Set payload length to zero
|
//Set payload length to zero
|
||||||
@ -112,11 +127,13 @@ class TCPController {
|
|||||||
else {
|
else {
|
||||||
//Part of payload
|
//Part of payload
|
||||||
//Transmit all to stream
|
//Transmit all to stream
|
||||||
_payloadStreamController.add(buffer);
|
_payloadStreamController.add(Uint8List.fromList(buffer));
|
||||||
//Reduce payload bytes left
|
//Reduce payload bytes left
|
||||||
payloadLength -= buffer.length;
|
payloadLength -= buffer.length;
|
||||||
//Clear buffer
|
//Clear buffer
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
|
//Exit and wait for another submit
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 16:16:19
|
* @Date : 2022-10-08 16:16:19
|
||||||
* @LastEditTime : 2022-10-08 23:20:36
|
* @LastEditTime : 2022-10-09 14:53:11
|
||||||
* @Description : Message Info Payload
|
* @Description : Message Info Payload
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class Message {
|
|||||||
..addAll(intToUint8List(targetid))
|
..addAll(intToUint8List(targetid))
|
||||||
..addAll(intToUint8List(timestamp))
|
..addAll(intToUint8List(timestamp))
|
||||||
..addAll(content.codeUnits)
|
..addAll(content.codeUnits)
|
||||||
),
|
).toString(),
|
||||||
"filemd5": filemd5
|
"filemd5": filemd5
|
||||||
};
|
};
|
||||||
Message.fromJSONObject(Map<String, Object?> data): _data = data;
|
Message.fromJSONObject(Map<String, Object?> data): _data = data;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 15:14:26
|
* @Date : 2022-10-08 15:14:26
|
||||||
* @LastEditTime : 2022-10-08 23:52:50
|
* @LastEditTime : 2022-10-09 17:36:42
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
@ -9,7 +9,6 @@ import 'dart:io';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
enum RequestType {
|
enum RequestType {
|
||||||
token ('TOKEN'), //Only exists when server is sending message
|
|
||||||
checkState ('STATE'), //Check login state for device token
|
checkState ('STATE'), //Check login state for device token
|
||||||
register ('REGISTER'), //Register new user
|
register ('REGISTER'), //Register new user
|
||||||
login ('LOGIN'), //Login via username and password
|
login ('LOGIN'), //Login via username and password
|
||||||
@ -41,7 +40,8 @@ class TCPRequest {
|
|||||||
final Map<String, Object?> _data;
|
final Map<String, Object?> _data;
|
||||||
File? payload;
|
File? payload;
|
||||||
|
|
||||||
TCPRequest(Uint8List data, this.payload): _data = jsonDecode(String.fromCharCodes(data));
|
TCPRequest(List<int> data, this.payload): _data = jsonDecode(String.fromCharCodes(data));
|
||||||
|
TCPRequest.none(): _data = {};
|
||||||
|
|
||||||
String get toJSON => jsonEncode(_data);
|
String get toJSON => jsonEncode(_data);
|
||||||
RequestType get requestType => RequestType.fromValue(_data['request'] as String);
|
RequestType get requestType => RequestType.fromValue(_data['request'] as String);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 22:40:47
|
* @Date : 2022-10-08 22:40:47
|
||||||
* @LastEditTime : 2022-10-08 23:05:01
|
* @LastEditTime : 2022-10-09 16:39:02
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -11,6 +11,38 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:tcp_server/tcpcontroller/request.dart';
|
import 'package:tcp_server/tcpcontroller/request.dart';
|
||||||
|
|
||||||
|
enum ResponseType {
|
||||||
|
token ('TOKEN'), //Only exists when server is sending message
|
||||||
|
checkState ('STATE'), //Check login state for device token
|
||||||
|
register ('REGISTER'), //Register new user
|
||||||
|
login ('LOGIN'), //Login via username and password
|
||||||
|
logout ('LOGOUT'), //Logout for current device token
|
||||||
|
profile ('PROFILE'), //Fetch current logged in user profile
|
||||||
|
modifyPassword('MODIFYPASSWD'), //Modify user password
|
||||||
|
modifyProfile ('MODIFYPROFILE'), //Modify user profile
|
||||||
|
sendMessage ('SENDMSG'), //Send message
|
||||||
|
forwardMessage('FORWARDMSG'), //Forward message
|
||||||
|
fetchMessage ('FETCHMSG'), //Fetch message
|
||||||
|
findFile ('FINDFILE'), //Find file by md5 before transmitting the file
|
||||||
|
fetchFile ('FETCHFILE'), //Fetch file and file md5 by message md5
|
||||||
|
searchUser ('SEARCHUSR'), //Search username and userid by username
|
||||||
|
addContact ('ADDCONTACT'), //Add one-way relation to a user
|
||||||
|
fetchContact ('FETCHCONTACT'), //Fetch all contacts, including requesting and pending
|
||||||
|
unknown ('UNKNOWN'); //Wrong command
|
||||||
|
|
||||||
|
const ResponseType(String value): _value = value;
|
||||||
|
final String _value;
|
||||||
|
String get value => _value;
|
||||||
|
|
||||||
|
//Construct the enum type by value
|
||||||
|
factory ResponseType.fromValue(String value) {
|
||||||
|
return ResponseType.values.firstWhere((element) => element._value == value, orElse: () => ResponseType.unknown);
|
||||||
|
}
|
||||||
|
factory ResponseType.fromRequestType(RequestType type) {
|
||||||
|
return ResponseType.values.firstWhere((element) => element._value == type.value, orElse: () => ResponseType.unknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ResponseStatus {
|
enum ResponseStatus {
|
||||||
ok('OK'),
|
ok('OK'),
|
||||||
err('ERR');
|
err('ERR');
|
||||||
@ -26,7 +58,7 @@ class TCPResponse {
|
|||||||
final File? payloadFile;
|
final File? payloadFile;
|
||||||
|
|
||||||
TCPResponse({
|
TCPResponse({
|
||||||
required RequestType type,
|
required ResponseType type,
|
||||||
required ResponseStatus status,
|
required ResponseStatus status,
|
||||||
Map<String, Object?>? body,
|
Map<String, Object?>? body,
|
||||||
String? errInfo,
|
String? errInfo,
|
||||||
@ -42,7 +74,7 @@ class TCPResponse {
|
|||||||
|
|
||||||
int get responseLength => responseJson.length;
|
int get responseLength => responseJson.length;
|
||||||
int get payloadLength => payloadFile?.lengthSync() ?? 0;
|
int get payloadLength => payloadFile?.lengthSync() ?? 0;
|
||||||
Stream<Uint8List> get stream async* {
|
Stream<List<int>> get stream async* {
|
||||||
yield Uint8List(4)..buffer.asInt32List()[0] = responseLength;
|
yield Uint8List(4)..buffer.asInt32List()[0] = responseLength;
|
||||||
yield Uint8List(4)..buffer.asInt32List()[0] = payloadLength;
|
yield Uint8List(4)..buffer.asInt32List()[0] = payloadLength;
|
||||||
yield Uint8List.fromList(responseJson.codeUnits);
|
yield Uint8List.fromList(responseJson.codeUnits);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user