Compare commits

..

29 Commits
v1.4.2 ... main

Author SHA1 Message Date
91510be124 fix:_WIN32_WINNT define pos. 2025-05-18 00:38:30 +08:00
655cfe6fad remove:不使用boost filesystem. 2025-05-18 00:25:33 +08:00
15085a1f8f fix:修正server ctrl-c退出时终端颜色没有恢复的问题。 2025-05-16 11:43:10 +08:00
b68017bc2a fix:修正tss-http win乱码问题 2025-04-29 14:07:46 +08:00
b9c7cbba3c fix:修正编译和tss-http浏览器乱码问题。 2025-04-29 12:49:47 +08:00
60f52a14d2 tss-http: 添加musl静态链接。 2025-04-29 11:53:37 +08:00
12b57e65c9 tss-http:扩展buffer长度 2025-04-29 11:50:24 +08:00
64d4255ca3 compile:tss-http win8及以下不支持且不支持mingw,对于此环境不生成目标文件 2025-04-29 11:40:13 +08:00
8f54951a28 add:添加一个mini http服务端tss-http 2025-04-29 11:07:43 +08:00
8121a370ce fix: server ctrl-c exit recovery color 2025-04-16 11:29:07 -04:00
733c5a68a3 rec:服务端终止恢复终端颜色。 2025-04-16 22:55:46 +08:00
9839fa79ea add:添加crash模块(-DGRAB_CRASH=ON)。 2025-04-16 22:19:49 +08:00
a81cc54025 change:改为clangd引擎。 2025-04-15 08:48:41 +08:00
13423d5172 upd:测试uptask用例完成。 2025-04-13 13:22:35 +08:00
414bc6ed75 list:文件list相关测试用例进度。 2025-04-13 00:53:52 +08:00
7a6b961392 ls:第一个Ls测试用例实现。 2025-04-13 00:08:50 +08:00
0ee24945bb upd:初步添加cmd测试用例架子。 2025-04-12 22:51:06 +08:00
2793dab420 add:添加使用catch2测试用例。 2025-04-12 21:58:53 +08:00
49d6ad1974 opt: initial new memory bytes 2025-04-09 19:29:12 +08:00
797f8f164e fix:修正秘钥key问题。 2025-04-09 18:17:13 +08:00
61ac79c93e change:更改内存删除分配方式。 2025-04-09 17:29:57 +08:00
99607c6721 fix:修正一个未置nullptr错误。 2025-04-09 16:32:40 +08:00
6ecf0d419a fix:修正加解密BUG。 2025-04-09 16:20:34 +08:00
807dbdfede fix:处理一个内存错误问题。 2025-04-09 15:05:56 +08:00
a5cab61603 doc:说明修改。 2025-04-09 12:00:43 +08:00
3a32e2f0d4 doc:说明修改。 2025-04-09 10:27:29 +08:00
41f0b5d726 doc:补充说明。 2025-04-09 10:25:45 +08:00
99a1223e52 debug:加解密速度测试和开关加解密。 2025-04-09 10:20:01 +08:00
119f1e8f72 fix:修正一个内存泄漏问题。 2025-04-09 09:14:30 +08:00
38 changed files with 38158 additions and 497 deletions

@ -11,13 +11,7 @@ ReflowComments: true
SpacesBeforeTrailingComments: 3
TabWidth: 4
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ColumnLimit: 110
ColumnLimit: 80
AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: None
AllowShortEnumsOnASingleLine: false
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<.*>'
Priority: 1
- Regex: '^".*"'
Priority: 2

3
.gitignore vendored

@ -5,4 +5,5 @@ cmake-*
.xmake
compile_commands.json
out
xpbuild
xpbuild
cbuild

3
.gitmodules vendored

@ -6,3 +6,6 @@
path = filecomplete
url = https://www.sinxmiao.cn/taynpg/filecomplete
branch = main
[submodule "crashelper"]
path = crashelper
url = https://www.sinxmiao.cn/taynpg/crashelper

86
.vscode/settings.json vendored

@ -1,10 +1,8 @@
{
"files.autoSave": "onFocusChange",
"editor.fontSize": 14,
"editor.fontFamily": "'Source Code Pro', 'Source Code Pro', 'Source Code Pro'",
"terminal.integrated.fontFamily": "Source Code Pro",
"editor.fontLigatures": true,
//"C_Cpp.default.configurationProvider": "tboox.xmake-vscode",
"editor.fontFamily": "'Monaspace Krypton Light', 'Monaspace Krypton Light', 'Monaspace Krypton Light'",
"terminal.integrated.fontFamily": "Monaspace Krypton Light",
"cmake.configureOnOpen": true,
"cmake.debugConfig": {
"console": "integratedTerminal",
@ -19,24 +17,20 @@
"ignoreFailures": true
}
],
"visualizerFile": "${workspaceRoot}/.vscode/qt5.natvis",
"args": [
"-u", "0"
"9999"
]
},
"cmake.environment": {
"PATH": "${env:PATH};"
},
"cmake.configureArgs": [
"-Wno-dev",
"-DCMAKE_PREFIX_PATH:STRING=C:/dev/wxwigets",
"-DUSE_GUI=ON",
"-DUSE_TRANSM_TEST=ON"
"-DGRAB_CRASH=ON"
],
"cmake.configureSettings": {
//"CMAKE_TOOLCHAIN_FILE": "${env:VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
},
"cmake.options.statusBarVisibility": "visible",
"cmake.generator": "Ninja",
"C_Cpp.default.compileCommands": "${workspaceRoot}/build/compile_commands.json",
"C_Cpp.default.cppStandard": "c++17",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"editor.inlayHints.enabled": "off",
"editor.unicodeHighlight.allowedLocales": {
"ja": true,
@ -44,22 +38,27 @@
"zh-hans": true
},
"files.associations": {
"xstring": "cpp",
"vector": "cpp",
"string": "cpp",
".clang-tidy": "yaml",
"filesystem": "cpp",
"regex": "cpp",
"functional": "cpp",
"algorithm": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
@ -69,13 +68,13 @@
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"filesystem": "cpp",
"expected": "cpp",
"resumable": "cpp",
"format": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
@ -99,21 +98,22 @@
"ratio": "cpp",
"set": "cpp",
"shared_mutex": "cpp",
"source_location": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string_view": "cpp",
"strstream": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"valarray": "cpp",
"variant": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
@ -125,39 +125,9 @@
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xmemory0": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp",
"qbytearray": "cpp",
"*.ipp": "cpp",
"xthread": "cpp",
"*.tcc": "cpp",
"regex": "cpp",
"bit": "cpp",
"memory_resource": "cpp",
"source_location": "cpp",
"charconv": "cpp",
"compare": "cpp",
"concepts": "cpp",
"coroutine": "cpp",
"format": "cpp",
"stop_token": "cpp",
"expected": "cpp",
"numbers": "cpp",
"semaphore": "cpp",
"span": "cpp",
"text_encoding": "cpp",
"*.in": "cpp",
"hash_map": "cpp",
"stdfloat": "cpp",
"unordered_set": "cpp",
"cfenv": "cpp",
"cassert": "cpp",
"version": "cpp",
"resumable": "cpp"
},
"makefile.configureOnOpen": false,
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
"xutility": "cpp"
}
}

11817
3rd/catch_amalgamated.cpp Normal file

File diff suppressed because it is too large Load Diff

14135
3rd/catch_amalgamated.hpp Normal file

File diff suppressed because it is too large Load Diff

10497
3rd/httplib.h Normal file

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
project(transm VERSION 1.4.2 LANGUAGES CXX)
project(transm VERSION 1.5.2 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -11,6 +11,7 @@ set(PROJECT_URL "https://www.sinxmiao.cn/taynpg/transm")
set(COMPILER_ID ${CMAKE_CXX_COMPILER_ID})
if(MSVC)
add_compile_options(/source-charset:utf-8)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
@ -19,12 +20,7 @@ add_compile_options(-finput-charset=utf-8)
add_compile_options(-fexec-charset=gbk)
add_compile_options(-Wa,-mbig-obj)
set(COMPILER_ID "mingw")
endif()
if(DEFINED USE_BOOST)
message(STATUS "use boost library ${USE_BOOST}")
add_definitions(-DUSE_BOOST)
include(config/MBoost.cmake)
include(config/Mingw.cmake)
endif()
string(TOLOWER ${COMPILER_ID} COMPILER_ID)
@ -32,11 +28,17 @@ if(DEFINED IOS_ISH)
message(STATUS "INPUT IOS_ISH ${IOS_ISH}")
endif()
if(DEFINED XP_SYSTEM)
message(STATUS "transm use xp defined ${XP_SYSTEM}")
add_definitions(-D_WIN32_WINNT=0x0501)
if(WIN32)
execute_process(COMMAND cmd /c ver
OUTPUT_VARIABLE VER_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(VER_OUTPUT MATCHES "XP")
message(STATUS "Windows XP platform.")
add_definitions(-D_WIN32_WINNT=0x0501)
else()
add_definitions(-D_WIN32_WINNT=0x0601)
message(STATUS "Windows NT platform.")
add_definitions(-D_WIN32_WINNT=0x0601)
endif()
endif()
set(CMAKE_DEBUG_POSTFIX "d")
@ -45,6 +47,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
${PROJECT_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}/
)
if (DEFINED GRAB_CRASH)
message(STATUS "GRAB_CRASH ${GRAB_CRASH}")
add_subdirectory(crashelper/crashelper)
add_definitions(-DGRAB_CRASH)
endif()
add_definitions(-DFMT_HEADER_ONLY)
include_directories(3rd)
include_directories(.)
@ -57,8 +65,16 @@ add_subdirectory(client)
add_subdirectory(filecomplete)
add_subdirectory(tinyaes)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
message(STATUS "tss-http can't support mingw, will not compile.")
else()
add_subdirectory(http-server)
endif()
if (DEFINED USE_TRANSM_TEST)
message(STATUS "USE USE_TRANSM_TEST ${USE_TRANSM_TEST}")
add_definitions(-DUSE_TRANSM_TEST)
include(CTest)
add_subdirectory(test)
endif()
@ -83,12 +99,14 @@ message(STATUS "VERSION_GIT_HASH: ${VERSION_GIT_HASH}")
install(TARGETS tsc DESTINATION bin)
install(TARGETS tss DESTINATION bin)
if (DEFINED USE_BOOST)
install(FILES ${MINGW32_DLLS} DESTINATION bin)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
message(STATUS "tss-http can't support mingw, will not pack.")
else()
install(TARGETS tss-http DESTINATION bin)
endif()
if (DEFINED USE_GUI)
message(STATUS "USE GUI ${USE_GUI}")
#add_subdirectory(gui)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
install(FILES ${MINGW32_DLLS} DESTINATION bin)
endif()
# ********************************************************** pack infomation

@ -1,63 +0,0 @@
{
"configurations": [
{
"name": "x64 Local Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64 Local Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}"
},
{
"name": "x64 Linux Debug",
"generator": "Ninja",
"configurationType": "Debug",
"cmakeExecutable": "cmake",
"remoteCopySourcesExclusionList": [ ".vs", "out" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "$HOME/vs/${projectDirName}/${workspaceHash}/src",
"remoteBuildRoot": "$HOME/vs/${projectDirName}/${workspaceHash}/out/build/${name}",
"remoteInstallRoot": "$HOME/vs/${projectDirName}/${workspaceHash}/out/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete",
"remoteCopyBuildOutput": false,
"remoteCopySourcesMethod": "rsync",
"variables": []
},
{
"name": "x64 Linux Release",
"generator": "Ninja",
"configurationType": "Release",
"cmakeExecutable": "cmake",
"remoteCopySourcesExclusionList": [ ".vs", "out" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "$HOME/vs/${projectDirName}/${workspaceHash}/src",
"remoteBuildRoot": "$HOME/vs/${projectDirName}/${workspaceHash}/out/build/${name}",
"remoteInstallRoot": "$HOME/vs/${projectDirName}/${workspaceHash}/out/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete",
"remoteCopyBuildOutput": false,
"remoteCopySourcesMethod": "rsync",
"variables": []
}
]
}

@ -18,7 +18,9 @@
## 一些特点(基于最新版本)
- 易编译。
- 通配符传输。
- 公网传输支持加密。
- 广泛的平台支持。
- 终端自动文件补全。
- 自动检测对方掉线。
@ -40,7 +42,7 @@
## 1.程序启动
- 对于服务端程序`tss`,绑定默认绑定`0.0.0.0``9898`端口,如果需要修改端口,使用参数启动,示例:`tss 9898`
- 对于客户端程序`tsc`,请使用`tsc --help`查看使用方式
- 对于客户端程序`tsc`,请使用`tsc --help`查看如何启动
## 2.使用
@ -65,6 +67,41 @@ ${HOME}/截图/Ni.jpg|${HOME}/dira
${CURRENT}/xxx.zip|D:\
```
### 3.1 版本内容补充
- `v1.5.0`及其以后版本:支持数据加密传输功能(会影响速度,默认开启,可关闭)。
```c++
/*
测试环境 ==>
Microsoft Windows 10 Professional (x64) Build 19045.5608 (22H2)
13th Gen Intel(R) Core(TM) i5-13500H 3200.0 MHz
Debug模式 tinyaes 加密解密测试速度:
=========================================
File size: 630239232 bytes (601.043 MB)
Effective block size: 102384 bytes
Total encryption time: 41887336 μs (14.349 MB/s)
Total decryption time: 41822620 μs (14.3712 MB/s)
Data verification: PASSED
=========================================
Release模式 tinyaes 加密解密测试速度:
=========================================
File size: 630239232 bytes (601.043 MB)
Effective block size: 102384 bytes
Total encryption time: 8367460 μs (71.831 MB/s)
Total decryption time: 8150036 μs (73.7473 MB/s)
Data verification: PASSED
=========================================
*/
```
- `v1.5.2`及其以后的代码版本:新增了`tss-http`服务端,简单用于某些时候,客户机上没有`tsc``tss`程序时,通过`http`协议传输文件。
> 关于`tss-http`编译:不支持`mingw`编译,因此`win8`及以下没有此程序支持。
> 示例启动:`tss-http 8080 D:/files`(参数为端口、根目录)。
# 三、编译
当前项目支持`cmake`构建工具。

@ -6,11 +6,6 @@ set(CMAKE_CXX_STANDARD 17)
if (MSVC)
add_compile_options(/source-charset:utf-8)
endif()
if(DEFINED USE_BOOST)
message(STATUS "tsc use boost lib.")
include_directories(${MBOOST_INCLUDE_DIR})
link_directories(${MBOOST_LIB_DIR})
endif()
add_executable(tsc main.cpp client.h client.cpp config.h config.cpp)
target_link_libraries(tsc PRIVATE trans_net trans_util filecomplete)
@ -20,9 +15,10 @@ endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(tsc PRIVATE ws2_32 wsock32)
endif()
if(DEFINED USE_BOOST)
target_link_directories(tsc PRIVATE ${MBOOST_LIB_DIR})
target_link_libraries(tsc PRIVATE ${MBOOST_LIBS})
if (DEFINED GRAB_CRASH)
message(STATUS "tsc link crashelper")
target_link_libraries(tsc PRIVATE crashelper)
endif()
if(UNIX)
execute_process(

@ -1,27 +1,21 @@
#include "client.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <of_path.h>
#include <of_str.h>
#include <of_util.h>
#include <version.h>
#ifdef USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
CClient::CClient() : msg_info_("")
TransmClient::TransmClient() : msg_info_("")
{
client_ = std::make_shared<CTcpClient>(io_context_);
sleep_.set_timeout(5000);
}
CClient::~CClient()
TransmClient::~TransmClient()
{
th_run_ = false;
sleep_.contiune();
@ -48,9 +42,12 @@ CClient::~CClient()
if (hearts_.joinable()) {
hearts_.join();
}
if (context_th_.joinable()) {
context_th_.join();
}
}
void CClient::print_help(bool detail)
void TransmClient::print_help(bool detail)
{
TLOGI("version: {}", VERSION_NUM);
TLOGI("opensource: {}", VERSION_URL);
@ -58,8 +55,9 @@ void CClient::print_help(bool detail)
if (!detail) {
TLOGW("Get|Who|Where|Ls|Sub|Fetch|Up|Down|UpTask|DownTask");
TLOGI("You can use 'h' to show cmd's detail.");
TLOGI("You can use 'end' or 'ctrl-c' to exit.");
TLOGI("<h> to show cmd's detail.");
TLOGI("use <end> or <ctrl-c> to exit.");
TLOGI("use <on> or <off> to oper encrypt(<?> to view).");
return;
}
@ -122,7 +120,8 @@ void CClient::print_help(bool detail)
TLOGI("{}", sp);
}
void CClient::run(const std::string& ip, const std::string& port, const std::string& config_dir)
bool TransmClient::base_init(const std::string& ip, const std::string& port,
const std::string& config_dir)
{
fs::path fp(config_dir);
config_path_ = fp.append("history.txt").string();
@ -133,7 +132,7 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
uuid_ = read_uuid();
if (uuid_.empty()) {
TLOGE("uuid is empty!");
return;
return false;
}
auto his = load_line_his();
@ -144,13 +143,22 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
th_run_ = true;
if (!client_->connect(ip, port)) {
TLOGI("{} connect err.", __FUNCTION__);
return;
return false;
}
client_->register_func([&](CFrameBuffer* buf) { handle_frame(buf); });
client_->async_recv();
hearts_ = std::thread([&]() { hearts(); });
std::thread thread([&]() { io_context_.run(); });
context_th_ = std::thread([&]() { io_context_.run(); });
th_down_active_ = std::thread([&]() { judget_down_active(); });
return true;
}
void TransmClient::run(const std::string& ip, const std::string& port,
const std::string& config_dir)
{
if (!base_init(ip, port, config_dir)) {
return;
}
print_help(false);
fc_append('|');
@ -166,7 +174,8 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
break;
}
if (!th_run_ || !client_->is_normal()) {
TLOGW("The link has been closed and cannot be continued. It will automatically exit.");
TLOGW("The link has been closed and cannot be continued. It will "
"automatically exit.");
break;
}
std::string cmd_input(readline);
@ -179,6 +188,23 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
continue;
}
if (cmd_input == "on" || cmd_input == "On") {
set_encrypt(true);
TLOGI("Encrypt is on.");
continue;
}
if (cmd_input == "?") {
TLOGI("Encrypt is {}.", get_encrypt_status() ? "on" : "off");
continue;
}
if (cmd_input == "off" || cmd_input == "Off") {
set_encrypt(false);
TLOGI("Encrypt is off.");
continue;
}
if (cmd_input == "end" || cmd_input == "End") {
th_run_ = false;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
@ -193,11 +219,13 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
TLOGD("At => {}", COfPath::to_full("."));
continue;
}
if (cmd_input == "Get" || cmd_input == "get" || cmd_input == "g" || cmd_input == "G") {
if (cmd_input == "Get" || cmd_input == "get" || cmd_input == "g" ||
cmd_input == "G") {
get_clients();
continue;
}
if (cmd_input == "Clear" || cmd_input == "clear" || cmd_input == "c" || cmd_input == "C") {
if (cmd_input == "Clear" || cmd_input == "clear" || cmd_input == "c" ||
cmd_input == "C") {
cmd_clear_submited();
continue;
}
@ -241,18 +269,63 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
TLOGE("No matched cmd, May be param size incorrect.");
}
client_->disconnect();
thread.join();
TLOGI("{} exit.", __FUNCTION__);
}
bool CClient::get_clients()
#ifdef USE_TRANSM_TEST
bool TransmClient::connect_for_test(const std::string& ip,
const std::string& port,
const std::string& config_dir)
{
if (!base_init(ip, port, config_dir)) {
return false;
}
get_clients();
get_id();
return true;
}
std::string TransmClient::test_get_own_id() const
{
return own_id_;
}
void TransmClient::set_task_state(TaskState state)
{
task_state_ = state;
}
TransmClient::TaskState TransmClient::get_task_state() const
{
return task_state_;
}
void TransmClient::disconnect_for_test()
{
client_->disconnect();
}
int TransmClient::test_index_by_id(const std::string& id)
{
int ret = -1;
for (const auto& item : clients_) {
if (item.second->id == id) {
ret = item.first;
break;
}
}
return ret;
}
#endif
bool TransmClient::get_clients()
{
std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
buf->type_ = TYPE_GET_LIST;
return send_frame(buf.get());
}
bool CClient::cmd_fetch_files(const std::string& param)
bool TransmClient::cmd_fetch_files(const std::string& param)
{
if (downloading_) {
TLOGW("Have Task Downloading, Please wait.....");
@ -288,21 +361,31 @@ bool CClient::cmd_fetch_files(const std::string& param)
}
// 开始传输文件
bool ret = true;
for (const auto& item : vec) {
if (!down_one_file(id, item, relative_path)) {
ret = false;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
#ifdef USE_TRANSM_TEST
if (ret) {
task_state_ = TaskState::TASK_STATE_DONE;
} else {
task_state_ = TaskState::TASK_STATE_ERROR;
}
#endif
return true;
}
bool CClient::cmd_sub_list(const std::string& param)
bool TransmClient::cmd_sub_list(const std::string& param)
{
{
std::lock_guard<std::mutex> lock(mutex_);
for (const auto& item : up_) {
if (item.second->trans_state_ == TRANS_REDAY || item.second->trans_state_ == TRANS_ING) {
if (item.second->trans_state_ == TRANS_REDAY ||
item.second->trans_state_ == TRANS_ING) {
TLOGW("Have Task Upping, Please wait!");
return false;
}
@ -349,12 +432,13 @@ bool CClient::cmd_sub_list(const std::string& param)
return send_frame(buf.get());
}
bool CClient::cmd_clear_submited()
bool TransmClient::cmd_clear_submited()
{
{
std::lock_guard<std::mutex> lock(mutex_);
for (const auto& item : up_) {
if (item.second->trans_state_ == TRANS_REDAY || item.second->trans_state_ == TRANS_ING) {
if (item.second->trans_state_ == TRANS_REDAY ||
item.second->trans_state_ == TRANS_ING) {
TLOGW("Have Task Upping, Please wait!");
return false;
}
@ -365,7 +449,7 @@ bool CClient::cmd_clear_submited()
return send_frame(buf.get());
}
bool CClient::cmd_upload_files(const std::string& param)
bool TransmClient::cmd_upload_files(const std::string& param)
{
auto tvec = COfStr::split(param, " ");
if (tvec.size() < 3) {
@ -438,13 +522,15 @@ bool CClient::cmd_upload_files(const std::string& param)
return true;
}
bool CClient::down_one_file(int remote_id, const std::string& file, const std::string& local_dir)
bool TransmClient::down_one_file(int remote_id, const std::string& file,
const std::string& local_dir)
{
std::string ret_id{};
std::string ret_uuid{};
if (clients_.count(remote_id) == 0) {
TLOGW("{} No Index Found {}, Try {}", __LINE__, remote_id, list_server_id_);
TLOGW("{} No Index Found {}, Try {}", __LINE__, remote_id,
list_server_id_);
ret_id = list_server_id_;
ret_uuid = list_server_uuid_;
} else {
@ -459,13 +545,17 @@ bool CClient::down_one_file(int remote_id, const std::string& file, const std::s
if (local_dir.empty()) {
down_->cur_file_ = COfPath::to_full(remote_file.filename().string());
} else {
down_->cur_file_ = fs::path(local_dir).append(remote_file.filename().string()).string();
down_->cur_file_ = fs::path(local_dir)
.append(remote_file.filename().string())
.string();
}
// 这里要先检查羁绊
if (ret_uuid == uuid_ && COfPath::is_same_dir(remote_file.string(), down_->cur_file_)) {
if (ret_uuid == uuid_ &&
COfPath::is_same_dir(remote_file.string(), down_->cur_file_)) {
// 处在同一个机器上的同目录下
TLOGE("You Can't Operate File In Same Dir And In Same Machine.", down_->cur_remote_file_);
TLOGE("You Can't Operate File In Same Dir And In Same Machine.",
down_->cur_remote_file_);
return false;
}
@ -483,7 +573,8 @@ bool CClient::down_one_file(int remote_id, const std::string& file, const std::s
msg_info.str = file;
serialize(msg_info, &buf->data_, buf->len_);
if (!send_frame(buf.get())) {
TLOGE("{} request open file [{}] send failed.", __FUNCTION__, down_->cur_remote_file_);
TLOGE("{} request open file [{}] send failed.", __FUNCTION__,
down_->cur_remote_file_);
down_->cur_remote_id_.clear();
down_->cur_remote_file_.clear();
return false;
@ -493,7 +584,8 @@ bool CClient::down_one_file(int remote_id, const std::string& file, const std::s
cur_down_size_ = 0;
float percent = 0.0;
fc_disable_cur();
while (down_->trans_state_ != TRANS_DONE && down_->trans_state_ != TRANS_FAILED) {
while (down_->trans_state_ != TRANS_DONE &&
down_->trans_state_ != TRANS_FAILED) {
std::this_thread::sleep_for(std::chrono::milliseconds(down_check_wait));
if (cur_file_size_ > 0) {
percent = (float)cur_down_size_ / cur_file_size_;
@ -511,7 +603,8 @@ bool CClient::down_one_file(int remote_id, const std::string& file, const std::s
}
fc_enable_cur();
if (cur_down_size_ > 0 && cur_file_size_ == cur_down_size_) {
TLOGW("down one file success, total:[{}/{}]", cur_down_size_, cur_file_size_);
TLOGW("down one file success, total:[{}/{}]", cur_down_size_,
cur_file_size_);
return true;
} else {
TLOGW("down one file {} failed, size not matched.", down_->cur_file_);
@ -523,7 +616,7 @@ bool CClient::down_one_file(int remote_id, const std::string& file, const std::s
}
}
void CClient::report_trans_ret(TransState state, const std::string& key)
void TransmClient::report_trans_ret(TransState state, const std::string& key)
{
std::shared_ptr<TransInfomation> t = nullptr;
if (key.empty()) {
@ -556,7 +649,7 @@ void CClient::report_trans_ret(TransState state, const std::string& key)
*/
bool CClient::cmd_sub_task(const std::string& param, bool is_send)
bool TransmClient::cmd_sub_task(const std::string& param, bool is_send)
{
auto tvec = COfStr::split(param, " ");
if (tvec.size() < 2) {
@ -642,7 +735,11 @@ bool CClient::cmd_sub_task(const std::string& param, bool is_send)
return false;
}
#ifdef USE_TRANSM_TEST
auto handel_ret = handle_user_select(mre, false);
#else
auto handel_ret = handle_user_select(mre, is_send);
#endif
if (handel_ret.empty()) {
TLOGE("handle_user_select not pass, abort action!");
return false;
@ -668,7 +765,8 @@ bool CClient::cmd_sub_task(const std::string& param, bool is_send)
return true;
}
bool CClient::variable_and_parse_files(const std::string& content, std::map<std::string, std::string>& files)
bool TransmClient::variable_and_parse_files(
const std::string& content, std::map<std::string, std::string>& files)
{
auto vec = COfStr::split(content, "\n");
bool valid = true;
@ -694,7 +792,8 @@ bool CClient::variable_and_parse_files(const std::string& content, std::map<std:
return valid;
}
bool CClient::down_update_file(const std::map<std::string, std::string>& files)
bool TransmClient::down_update_file(
const std::map<std::string, std::string>& files)
{
std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
buf->tid_ = list_server_id_;
@ -726,7 +825,8 @@ bool CClient::down_update_file(const std::map<std::string, std::string>& files)
return suc;
}
bool CClient::get_dir_files(const std::string& dir, std::string& out, std::string& error)
bool TransmClient::get_dir_files(const std::string& dir, std::string& out,
std::string& error)
{
fs::path p(dir);
out.clear();
@ -746,7 +846,7 @@ bool CClient::get_dir_files(const std::string& dir, std::string& out, std::strin
return true;
}
bool CClient::cmd_ls(const std::string& param)
bool TransmClient::cmd_ls(const std::string& param)
{
auto tvec = COfStr::split(param, " ");
if (tvec.size() < 2) {
@ -766,8 +866,8 @@ bool CClient::cmd_ls(const std::string& param)
buf->type_ = TYPE_GET_DIRFILES;
CMessageInfo msg_info(own_id_);
msg_info.str = path;
serialize(msg_info, &buf->data_, buf->len_);
buf->tid_ = sr->id;
serialize(msg_info, &buf->data_, buf->len_);
if (!send_frame(buf.get())) {
TLOGE("Send Failed {}", __LINE__);
@ -776,7 +876,7 @@ bool CClient::cmd_ls(const std::string& param)
return true;
}
bool CClient::cmd_down_list(const std::string& param)
bool TransmClient::cmd_down_list(const std::string& param)
{
auto tvec = COfStr::split(param, " ");
if (tvec.size() < 2) {
@ -785,7 +885,8 @@ bool CClient::cmd_down_list(const std::string& param)
}
int index = std::stoi(tvec[0]);
std::string lists = tvec[1];
std::string local = tvec.size() > 2 ? COfPath::to_full(tvec[2]) : COfPath::to_full("");
std::string local =
tvec.size() > 2 ? COfPath::to_full(tvec[2]) : COfPath::to_full("");
if (!clients_.count(index)) {
TLOGE("{} No Index Found {}.", __LINE__, index);
@ -812,7 +913,7 @@ bool CClient::cmd_down_list(const std::string& param)
return true;
}
bool CClient::send_frame(CFrameBuffer* buf)
bool TransmClient::send_frame(CFrameBuffer* buf)
{
char* out_buf{};
int out_len{};
@ -829,7 +930,7 @@ bool CClient::send_frame(CFrameBuffer* buf)
return true;
}
void CClient::save_line_his(const std::string& input)
void TransmClient::save_line_his(const std::string& input)
{
if (input.empty()) {
return;
@ -858,7 +959,7 @@ void CClient::save_line_his(const std::string& input)
}
}
std::vector<std::string> CClient::load_line_his()
std::vector<std::string> TransmClient::load_line_his()
{
std::vector<std::string> history;
if (!fs::exists(config_path_)) {
@ -877,7 +978,7 @@ std::vector<std::string> CClient::load_line_his()
return history;
}
std::string CClient::variable_and_reverse_files(const std::string& source)
std::string TransmClient::variable_and_reverse_files(const std::string& source)
{
auto vec = COfStr::split(source, "\n");
std::string result;
@ -916,7 +1017,7 @@ std::string CClient::variable_and_reverse_files(const std::string& source)
return result;
}
bool CClient::save_uuid()
bool TransmClient::save_uuid()
{
fs::path uuid_path(uuid_path_);
if (fs::exists(uuid_path)) {
@ -935,7 +1036,7 @@ bool CClient::save_uuid()
return true;
}
std::string CClient::read_uuid()
std::string TransmClient::read_uuid()
{
fs::path uuid_path(uuid_path_);
if (!fs::exists(uuid_path)) {
@ -952,7 +1053,7 @@ std::string CClient::read_uuid()
return uuid;
}
void CClient::get_id()
void TransmClient::get_id()
{
auto* bf = new CFrameBuffer();
bf->type_ = TYPE_GET_ID;
@ -963,7 +1064,7 @@ void CClient::get_id()
delete bf;
}
void CClient::handle_frame(CFrameBuffer* buf)
void TransmClient::handle_frame(CFrameBuffer* buf)
{
if (buf == nullptr) {
TLOGE("{} nullptr.", __FUNCTION__);
@ -1067,13 +1168,12 @@ void CClient::handle_frame(CFrameBuffer* buf)
if (!get_dir_files(msg_info.str, out, err)) {
TLOGE("Get Dir Files Failed. {}", err);
buf->type_ = TYPE_GET_DIRFILES_FAILED;
delete[] buf->data_;
msg_info.str = err;
} else {
buf->type_ = TYPE_GET_DIRFILES_DONE;
delete[] buf->data_;
msg_info.str = out;
}
msg_info.id = buf->tid_;
serialize(msg_info, &buf->data_, buf->len_);
std::swap(buf->tid_, buf->fid_);
if (!send_frame(buf)) {
@ -1128,10 +1228,12 @@ void CClient::handle_frame(CFrameBuffer* buf)
std::lock_guard<std::mutex> lock(mutex_);
up_[buf->fid_] = std::make_shared<TransInfomation>();
up_[buf->fid_]->cur_file_ = msg_info.str;
up_[buf->fid_]->file_.open(up_[buf->fid_]->cur_file_, std::ios::in | std::ios::binary);
up_[buf->fid_]->file_.open(up_[buf->fid_]->cur_file_,
std::ios::in | std::ios::binary);
up_[buf->fid_]->trans_state_ = TRANS_REDAY;
if (!up_[buf->fid_]->file_.is_open()) {
TLOGE("Ready Send File {} Open Failed.", up_[buf->fid_]->cur_file_);
TLOGE("Ready Send File {} Open Failed.",
up_[buf->fid_]->cur_file_);
buf->type_ = TYPE_OPEN_FAILED;
std::swap(buf->tid_, buf->fid_);
if (!send_frame(buf)) {
@ -1143,7 +1245,8 @@ void CClient::handle_frame(CFrameBuffer* buf)
keys = buf->fid_;
}
if (!keys.empty()) {
ths_.emplace_back([this, keys]() { send_file_data_th(keys.c_str()); });
ths_.emplace_back(
[this, keys]() { send_file_data_th(keys.c_str()); });
}
break;
}
@ -1189,9 +1292,10 @@ void CClient::handle_frame(CFrameBuffer* buf)
TLOGE("{} GetList deserialize failed.", __LINE__);
break;
}
delete[] buf->data_;
msg_info.str = variable_and_reverse_files(msg_info.str);
msg_info.id = buf->tid_;
serialize(msg_info, &buf->data_, buf->len_);
std::swap(buf->tid_, buf->fid_);
buf->type_ = TYPE_REQUEST_UPDATE_LIST;
if (!send_frame(buf)) {
@ -1201,12 +1305,12 @@ void CClient::handle_frame(CFrameBuffer* buf)
break;
};
case TYPE_REQUEST_UPDATE_LIST: {
CMessageInfo msg_info(buf->fid_);
std::map<std::string, std::string> files;
if (down_ && down_->trans_state_ == TRANS_REDAY) {
TLOGW("Update Busy......, Ignore {}", buf->fid_);
buf->type_ = TYPE_BUSY_UPDATE_LIST;
} else {
CMessageInfo msg_info(buf->fid_);
if (!deserialize(buf->data_, buf->len_, msg_info)) {
TLOGE("{} GetList deserialize failed.", __LINE__);
break;
@ -1218,6 +1322,8 @@ void CClient::handle_frame(CFrameBuffer* buf)
buf->type_ = TYPE_UNCONFIRM_UPDATE_LIST;
}
}
msg_info.id = buf->tid_;
serialize(msg_info, &buf->data_, buf->len_);
std::swap(buf->tid_, buf->fid_);
if (!send_frame(buf)) {
TLOGE("Send Failed {}.", __LINE__);
@ -1231,7 +1337,8 @@ void CClient::handle_frame(CFrameBuffer* buf)
if (update_list_th_.joinable()) {
update_list_th_.join();
}
update_list_th_ = std::thread([this, files]() { down_update_file(files); });
update_list_th_ =
std::thread([this, files]() { down_update_file(files); });
break;
}
case TYPE_CONFIRM_UPDATE_LIST: {
@ -1240,10 +1347,16 @@ void CClient::handle_frame(CFrameBuffer* buf)
}
case TYPE_UNCONFIRM_UPDATE_LIST: {
TLOGE("remote {} check {} not passed!", buf->fid_, list_file_);
#ifdef USE_TRANSM_TEST
task_state_ = TaskState::TASK_STATE_ERROR;
#endif
break;
}
case TYPE_DONE_UPDATE_LIST: {
TLOGI("remote {} do task {} success!", buf->fid_, list_file_);
#ifdef USE_TRANSM_TEST
task_state_ = TaskState::TASK_STATE_DONE;
#endif
break;
}
case TYPE_FAILED_UPDATE_LIST: {
@ -1251,7 +1364,8 @@ void CClient::handle_frame(CFrameBuffer* buf)
break;
}
case TYPE_BUSY_UPDATE_LIST: {
TLOGI("remote {} are busy, will not exec task {}", buf->fid_, list_file_);
TLOGI("remote {} are busy, will not exec task {}", buf->fid_,
list_file_);
break;
}
case TYPE_FILE_INFO: {
@ -1276,12 +1390,13 @@ void CClient::handle_frame(CFrameBuffer* buf)
break;
}
default:
TLOGE("UnSupport Type {}, Current Version v{}", static_cast<int>(buf->type_), VERSION_NUM);
TLOGE("UnSupport Type {}, Current Version v{}",
static_cast<int>(buf->type_), VERSION_NUM);
break;
}
}
void CClient::send_file_data_th(const char* keys)
void TransmClient::send_file_data_th(const char* keys)
{
std::string str_key(keys);
std::shared_ptr<TransInfomation> t = nullptr;
@ -1305,9 +1420,11 @@ void CClient::send_file_data_th(const char* keys)
// ********************************************************
// seekg 用于读,seekp 用于写。
t->file_.seekg(0, std::ios::end);
long long size = t->file_.tellg();
t->file_.seekg(0, std::ios::beg);
// t->file_.seekg(0, std::ios::end);
// long long size = t->file_.tellg();
// t->file_.seekg(0, std::ios::beg);
auto size = fs::file_size(t->cur_file_);
buf->type_ = TYPE_FILE_INFO;
std::string str_size = std::to_string(size);
@ -1322,8 +1439,8 @@ void CClient::send_file_data_th(const char* keys)
#endif
std::string info_result = plat + "," + str_size + "," + str_perm;
TLOGI("To {} File Size: {} [{}], permissions:{}", str_key, ofen::OfUtil::get_file_size(size), size,
str_perm);
TLOGI("To {} File Size: {} [{}], permissions:{}", str_key,
ofen::OfUtil::get_file_size(size), size, str_perm);
msg_info.str = info_result;
serialize(msg_info, &buf->data_, buf->len_);
if (!send_frame(buf.get())) {
@ -1331,6 +1448,10 @@ void CClient::send_file_data_th(const char* keys)
TLOGE("Stop Trans {} To {} failed.", t->cur_file_, str_key);
return;
}
delete[] buf->data_;
buf->data_ = nullptr;
buf->type_ = TYPE_TRANS_FILE;
buf->mark_ = 1;
@ -1347,7 +1468,7 @@ void CClient::send_file_data_th(const char* keys)
cur_send_size = t->file_.gcount();
msg_info.data.resize(cur_send_size);
send_size += cur_send_size;
serialize(msg_info, &buf->data_, buf->len_);
serialize(msg_info, &buf->data_, buf->len_, true);
if (!send_frame(buf.get())) {
report_trans_ret(TRANS_FAILED, str_key);
TLOGE("Stop Trans {} To {} failed.", t->cur_file_, str_key);
@ -1363,7 +1484,7 @@ void CClient::send_file_data_th(const char* keys)
TLOGD("Trans File {} To {} Done !!!, {}", t->cur_file_, str_key, send_size);
}
void CClient::hearts()
void TransmClient::hearts()
{
std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
buf->type_ = TYPE_HEARTS;
@ -1376,7 +1497,7 @@ void CClient::hearts()
}
}
void CClient::judget_down_active()
void TransmClient::judget_down_active()
{
std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
buf->type_ = TYPE_JUDGE_ACTIVE;
@ -1392,13 +1513,15 @@ void CClient::judget_down_active()
}
}
std::string CClient::variable_handle(const std::string& task_list_path, const std::string& source,
bool is_local)
std::string TransmClient::variable_handle(const std::string& task_list_path,
const std::string& source,
bool is_local)
{
std::string result(source);
// 支持的变量如下:
// ${HOME} 用户目录(发送端接收端均支持)
// ${CURRENT} 任务文件所在目录(该变量仅支持发送端,因为接收端没有任务文件所在路径)
// ${CURRENT}
// 任务文件所在目录(该变量仅支持发送端,因为接收端没有任务文件所在路径)
if (is_local && source.find("${HOME}") != std::string::npos) {
result = COfStr::replace(result, "${HOME}", COfPath::get_home());
}
@ -1413,7 +1536,8 @@ std::string CClient::variable_handle(const std::string& task_list_path, const st
return result;
}
std::string CClient::handle_user_select(const std::unordered_map<int, std::string>& source, bool is_send)
std::string TransmClient::handle_user_select(
const std::unordered_map<int, std::string>& source, bool is_send)
{
std::string handled_content{};
std::string input{};
@ -1457,11 +1581,13 @@ std::string CClient::handle_user_select(const std::unordered_map<int, std::strin
handled_content.append(source.at(key) + "\n");
} else {
// 如果mre中没有这个key
TLOGE("Invalid input, please enter valid numbers or '0' for all.");
TLOGE("Invalid input, please enter valid numbers or "
"'0' for all.");
break;
}
} catch (const std::exception& e) {
TLOGE("Invalid input, please enter valid numbers or '0' for all.");
TLOGE("Invalid input, please enter valid numbers or '0' "
"for all.");
break;
}
}
@ -1477,7 +1603,8 @@ CFileOpr::CFileOpr() = default;
CFileOpr::~CFileOpr() = default;
bool CFileOpr::get_file_list(const std::string& input, std::vector<std::string>& out)
bool CFileOpr::get_file_list(const std::string& input,
std::vector<std::string>& out)
{
out.clear();
auto backup = COfStr::trim(input);
@ -1499,14 +1626,17 @@ bool CFileOpr::get_file_list(const std::string& input, std::vector<std::string>&
}
#endif
if (ret.find("?") != std::string::npos || ret.find("*") != std::string::npos) {
if (ret.find("?") != std::string::npos ||
ret.find("*") != std::string::npos) {
auto fv = COfPath::match_files(ret);
for (const auto& v : fv) {
TLOGI("match file: {}", v);
}
std::string cof;
while (true) {
TLOGI("Detected regex's file (num = {}), please confirm if it is correct? ", fv.size());
TLOGI("Detected regex's file (num = {}), please confirm if it "
"is correct? ",
fv.size());
TLOGW("support input in [y,Y,end]", fv.size());
std::getline(std::cin, cof);
if (cof == "y" || cof == "Y") {

@ -38,30 +38,34 @@ struct TransInfomation {
};
constexpr int down_check_wait = 100; // millsec
class CClient
class TransmClient
{
public:
CClient();
~CClient();
public:
void run(const std::string& ip, const std::string& port, const std::string& config_dir);
TransmClient();
~TransmClient();
public:
void run(const std::string& ip, const std::string& port,
const std::string& config_dir);
bool get_clients();
bool cmd_fetch_files(const std::string& param);
bool cmd_sub_list(const std::string& param);
bool cmd_clear_submited();
bool cmd_upload_files(const std::string& param);
bool down_one_file(int remote_id, const std::string& file, const std::string& local_dir = "");
void report_trans_ret(TransState state, const std::string& key = "");
bool cmd_sub_task(const std::string& param, bool is_send);
bool variable_and_parse_files(const std::string& content, std::map<std::string, std::string>& files);
bool down_update_file(const std::map<std::string, std::string>& files);
bool get_dir_files(const std::string& dir, std::string& out, std::string& error);
bool cmd_ls(const std::string& param);
bool cmd_down_list(const std::string& param);
private:
bool variable_and_parse_files(const std::string& content,
std::map<std::string, std::string>& files);
bool down_update_file(const std::map<std::string, std::string>& files);
bool get_dir_files(const std::string& dir, std::string& out,
std::string& error);
void report_trans_ret(TransState state, const std::string& key = "");
bool down_one_file(int remote_id, const std::string& file,
const std::string& local_dir = "");
private:
bool send_frame(CFrameBuffer* buf);
void save_line_his(const std::string& input);
@ -71,14 +75,19 @@ private:
std::string read_uuid();
void get_id();
void print_help(bool detail);
bool base_init(const std::string& ip, const std::string& port,
const std::string& config_dir);
private:
void handle_frame(CFrameBuffer* buf);
void send_file_data_th(const char* keys);
void hearts();
void judget_down_active();
std::string variable_handle(const std::string& task_list_path, const std::string& source, bool is_local);
std::string handle_user_select(const std::unordered_map<int, std::string>& source, bool is_send);
std::string variable_handle(const std::string& task_list_path,
const std::string& source, bool is_local);
std::string
handle_user_select(const std::unordered_map<int, std::string>& source,
bool is_send);
private:
std::mutex mutex_;
@ -96,6 +105,7 @@ private:
std::shared_ptr<TransInfomation> down_;
std::vector<std::thread> ths_;
std::map<std::string, std::shared_ptr<TransInfomation>> up_;
std::thread context_th_;
std::thread th_down_active_;
long long cur_file_size_{};
long long cur_down_size_{};
@ -109,6 +119,28 @@ private:
std::string config_path_{};
std::string uuid_path_{};
std::string uuid_{};
#ifdef USE_TRANSM_TEST
public:
enum TaskState {
TASK_STATE_IDLE,
TASK_STATE_RUNNING,
TASK_STATE_DONE,
TASK_STATE_ERROR
};
public:
bool connect_for_test(const std::string& ip, const std::string& port,
const std::string& config_dir);
void disconnect_for_test();
int test_index_by_id(const std::string& id);
std::string test_get_own_id() const;
void set_task_state(TaskState state);
TaskState get_task_state() const;
private:
TaskState task_state_{};
#endif
};
class CFileOpr
@ -118,5 +150,6 @@ public:
~CFileOpr();
public:
static bool get_file_list(const std::string& input, std::vector<std::string>& out);
static bool get_file_list(const std::string& input,
std::vector<std::string>& out);
};

@ -1,22 +1,16 @@
#include "config.h"
#include <cassert>
#ifdef USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
CServerConfig::CServerConfig()
ClientConfig::ClientConfig()
{
}
CServerConfig::~CServerConfig() = default;
ClientConfig::~ClientConfig() = default;
bool CServerConfig::baseInit()
bool ClientConfig::baseInit()
{
fs::path tpath(COfPath::get_config_dir("transm", true));
config_dir_ = tpath.string();
@ -33,7 +27,7 @@ bool CServerConfig::baseInit()
return true;
}
bool CServerConfig::read_ini(std::vector<TransmSet>& set)
bool ClientConfig::read_ini(std::vector<TransmSet>& set)
{
assert(init_ == true);
long groups = ini_handle_.GetLongValue("BASE", "GROUPS");
@ -59,7 +53,8 @@ bool CServerConfig::read_ini(std::vector<TransmSet>& set)
return true;
}
long CServerConfig::have_ini(const std::vector<TransmSet>& set, const std::string& ip, long port)
long ClientConfig::have_ini(const std::vector<TransmSet>& set,
const std::string& ip, long port)
{
long id = -1;
for (const auto& item : set) {
@ -71,20 +66,22 @@ long CServerConfig::have_ini(const std::vector<TransmSet>& set, const std::strin
return id;
}
bool CServerConfig::write_ini(const std::vector<TransmSet>& set)
bool ClientConfig::write_ini(const std::vector<TransmSet>& set)
{
assert(init_ == true);
for (size_t start = 0; start < set.size(); ++start) {
std::string key = "GROUP" + std::to_string(start);
ini_handle_.SetValue(key.c_str(), "IP", set[start].ip.c_str());
ini_handle_.SetLongValue(key.c_str(), "PORT", set[start].port);
ini_handle_.SetValue(key.c_str(), "COMMENT", set[start].comment.c_str());
ini_handle_.SetValue(key.c_str(), "COMMENT",
set[start].comment.c_str());
}
ini_handle_.SaveFile(config_path_.c_str());
return true;
}
long CServerConfig::append_ini(const std::string& ip, long port, const std::string& comment)
long ClientConfig::append_ini(const std::string& ip, long port,
const std::string& comment)
{
assert(init_ == true);
long id = -1;
@ -109,22 +106,24 @@ long CServerConfig::append_ini(const std::string& ip, long port, const std::stri
return id;
}
bool CServerConfig::remove_ini(long num)
bool ClientConfig::remove_ini(long num)
{
assert(init_ == true);
std::vector<TransmSet> set;
if (!read_ini(set)) {
return false;
}
set.erase(
std::remove_if(set.begin(), set.end(), [&num](const TransmSet& item) { return item.grp_id == num; }),
set.end());
set.erase(std::remove_if(
set.begin(), set.end(),
[&num](const TransmSet& item) { return item.grp_id == num; }),
set.end());
ini_handle_.Reset();
ini_handle_.SetLongValue("BASE", "GROUPS", static_cast<long>(set.size()));
return write_ini(set);
}
bool CServerConfig::get_ini(const std::vector<TransmSet>& set, long num, TransmSet& use)
bool ClientConfig::get_ini(const std::vector<TransmSet>& set, long num,
TransmSet& use)
{
bool find = false;
for (const auto& item : set) {
@ -137,12 +136,12 @@ bool CServerConfig::get_ini(const std::vector<TransmSet>& set, long num, TransmS
return find;
}
std::string CServerConfig::get_config_dir() const
std::string ClientConfig::get_config_dir() const
{
return config_dir_;
}
bool CServerConfig::save_last_use(const std::string& ip, long port)
bool ClientConfig::save_last_use(const std::string& ip, long port)
{
assert(init_ == true);
ini_handle_.SetValue("Base", "LastUseIP", ip.c_str());
@ -151,10 +150,11 @@ bool CServerConfig::save_last_use(const std::string& ip, long port)
return true;
}
bool CServerConfig::get_last_use(std::string& ip, long& port)
bool ClientConfig::get_last_use(std::string& ip, long& port)
{
assert(init_ == true);
if (!ini_handle_.KeyExists("Base", "LastUseIP") || !ini_handle_.KeyExists("Base", "LastUsePort")) {
if (!ini_handle_.KeyExists("Base", "LastUseIP") ||
!ini_handle_.KeyExists("Base", "LastUsePort")) {
TLOGE("Not Found Last Use Record.");
return false;
}
@ -163,7 +163,7 @@ bool CServerConfig::get_last_use(std::string& ip, long& port)
return true;
}
void CServerConfig::gen_default_ini(const std::string& path)
void ClientConfig::gen_default_ini(const std::string& path)
{
TLOGW("Gen Default Setting Ini in [{}].", path);
ini_handle_.LoadFile(path.c_str());

@ -25,11 +25,11 @@ struct CmdParam {
bool null_use{false};
};
class CServerConfig
class ClientConfig
{
public:
CServerConfig();
~CServerConfig();
ClientConfig();
~ClientConfig();
public:
bool baseInit();

@ -16,27 +16,41 @@
#endif
#endif
std::shared_ptr<CServerConfig> g_Config = nullptr;
#if defined(GRAB_CRASH)
#include <filesystem>
namespace fs = std::filesystem;
#include <crashelper.h>
#endif
std::shared_ptr<ClientConfig> g_Config = nullptr;
int parse_cmd(int argc, char** argv, CmdParam& param)
{
std::string intro(
fmt::format("tsc cmd introduce, version: {}\nopensource: {}", VERSION_NUM, VERSION_URL));
fmt::format("tsc cmd introduce, version: {}\nopensource: {}",
VERSION_NUM, VERSION_URL));
CLI::App app(intro);
app.add_option("-u, --use", param.use_config, "使用服务器地址组(值为使用--show中显示的序号)");
app.add_option("-a, --append", param.appendValue, "添加服务器地址组(地址格式:127.0.0.1:9898:注释)");
app.add_option("-u, --use", param.use_config,
"使用服务器地址组(值为使用--show中显示的序号)");
app.add_option("-a, --append", param.appendValue,
"添加服务器地址组(地址格式:127.0.0.1:9898:注释)");
app.add_flag("-s, --show", param.showValue, "查看服务器地址组");
app.add_option("-r, --remove", param.removeValue, "移除服务器地址组(值为使用--show中显示的序号)");
app.add_flag("-d, --direct", param.direct_use, "添加服务器时直接使用此服务器。");
app.add_flag("-l, --last", param.last_use, "直接使用之前最后一次使用的服务器。");
app.add_option("-r, --remove", param.removeValue,
"移除服务器地址组(值为使用--show中显示的序号)");
app.add_flag("-d, --direct", param.direct_use,
"添加服务器时直接使用此服务器。");
app.add_flag("-l, --last", param.last_use,
"直接使用之前最后一次使用的服务器。");
app.add_flag("-n, --null", param.null_use, "先运行在选择服务器。");
app.add_option("-c, --connect", param.connectValue, "直连服务器((地址格式:127.0.0.1:9898)。");
app.add_option("-c, --connect", param.connectValue,
"直连服务器((地址格式:127.0.0.1:9898)。");
if (argc == 1) {
std::cout << app.help() << std::endl;
return 0;
}
// 这里的 CLI11_PARSE 在程序没有输入或者仅输入--help(-h)时,会直接返回,后面代码都不会执行。
// 这里的 CLI11_PARSE
// 在程序没有输入或者仅输入--help(-h)时,会直接返回,后面代码都不会执行。
// 当有自定义的参数被输入时,后面代码会执行。
try {
CLI11_PARSE(app, argc, argv);
@ -47,7 +61,8 @@ int parse_cmd(int argc, char** argv, CmdParam& param)
return 0;
}
bool select_server(const std::vector<TransmSet>& sets, std::string& ip, long& port)
bool select_server(const std::vector<TransmSet>& sets, std::string& ip,
long& port)
{
TLOGI("Please Select a Server:");
if (sets.empty()) {
@ -60,7 +75,8 @@ bool select_server(const std::vector<TransmSet>& sets, std::string& ip, long& po
if (server.comment.empty()) {
TLOGI("[{}] {}:{}", i + 1, server.ip, server.port);
} else {
TLOGI("[{}] {}:{} ({})", i + 1, server.ip, server.port, server.comment);
TLOGI("[{}] {}:{} ({})", i + 1, server.ip, server.port,
server.comment);
}
}
@ -76,8 +92,10 @@ bool select_server(const std::vector<TransmSet>& sets, std::string& ip, long& po
}
// 检查输入是否为空或非数字
if (input.empty() || !std::all_of(input.begin(), input.end(), ::isdigit)) {
TLOGE("Invalid input '{}'. Please enter a valid number or 'exit'.", input);
if (input.empty() ||
!std::all_of(input.begin(), input.end(), ::isdigit)) {
TLOGE("Invalid input '{}'. Please enter a valid number or 'exit'.",
input);
continue;
}
@ -86,7 +104,9 @@ bool select_server(const std::vector<TransmSet>& sets, std::string& ip, long& po
// 检查数字是否在有效范围内
if (choice < 1 || choice > static_cast<int>(sets.size())) {
TLOGE("Invalid choice '{}'. Please select a number between 1 and {}.", choice, sets.size());
TLOGE(
"Invalid choice '{}'. Please select a number between 1 and {}.",
choice, sets.size());
continue;
}
@ -109,7 +129,8 @@ bool exec_cmd(CmdParam& param, bool& run)
return false;
}
for (const auto& item : set) {
TLOGI("{} => {}:{} {}", item.group, item.ip, item.port, item.comment);
TLOGI("{} => {}:{} {}", item.group, item.ip, item.port,
item.comment);
}
return true;
}
@ -122,7 +143,8 @@ bool exec_cmd(CmdParam& param, bool& run)
return false;
}
if (!param.appendValue.empty()) {
std::regex pattern(R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)(?::(.*))?)");
std::regex pattern(
R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)(?::(.*))?)");
std::smatch matches;
std::string ip, port, comment;
if (std::regex_match(param.appendValue, matches, pattern)) {
@ -141,7 +163,8 @@ bool exec_cmd(CmdParam& param, bool& run)
}
if (!param.removeValue.empty()) {
if (!g_Config->remove_ini(std::stol(param.removeValue))) {
TLOGW("remove config num=[{}] failed, please check!", param.removeValue);
TLOGW("remove config num=[{}] failed, please check!",
param.removeValue);
return false;
}
TLOGI("remove config num=[{}] success!", param.removeValue);
@ -156,6 +179,14 @@ bool exec_cmd(CmdParam& param, bool& run)
int main(int argc, char* argv[])
{
#if defined(GRAB_CRASH)
auto config_dir = COfPath::get_config_dir("transm", true);
auto err = fs::path(config_dir).append("errs").string();
backward::SetDumpFileSavePath(err);
backward::SetDumpLogSavePath(err);
CRASHELPER_MARK_ENTRY();
#endif
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode;
@ -169,7 +200,7 @@ int main(int argc, char* argv[])
delete p;
});
g_Config = std::make_shared<CServerConfig>();
g_Config = std::make_shared<ClientConfig>();
if (!g_Config->baseInit()) {
return -1;
}
@ -188,7 +219,8 @@ int main(int argc, char* argv[])
std::regex pattern(R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+))");
std::smatch matches;
if (std::regex_match(param.connectValue, matches, pattern) && matches.size() == 3) {
if (std::regex_match(param.connectValue, matches, pattern) &&
matches.size() == 3) {
ip = matches[1].str();
port = std::stol(matches[2].str());
run = true;
@ -232,10 +264,11 @@ int main(int argc, char* argv[])
}
g_Config->save_last_use(ip, port);
}
TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__,
VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
TLOGI("use ip => [{}], port => [{}]", ip, port);
CClient client;
client.run(ip, std::to_string(port), g_Config->get_config_dir());
std::shared_ptr<TransmClient> client = std::make_shared<TransmClient>();
client->run(ip, std::to_string(port), g_Config->get_config_dir());
TLOGI("exit ==========");
return 0;
}

@ -1,6 +1,3 @@
set(MBOOST_INCLUDE_DIR "C:/boost/include/boost-1_83")
set(MBOOST_LIB_DIR "C:/boost/lib")
set(MBOOST_LIBS "boost_filesystem-mgw7-mt-x32-1_83")
get_filename_component(CXX_COMPILER_PATH ${CMAKE_CXX_COMPILER} DIRECTORY)
set(MINGW32_DLLS
"${CXX_COMPILER_PATH}/libgcc_s_dw2-1.dll"

1
crashelper Submodule

@ -0,0 +1 @@
Subproject commit 1d84054da9ec3a70ae1311d6166a9404a10a356c

@ -1 +1 @@
Subproject commit 4b6612cc63f21b4d092a0b5731ceb7f817f20d23
Subproject commit 1a35e4371945b7c439a95a3e42612e9870e03e65

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.16)
project(tss-http LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (MSVC)
add_compile_options(/source-charset:utf-8)
endif()
add_executable(tss-http main.cpp)
if (UNIX)
target_link_libraries(tss-http PRIVATE pthread)
endif()
if(UNIX)
execute_process(
COMMAND uname -a
OUTPUT_VARIABLE UNAME_OUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(UNAME_OUT MATCHES "alpine" OR UNAME_OUT MATCHES "Alpine")
message(STATUS "tss-http on musl static link")
target_link_libraries(tss-http PRIVATE -static;-static-libstdc++)
endif()
endif()

264
http-server/main.cpp Normal file

@ -0,0 +1,264 @@
#include <filesystem>
#include <fstream>
#include <httplib.h>
#include <iostream>
#include <string>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
namespace fs = std::filesystem;
#ifdef _WIN32
std::string u8_to_ansi(const std::string& str)
{
int wideCharLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
if (wideCharLen <= 0) {
return "";
}
std::wstring wideStr(wideCharLen, L'\0');
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideStr[0], wideCharLen);
int gbkLen = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (gbkLen <= 0) {
return "";
}
std::string gbkStr(gbkLen, '\0');
WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &gbkStr[0], gbkLen, nullptr, nullptr);
gbkStr.resize(gbkLen - 1);
return gbkStr;
}
std::string ansi_to_u8(const std::string& str)
{
int wideCharLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
if (wideCharLen <= 0) {
return "";
}
std::wstring wideStr(wideCharLen, L'\0');
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wideStr[0], wideCharLen);
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (utf8Len <= 0) {
return "";
}
std::string utf8Str(utf8Len, '\0');
WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8Str[0], utf8Len, nullptr, nullptr);
utf8Str.resize(utf8Len - 1);
return utf8Str;
}
#endif
// 生成文件列表的HTML页面
std::string generate_file_list(const std::string& base_path, const std::string& current_path)
{
fs::path full_path = fs::path(base_path) / current_path;
std::string html = R"(
<!DOCTYPE html>
<html>
<head>
<title>File Browser</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #333; }
.path { color: #666; margin-bottom: 20px; }
ul { list-style-type: none; padding: 0; }
li { margin: 8px 0; }
a { text-decoration: none; color: #0066cc; }
a:hover { text-decoration: underline; }
.file::before { content: "\1F4C4 "; }
.dir::before { content: "\1F4C1 "; }
.parent::before { content: "\2B06 "; }
.size { color: #888; font-size: 0.9em; margin-left: 10px; }
</style>
</head>
<body>
<h1>File Browser</h1>
<div class="path">Current path: /)" +
current_path + R"(</div>
<ul>
)";
// 添加返回上一级链接(如果不是根目录)
if (current_path != "") {
fs::path parent_path = fs::path(current_path).parent_path();
std::string parent_link = parent_path.empty() ? "" : parent_path.string();
html += R"(<li class="parent"><a href="/browse/)" + parent_link + R"(">..</a></li>)";
}
// 遍历目录下的文件和子目录
for (const auto& entry : fs::directory_iterator(full_path)) {
std::string filename = entry.path().filename().string();
std::string new_path = (fs::path(current_path) / filename).string();
if (entry.is_directory()) {
html += R"(<li class="dir"><a href="/browse/)" + new_path + R"(">)" + filename + R"(/</a></li>)";
} else {
// 获取文件大小并转换为MB
uintmax_t file_size = entry.file_size();
double size_mb = file_size / (1024.0 * 1024.0);
char size_str[20];
snprintf(size_str, sizeof(size_str), "%.4f MB", size_mb);
html += R"(<li class="file"><a href="/download/)" + new_path + R"(">)" + filename + R"(</a><span class="size">)" +
size_str + R"(</span></li>)";
}
}
html += R"(
</ul>
</body>
</html>
)";
#ifdef _WIN32
html = ansi_to_u8(html);
#endif
return html;
}
int main(int argc, char** argv)
{
if (argc != 3) {
std::cout << "Usage: " << argv[0] << " <port> <base_dir>" << std::endl;
return -2;
}
const std::string base_dir(argv[2]); // 基础文件目录
if (!fs::exists(base_dir)) {
std::cout << "Base directory does not exist: " << base_dir << std::endl;
return -1;
}
int port = std::stoi(argv[1]);
httplib::Server server;
// 确保基础目录存在
if (!fs::exists(base_dir)) {
fs::create_directory(base_dir);
}
// 文件浏览路由
server.Get("/browse(.*)", [&base_dir](const httplib::Request& req, httplib::Response& res) {
std::string path = req.matches[1].str();
#ifdef _WIN32
path = u8_to_ansi(path);
#endif
// 移除开头的斜杠
if (!path.empty() && path[0] == '/') {
path = path.substr(1);
}
// 安全检查:防止目录遍历攻击
if (path.find("..") != std::string::npos) {
res.set_content("Invalid path", "text/plain");
res.status = 400;
return;
}
fs::path full_path = fs::path(base_dir) / path;
if (!fs::exists(full_path)) {
res.set_content("Path not found", "text/plain");
res.status = 404;
return;
}
if (!fs::is_directory(full_path)) {
res.set_content("Not a directory", "text/plain");
res.status = 400;
return;
}
res.set_header("Content-Type", "text/html; charset=utf-8");
res.set_content(generate_file_list(base_dir, path), "text/html; charset=utf-8");
});
// 文件下载路由
server.Get("/download/(.*)", [&base_dir](const httplib::Request& req, httplib::Response& res) {
std::string path = req.matches[1];
#ifdef _WIN32
path = u8_to_ansi(path);
#endif
// 安全检查:防止目录遍历攻击
if (path.find("..") != std::string::npos) {
res.set_content("Invalid path", "text/plain");
res.status = 400;
return;
}
fs::path file_path = fs::path(base_dir) / path;
if (!fs::exists(file_path)) {
res.set_content("File not found", "text/plain");
res.status = 404;
return;
}
if (fs::is_directory(file_path)) {
res.set_content("Cannot download directory", "text/plain");
res.status = 400;
return;
}
// 设置响应头,触发浏览器下载
res.set_header("Content-Type", "application/octet-stream");
res.set_header("Content-Disposition", "attachment; filename=" + file_path.filename().string());
auto file = std::make_shared<std::ifstream>(file_path, std::ios::binary);
if (!*file) {
res.status = 500;
res.set_content("Failed to open file", "text/plain");
return;
}
// 获取文件大小
const size_t file_size = fs::file_size(file_path);
// 定义分块回调(严格匹配 ContentProvider 签名)
auto provider = [file](size_t offset, size_t length, httplib::DataSink& sink) {
file->seekg(offset);
const size_t chunk_size = std::min<size_t>(1024 * 1024, length); // 64KB或剩余长度
std::vector<char> buffer(chunk_size);
size_t remaining = length;
while (remaining > 0 && sink.is_writable() && *file) {
size_t read_size = std::min(buffer.size(), remaining);
file->read(buffer.data(), read_size);
size_t bytes_read = file->gcount();
if (bytes_read > 0) {
sink.write(buffer.data(), bytes_read);
remaining -= bytes_read;
} else {
break;
}
}
return true;
};
// 定义资源清理回调
auto releaser = [file](bool /*success*/) { file->close(); };
// 调用 set_content_provider
res.set_content_provider(file_size, // 文件总大小
"application/octet-stream", // MIME类型
provider, // 数据提供回调
releaser // 资源清理回调
);
});
// 根目录重定向到/browse
server.Get("/", [](const httplib::Request& req, httplib::Response& res) { res.set_redirect("/browse"); });
std::cout << "Server running at http://localhost:" << port << std::endl;
std::cout << "Access the root path to browse files.\n";
if (!server.listen("0.0.0.0", port)) {
std::cerr << "Failed to start server\n";
return -1;
}
return 0;
}

@ -1,7 +1,7 @@
@echo off
cmake -Bxpbuild -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DXP_SYSTEM=ON -DUSE_BOOST=ON
cd xpbuild
cmake -BMinGWBuild -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
cd MinGWBuild
cpack
if %errorlevel% neq 0 (
echo Error: cmake build failed.

2
ofen

@ -1 +1 @@
Subproject commit fbc26d565f7b73418dd9bfaee3fc67b77a56daba
Subproject commit e1624c71451febf956a98bc692c0e886366f4c34

@ -6,23 +6,18 @@ set(CMAKE_CXX_STANDARD 17)
if (MSVC)
add_compile_options(/source-charset:utf-8)
endif()
if(DEFINED USE_BOOST)
message(STATUS "tss use boost lib.")
include_directories(${MBOOST_INCLUDE_DIR})
endif()
add_executable(tss main.cpp server.h server.cpp)
target_link_libraries(tss PRIVATE trans_net trans_util)
if(DEFINED USE_BOOST)
target_link_directories(tss PRIVATE ${MBOOST_LIB_DIR})
target_link_libraries(tss PRIVATE ${MBOOST_LIBS})
endif()
if (UNIX)
target_link_libraries(tss PRIVATE pthread)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(tss PRIVATE ws2_32 wsock32)
endif()
if (DEFINED GRAB_CRASH)
target_link_libraries(tss PRIVATE crashelper)
endif()
if(UNIX)
execute_process(
COMMAND uname -a

@ -3,6 +3,9 @@
#include "server.h"
#include "version.h"
#include <filesystem>
#include <of_path.h>
namespace fs = std::filesystem;
#ifdef _WIN32
#include <fcntl.h>
@ -13,17 +16,28 @@
#endif
#endif
void signal_handler(int signal)
#if defined(GRAB_CRASH)
#include <crashelper.h>
#endif
void msignal_handler(int signal)
{
if (signal == SIGINT) {
fc_recovery_color();
exit(signal);
}
fc_recovery_color();
exit(signal);
}
int main(int argc, char* argv[])
{
std::signal(SIGINT, signal_handler);
#if defined(GRAB_CRASH)
auto config_dir = COfPath::get_config_dir("transm", true);
auto err = fs::path(config_dir).append("errs").string();
backward::SetDumpFileSavePath(err);
backward::SetDumpLogSavePath(err);
CRASHELPER_MARK_ENTRY();
sh.register_user_sig_handler([](int sig) { msignal_handler(sig); });
#else
signal(SIGINT, msignal_handler);
#endif
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
@ -38,7 +52,8 @@ int main(int argc, char* argv[])
delete p;
});
TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__,
VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
int port = 9898;
if (argc < 2) {
TLOGI("Use Default Port:{}", port);
@ -48,10 +63,10 @@ int main(int argc, char* argv[])
TLOGI("Use Port:{}", port);
}
asio::io_context io_context;
CTcpServer server(io_context);
TransmServer server(io_context);
if (!server.start(port)) {
return -1;
}
io_context.run();
return 0;
}
}

@ -7,13 +7,13 @@
using namespace ofen;
constexpr int check_idle_percycle = 1000 * 30; // 毫秒
constexpr int remove_after_time = 60; // 秒
CTcpServer::CTcpServer(asio::io_context& io_context) : io_context_(io_context), acceptor_(io_context)
TransmServer::TransmServer(asio::io_context& io_context) : io_context_(io_context), acceptor_(io_context)
{
th_run_ = true;
sleep_.set_timeout(check_idle_percycle);
}
CTcpServer::~CTcpServer()
TransmServer::~TransmServer()
{
th_run_ = false;
sleep_.contiune();
@ -22,7 +22,7 @@ CTcpServer::~CTcpServer()
}
}
bool CTcpServer::start(unsigned short port)
bool TransmServer::start(unsigned short port)
{
asio::ip::tcp::resolver resolver(io_context_);
asio::ip::tcp::resolver::query query(asio::ip::host_name(), "");
@ -63,7 +63,7 @@ bool CTcpServer::start(unsigned short port)
return true;
}
void CTcpServer::stop()
void TransmServer::stop()
{
acceptor_.close();
std::unique_lock<std::shared_mutex> lock(cli_mut_);
@ -75,14 +75,14 @@ void CTcpServer::stop()
client_threads_.clear();
}
void CTcpServer::get_client_list(CMessageInfo& msg_info)
void TransmServer::get_client_list(CMessageInfo& msg_info)
{
struct TmpInfo {
std::string id;
std::string online_time;
std::string uuid;
std::string task;
uint64_t timestamp;
uint64_t timestamp{};
};
std::vector<TmpInfo> vec;
std::string msg;
@ -101,8 +101,7 @@ void CTcpServer::get_client_list(CMessageInfo& msg_info)
}
// 排序 vec 根据 client->timestamp
std::sort(vec.begin(), vec.end(),
[](const TmpInfo& a, const TmpInfo& b) { return a.timestamp < b.timestamp; });
std::sort(vec.begin(), vec.end(), [](const TmpInfo& a, const TmpInfo& b) { return a.timestamp < b.timestamp; });
int index = 1;
for (const auto& item : vec) {
@ -117,7 +116,7 @@ void CTcpServer::get_client_list(CMessageInfo& msg_info)
msg_info.str = msg;
}
void CTcpServer::trans_data(CFrameBuffer* buf)
void TransmServer::trans_data(CFrameBuffer* buf)
{
std::shared_ptr<ClientCache> fcli = nullptr;
std::shared_ptr<ClientCache> tcli = nullptr;
@ -190,8 +189,7 @@ void CTcpServer::trans_data(CFrameBuffer* buf)
}
}
bool CTcpServer::check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>& fcli,
std::shared_ptr<ClientCache>& tcli)
bool TransmServer::check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>& fcli, std::shared_ptr<ClientCache>& tcli)
{
std::shared_lock<std::shared_mutex> lock(cli_mut_);
if (client_map_.count(buf->fid_)) {
@ -220,7 +218,7 @@ bool CTcpServer::check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>& f
return true;
}
void CTcpServer::accept_client()
void TransmServer::accept_client()
{
auto socket = std::make_shared<asio::ip::tcp::socket>(io_context_);
acceptor_.async_accept(*socket, [this, socket](const asio::error_code& error) {
@ -248,15 +246,14 @@ void CTcpServer::accept_client()
if (!can) {
std::this_thread::sleep_for(std::chrono::minutes(1));
} else {
client_threads_[client_key] = std::thread(&CTcpServer::th_client, this, socket, client_key);
client_threads_[client_key] = std::thread(&TransmServer::th_client, this, socket, client_key);
}
}
accept_client();
});
}
void CTcpServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socket,
const std::string& client_key)
void TransmServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socket, const std::string& client_key)
{
std::shared_ptr<int> deleter(new int(0), [&](int* p) {
std::unique_lock<std::shared_mutex> lock(cli_mut_);
@ -330,7 +327,7 @@ void CTcpServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socket,
}
}
bool CTcpServer::send_frame(const std::shared_ptr<asio::ip::tcp::socket>& socket, CFrameBuffer* buf)
bool TransmServer::send_frame(const std::shared_ptr<asio::ip::tcp::socket>& socket, CFrameBuffer* buf)
{
char* out_buf{};
int out_len{};
@ -344,21 +341,20 @@ bool CTcpServer::send_frame(const std::shared_ptr<asio::ip::tcp::socket>& socket
}
try {
if (!socket->send(asio::buffer(out_buf, out_len))) {
TLOGE("{} send failed, buf type:{}, fid:{}, tid:{}", __FUNCTION__, static_cast<int>(buf->type_),
buf->fid_, buf->tid_);
TLOGE("{} send failed, buf type:{}, fid:{}, tid:{}", __FUNCTION__, static_cast<int>(buf->type_), buf->fid_,
buf->tid_);
delete[] out_buf;
return false;
}
} catch (const std::exception& e) {
TLOGE("send failed, type:{}, fid:{}, tid:{}, mark:{}", static_cast<int>(buf->type_), buf->fid_,
buf->tid_, buf->mark_);
TLOGE("send failed, type:{}, fid:{}, tid:{}, mark:{}", static_cast<int>(buf->type_), buf->fid_, buf->tid_, buf->mark_);
}
delete[] out_buf;
return true;
}
void CTcpServer::monitor_idle()
void TransmServer::monitor_idle()
{
while (th_run_) {
sleep_.sleep();
@ -369,9 +365,7 @@ void CTcpServer::monitor_idle()
std::unique_lock<std::shared_mutex> lock(cli_mut_);
for (auto& item : client_map_) {
auto now = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::seconds>(now - item.second->last_active_time_)
.count();
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - item.second->last_active_time_).count();
if (duration >= remove_after_time) {
TLOGW("OnLine Time [{}] sec, Proactively disconnect:{}", duration, item.first);
remove_vec.push_back(item.first);

@ -13,22 +13,22 @@ using namespace ofen;
using high_c = std::chrono::time_point<std::chrono::high_resolution_clock>;
struct ClientCache {
std::shared_ptr<asio::ip::tcp::socket> socket_;
CMutBuffer buffer_{};
CMutBuffer buffer_;
std::array<char, g_BuffSize> tmp_buf_{};
std::string task_{};
std::string uuid{};
std::string task_time_{};
std::string online_time_{};
std::string task_;
std::string uuid;
std::string task_time_;
std::string online_time_;
uint64_t timestamp{};
high_c last_active_time_;
FrameType cur_type_{TYPE_DEFAULT};
};
class CTcpServer
class TransmServer
{
public:
CTcpServer(asio::io_context& io_context);
~CTcpServer();
explicit TransmServer(asio::io_context& io_context);
~TransmServer();
public:
bool start(unsigned short port);
@ -39,8 +39,7 @@ private:
private:
void trans_data(CFrameBuffer* buf);
bool check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>& fcli,
std::shared_ptr<ClientCache>& tcli);
bool check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>& fcli, std::shared_ptr<ClientCache>& tcli);
private:
void accept_client();

@ -1,7 +1,42 @@
cmake_minimum_required(VERSION 3.16)
project(test LANGUAGES CXX)
project(transm_test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
add_executable(test main.cxx)
target_link_libraries(test PRIVATE tinyaes trans_util)
set(TEST_SOURCES
../3rd/catch_amalgamated.cpp
assistant.h
assistant.cxx
)
set(TRANSM_TEST_SOURCES
../3rd/catch_amalgamated.cpp
../client/client.cpp
../client/client.h
../client/config.cpp
../client/config.h
../util/util.h
../server/server.cpp
../server/server.h
../net/net_base.h
assistant.h
assistant.cxx
)
add_executable(encry_correct_test EncryptCorrect.cxx ${TEST_SOURCES})
add_executable(encry_speed_test EncryptSpeed.cxx ${TEST_SOURCES})
target_link_libraries(encry_correct_test PRIVATE tinyaes trans_util)
target_link_libraries(encry_speed_test PRIVATE tinyaes trans_util)
add_executable(transm_cmd_test Cmd.cxx ${TRANSM_TEST_SOURCES})
target_link_libraries(transm_cmd_test PRIVATE trans_net trans_util)
if (UNIX)
target_link_libraries(encry_speed_test PRIVATE pthread)
target_link_libraries(encry_correct_test PRIVATE pthread)
target_link_libraries(transm_cmd_test PRIVATE pthread)
endif()
enable_testing()
add_test(NAME EncryCorrectTest COMMAND encry_correct_test)
add_test(NAME EncrySpeedTest COMMAND encry_speed_test -s)
add_test(NAME CmdTest COMMAND transm_cmd_test)

241
test/Cmd.cxx Normal file

@ -0,0 +1,241 @@
#include <catch_amalgamated.hpp>
#include <cstdint>
#include <fstream>
#include "../client/client.h"
#include "../client/config.h"
#include "../server/server.h"
#include "../util/util.h"
#include "assistant.h"
std::shared_ptr<TransmServer> server;
std::shared_ptr<TransmClient> clientA;
std::shared_ptr<TransmClient> clientB;
std::shared_ptr<ClientConfig> config;
asio::io_context server_context;
constexpr auto ip = "127.0.0.1";
constexpr unsigned short port = 9897;
bool server_suc = false;
constexpr unsigned int max_wait = 3000;
constexpr unsigned int wait_interval = 100;
std::string str_id_a;
int ida_in_b = -1;
std::thread server_th;
std::string test_filea = "filea.dat";
std::string test_fileb = "fileb.dat";
std::string test_sub_dir = "test_sub";
std::string test_task_file = "test_task.txt";
bool test_ls();
bool random_ralated_files();
bool test_up_task(bool encrypt);
void server_run()
{
server = std::make_shared<TransmServer>(server_context);
if (!server->start(port)) {
server_suc = false;
return;
}
server_suc = true;
server_context.run();
}
bool base_connect()
{
config = std::make_shared<ClientConfig>();
if (!config->baseInit()) {
return false;
}
server_th = std::thread(server_run);
if (value_wait<bool>([]() -> bool { return server_suc; }, true,
std::equal_to<bool>(), max_wait,
wait_interval) == false) {
return false;
}
clientA = std::make_shared<TransmClient>();
if (clientA->connect_for_test(ip, std::to_string(port),
config->get_config_dir()) == false) {
return false;
}
clientB = std::make_shared<TransmClient>();
if (clientB->connect_for_test(ip, std::to_string(port),
config->get_config_dir()) == false) {
return false;
}
if (value_wait<std::string>(
[]() -> std::string { return clientA->test_get_own_id(); },
std::string(), std::not_equal_to<std::string>(), max_wait,
wait_interval) == false) {
return false;
}
if (value_wait<std::string>(
[]() -> std::string { return clientB->test_get_own_id(); },
std::string(), std::not_equal_to<std::string>(), max_wait,
wait_interval) == false) {
return false;
}
str_id_a = clientA->test_get_own_id();
std::cout << "clientA id: " << str_id_a << std::endl;
if (value_wait<int>(
[]() -> int { return clientB->test_index_by_id(str_id_a); }, -1,
std::not_equal_to<int>(), max_wait, wait_interval) == false) {
return false;
}
ida_in_b = clientB->test_index_by_id(str_id_a);
std::cout << "clientA index In B: " << ida_in_b << std::endl;
return true;
}
bool main_test()
{
ON_SCOPE_EXIT
{
fc_recovery_color();
};
if (!base_connect()) {
return false;
}
std::shared_ptr<int> deleter(new int(), [](int* p) {
if (clientA) {
clientA->disconnect_for_test();
}
if (clientB) {
clientB->disconnect_for_test();
}
if (server) {
server->stop();
}
server_context.stop();
if (server_th.joinable()) {
server_th.join();
}
delete p;
});
if (!test_ls()) {
return false;
}
if (!random_ralated_files()) {
return false;
}
if (!test_up_task(true)) {
return false;
}
if (!test_up_task(false)) {
return false;
}
std::this_thread::sleep_for(std::chrono::seconds(10));
return true;
}
// 测试 Ls
bool test_ls()
{
std::string cmd = std::to_string(ida_in_b) + " .";
if (!clientB->cmd_ls(cmd)) {
return false;
}
return true;
}
bool test_up_task(bool encrypt)
{
std::string cmd = std::to_string(ida_in_b) + " " + test_task_file;
auto fas = test_filea;
auto fat = test_sub_dir + "/" + test_filea;
auto fbs = test_fileb;
auto fbt = test_sub_dir + "/" + test_fileb;
ON_SCOPE_EXIT
{
if (fs::exists(fat)) {
fs::remove(fat);
}
if (fs::exists(fbt)) {
fs::remove(fbt);
}
};
set_encrypt(encrypt);
clientB->set_task_state(TransmClient::TaskState::TASK_STATE_IDLE);
if (!clientB->cmd_sub_task(cmd, true)) {
return false;
}
if (value_wait<TransmClient::TaskState>(
[&]() -> TransmClient::TaskState {
return clientB->get_task_state();
},
TransmClient::TaskState::TASK_STATE_IDLE,
std::not_equal_to<TransmClient::TaskState>(), max_wait * 2,
wait_interval) == false) {
return false;
}
auto r = clientB->get_task_state();
if (r != TransmClient::TaskState::TASK_STATE_DONE) {
return false;
}
if (!is_equal_filecontent(fas, fat)) {
return false;
}
if (!is_equal_filecontent(fbs, fbt)) {
return false;
}
std::cout << "****** up task done encrypt:" << encrypt << " ******"
<< std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return true;
}
bool random_ralated_files()
{
if (!random_file("filea.dat", 1024 * 1024 * 10)) {
return false;
}
if (!random_file("fileb.dat", 1024 * 1024 * 10)) {
return false;
}
if (fs::exists(test_sub_dir)) {
fs::remove_all(test_sub_dir);
}
fs::create_directories(test_sub_dir);
if (fs::exists(test_task_file)) {
fs::remove(test_task_file);
}
std::ofstream ofs(test_task_file);
ofs << "${CURRENT}/" << test_filea << "|" << test_sub_dir + "/"
<< std::endl;
ofs << test_fileb << "|" << test_sub_dir + "/" << std::endl;
ofs.close();
return true;
}
TEST_CASE("transm cmd part", "[cmd]")
{
SECTION("correctness of cmd")
{
REQUIRE(main_test() == true);
}
}

41
test/EncryptCorrect.cxx Normal file

@ -0,0 +1,41 @@
#include <aes.hpp>
#include <catch_amalgamated.hpp>
#include <fstream>
#include <string>
#include <util.h>
bool correctness_test()
{
std::string key = "demokey";
uint8_t ik[32]{};
hash(key.c_str(), ik);
int offset = 16;
char* msg = new char[256]{};
std::shared_ptr<int> deleter(new int(), [msg](int* p) {
delete p;
delete[] msg;
});
char source[] = "hello world";
memset(msg, 0, 256);
auto len = std::snprintf(msg + offset, 256 - offset, "%s", source);
if (!encrypt(ik, (uint8_t*)msg, len + offset)) {
return false;
}
uint8_t ik2[32]{};
hash(key.c_str(), ik2);
if (!decrypt(ik2, (uint8_t*)msg, len + offset)) {
return false;
}
return std::memcmp(source, msg + offset, len) == 0;
}
TEST_CASE("transm encry part", "[encry]")
{
SECTION("correctness of encryption")
{
REQUIRE(correctness_test() == true);
}
}

197
test/EncryptSpeed.cxx Normal file

@ -0,0 +1,197 @@
#include <aes.hpp>
#include <catch_amalgamated.hpp>
#include <fstream>
#include <string>
#include <util.h>
#include "assistant.h"
const size_t BLOCK_SIZE = 102400; // 100KB块大小
const size_t IV_SIZE = 16; // 随机值大小
struct SpeedRet {
std::string mode;
long long file_size;
long long encry_speed;
long long decry_speed;
bool verify;
};
bool test_speed(SpeedRet& ret)
{
std::string test_file("1.dat");
if (!random_file(test_file, 1024 * 1024 * 10)) {
std::cerr << "Failed to create test file" << std::endl;
return false;
}
ret.decry_speed = 0;
ret.encry_speed = 0;
ret.mode = "";
ret.verify = false;
std::shared_ptr<int> deleter(new int(1), [test_file](int* p) {
delete p;
if (fs::exists(test_file)) {
fs::remove(test_file);
}
});
if (!fs::exists(test_file)) {
std::cerr << "Input file not found: " << test_file << std::endl;
return false;
}
size_t file_size = fs::file_size(test_file);
ret.file_size = file_size / (1024 * 1024);
if (file_size == 0) {
std::cerr << "Input file is empty" << std::endl;
return false;
}
std::string key = "test_speed_key";
uint8_t ik[32]{};
hash(key.c_str(), ik);
fs::path decrypted_path = fs::path(test_file).replace_filename(
fs::path(test_file).stem().string() + "_decrypted" +
fs::path(test_file).extension().string());
std::ofstream decrypted_file(decrypted_path, std::ios::binary);
if (!decrypted_file) {
std::cerr << "Failed to create decrypted file" << std::endl;
return false;
}
std::ifstream in_file(test_file, std::ios::binary);
if (!in_file) {
std::cerr << "Failed to open input file" << std::endl;
return false;
}
// 测试数据缓冲区(额外预留16字节空间)
std::vector<uint8_t> original_block(BLOCK_SIZE);
std::vector<uint8_t> processing_block(BLOCK_SIZE +
IV_SIZE); // 加密/解密处理缓冲区
size_t total_bytes = 0;
size_t blocks_processed = 0;
bool verification_passed = true;
auto total_encrypt_time = std::chrono::microseconds(0);
auto total_decrypt_time = std::chrono::microseconds(0);
while (in_file) {
in_file.read(reinterpret_cast<char*>(original_block.data()),
BLOCK_SIZE - IV_SIZE);
size_t bytes_read = in_file.gcount();
if (bytes_read == 0)
break;
memcpy(processing_block.data() + IV_SIZE, original_block.data(),
bytes_read);
auto start_encrypt = std::chrono::high_resolution_clock::now();
if (!encrypt(ik, processing_block.data(), bytes_read + IV_SIZE)) {
std::cerr << "Encryption failed at block " << blocks_processed
<< std::endl;
verification_passed = false;
break;
}
auto end_encrypt = std::chrono::high_resolution_clock::now();
total_encrypt_time +=
std::chrono::duration_cast<std::chrono::microseconds>(
end_encrypt - start_encrypt);
auto start_decrypt = std::chrono::high_resolution_clock::now();
if (!decrypt(ik, processing_block.data(), bytes_read + IV_SIZE)) {
std::cerr << "Decryption failed at block " << blocks_processed
<< std::endl;
verification_passed = false;
break;
}
auto end_decrypt = std::chrono::high_resolution_clock::now();
total_decrypt_time +=
std::chrono::duration_cast<std::chrono::microseconds>(
end_decrypt - start_decrypt);
if (memcmp(original_block.data(), processing_block.data() + IV_SIZE,
bytes_read) != 0) {
std::cerr << "Data mismatch at block " << blocks_processed
<< std::endl;
verification_passed = false;
break;
}
decrypted_file.write(
reinterpret_cast<const char*>(processing_block.data() + IV_SIZE),
bytes_read);
total_bytes += bytes_read;
blocks_processed++;
}
in_file.close();
decrypted_file.close();
#if !defined(NDEBUG) || defined(_DEBUG) || defined(DEBUG)
// Debug 模式
ret.mode = "Debug";
#else
// Release 模式
ret.mode = "Release";
#endif
// 计算吞吐量(只计算有效数据部分)
double encrypt_throughput = (double)total_bytes / (1024 * 1024) /
(total_encrypt_time.count() / 1000000.0);
double decrypt_throughput = (double)total_bytes / (1024 * 1024) /
(total_decrypt_time.count() / 1000000.0);
ret.encry_speed = encrypt_throughput;
ret.decry_speed = decrypt_throughput;
ret.verify = verification_passed;
fs::remove(decrypted_path);
return verification_passed;
}
bool correctness_test()
{
std::string key = "demokey";
uint8_t ik[32]{};
hash(key.c_str(), ik);
int offset = 16;
char* msg = new char[256]{};
std::shared_ptr<int> deleter(new int(), [msg](int* p) {
delete p;
delete[] msg;
});
char source[] = "hello world";
memset(msg, 0, 256);
auto len = std::snprintf(msg + offset, 256 - offset, "%s", source);
if (!encrypt(ik, (uint8_t*)msg, len + offset)) {
return false;
}
uint8_t ik2[32]{};
hash(key.c_str(), ik2);
if (!decrypt(ik2, (uint8_t*)msg, len + offset)) {
return false;
}
return std::memcmp(source, msg + offset, len) == 0;
}
TEST_CASE("transm encry part", "[encry]")
{
SECTION("speed of encryption")
{
SpeedRet ret{};
auto r = test_speed(ret);
UNSCOPED_INFO("Encryption mode: " << ret.mode << "");
UNSCOPED_INFO("FileSize: " << ret.file_size << " MB");
UNSCOPED_INFO("Encryption speed: " << ret.encry_speed << " MB/s");
UNSCOPED_INFO("Decryption speed: " << ret.decry_speed << " MB/s");
REQUIRE(r == true);
}
}

77
test/assistant.cxx Normal file

@ -0,0 +1,77 @@
#include "assistant.h"
bool random_file(const std::string& file, size_t size)
{
if (file.empty() || size == 0) {
return false;
}
std::ofstream ofs(file, std::ios::binary | std::ios::trunc);
if (!ofs) {
return false;
}
std::random_device rd;
std::mt19937_64 gen(rd());
std::uniform_int_distribution<int> dis(0, 255);
const size_t buffer_size = 4096;
std::vector<unsigned char> buffer(buffer_size);
for (size_t remaining = size; remaining > 0;) {
size_t chunk = std::min(remaining, buffer_size);
for (size_t i = 0; i < chunk; ++i) {
buffer[i] = static_cast<unsigned char>(dis(gen));
}
if (!ofs.write(reinterpret_cast<const char*>(buffer.data()), chunk)) {
return false;
}
remaining -= chunk;
}
return true;
}
/**
* @brief
*
* @param filea
* @param fileb
* @return true
* @return false
*/
bool is_equal_filecontent(const std::string& filea, const std::string& fileb)
{
std::ifstream stream_a(filea, std::ios::binary);
std::ifstream stream_b(fileb, std::ios::binary);
if (!stream_a.is_open() || !stream_b.is_open()) {
return false;
}
auto size_a = fs::file_size(filea);
auto size_b = fs::file_size(fileb);
if (size_a != size_b) {
return false;
}
const size_t buffer_size = 4096; // 4KB 缓冲区
char buffer_a[buffer_size];
char buffer_b[buffer_size];
while (stream_a.good() && stream_b.good()) {
stream_a.read(buffer_a, buffer_size);
stream_b.read(buffer_b, buffer_size);
if (stream_a.gcount() != stream_b.gcount()) {
return false;
}
if (!std::equal(buffer_a, buffer_a + stream_a.gcount(), buffer_b)) {
return false;
}
}
return !stream_a.bad() && !stream_b.bad();
}

106
test/assistant.h Normal file

@ -0,0 +1,106 @@
#ifndef ASSISTANT_H
#define ASSISTANT_H
#include <filesystem>
#include <fstream>
#include <functional>
#include <random>
#include <string>
#include <thread>
namespace fs = std::filesystem;
/**
* @brief
*
* @param filea
* @param fileb
* @return true
* @return false
*/
bool is_equal_filecontent(const std::string& filea, const std::string& fileb);
bool random_file(const std::string& file, size_t size);
/**
* @brief
*
* @tparam T
* @tparam Compare
* @param value
* @param expected
* @param comparator ( std::equal_to)
* @param timeout_ms ()0
* @param interval_ms ()
* @return true
* @return false
*/
template <typename T, typename Compare = std::equal_to<T>>
bool value_wait(const std::function<T()>& value_ref, const T& expected,
Compare comparator = Compare(), unsigned long timeout_ms = 0,
unsigned long interval_ms = 100)
{
auto start = std::chrono::steady_clock::now();
while (true) {
T value = value_ref();
if (comparator(value, expected)) {
return true;
}
if (timeout_ms > 0) {
auto elapsed =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count();
if (elapsed >= timeout_ms) {
return false;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms));
}
}
class ScopeExit
{
public:
ScopeExit() = default;
template <typename F> ScopeExit(F&& f) : func_(std::forward<F>(f))
{
}
ScopeExit(const ScopeExit&) = delete;
ScopeExit& operator=(const ScopeExit&) = delete;
ScopeExit(ScopeExit&& other) noexcept : func_(std::move(other.func_))
{
other.func_ = nullptr;
}
ScopeExit& operator=(ScopeExit&& other) noexcept
{
if (this != &other) {
if (func_)
func_();
func_ = std::move(other.func_);
other.func_ = nullptr;
}
return *this;
}
~ScopeExit()
{
if (func_)
func_();
}
void dismiss() noexcept
{
func_ = nullptr;
}
private:
std::function<void()> func_;
};
#define _SCOPE_EXIT_CONCAT(a, b) a##b
#define _MAKE_ON_SCOPE_EXIT(line) \
ScopeExit _SCOPE_EXIT_CONCAT(exit_defer_, line) = [&]()
#define ON_SCOPE_EXIT _MAKE_ON_SCOPE_EXIT(__LINE__)
#endif // ASSISTANT_H

@ -1,27 +0,0 @@
#include <aes.hpp>
#include <string>
#include <util.h>
int main()
{
std::string key = "sss";
uint8_t ik[32]{};
hash(key.c_str(), ik);
int offset = 16;
char* msg = new char[256]{};
memset(msg, 0, 256);
auto len = std::snprintf(msg + offset, 256 - offset, "%s", "hello world");
std::cout << encrypt(ik, (uint8_t*)msg, len + offset) << std::endl;
std::cout << msg + offset << std::endl;
uint8_t ik2[32]{};
hash(key.c_str(), ik2);
std::cout << decrypt(ik2, (uint8_t*)msg, len + offset) << std::endl;
std::cout << msg + offset << std::endl;
return 0;
}

@ -1,10 +1,12 @@
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC
mode. Block size can be chosen in aes.h - available choices are AES128, AES192,
AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
National Institute of Standards and Technology Special Publication 800-38A
2001 ED
ECB-AES128
----------
@ -42,7 +44,8 @@ NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
// The number of columns comprising a state in AES. This is a constant in AES.
// Value=4
#define Nb 4
#if defined(AES256) && (AES256 == 1)
@ -58,7 +61,8 @@ NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
// See this link for more information:
// https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
@ -69,59 +73,77 @@ NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
// The lookup-tables are marked const so they can be placed in read-only storage
// instead of RAM The numbers below can be computed dynamically trading ROM for
// RAM - This can be useful in (embedded) bootloader applications, where ROM is
// often limited.
static const uint8_t sbox[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};
// 0 1 2 3 4 5 6 7 8 9 A B C
// D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
0xb0, 0x54, 0xbb, 0x16};
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
0x55, 0x21, 0x0c, 0x7d};
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field
// GF(2^8)
static const uint8_t Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
0x20, 0x40, 0x80, 0x1b, 0x36};
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
* Jordan Goulder points out in PR #12
* (https://github.com/kokke/tiny-AES-C/pull/12), that you can remove most of
* the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @
* https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys
* are needed), up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
* "Only the first some of these constants are actually used – up to rcon[10]
* for AES-128 (as 11 round keys are needed), up to rcon[8] for AES-192, up to
* rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
@ -142,7 +164,8 @@ static uint8_t getSBoxInvert(uint8_t num)
*/
#define getSBoxInvert(num) (rsbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
// This function produces Nb(Nr+1) round keys. The round keys are used in each
// round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
@ -180,7 +203,8 @@ static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// applies the S-box to each of the four bytes to produce an output
// word.
// Function Subword()
{
@ -312,27 +336,33 @@ static void MixColumns(state_t* state)
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
// Note: The last call to xtime() is unneeded, but often ends up generating a
// smaller binary
// The compiler seems to be able to vectorize the operation better this
// way. See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ ((y >> 2 & 1) * xtime(xtime(x))) ^
return (((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^
((y >> 2 & 1) * xtime(xtime(x))) ^
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
((y >> 4 & 1) *
xtime(xtime(xtime(
xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
(((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ ((y >> 2 & 1) * xtime(xtime(x))) ^ \
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))))
#define Multiply(x, y) \
(((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ \
((y >> 2 & 1) * xtime(xtime(x))) ^ \
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))))
#endif
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
// The method used to multiply may be difficult to understand for the
// inexperienced. Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
@ -343,10 +373,14 @@ static void InvMixColumns(state_t* state)
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^
Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^
Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^
Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^
Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
@ -449,13 +483,15 @@ static void InvCipher(state_t* state, uint8_t* RoundKey)
void AES_ECB_encrypt(struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
// The next function call encrypts the PlainText with the Key using AES
// algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
// The next function call decrypts the PlainText with the Key using AES
// algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
@ -466,7 +502,8 @@ void AES_ECB_decrypt(struct AES_ctx* ctx, uint8_t* buf)
static void XorWithIv(uint8_t* buf, uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
for (i = 0; i < AES_BLOCKLEN;
++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
@ -504,8 +541,8 @@ void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
#if defined(CTR) && (CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be
* reused with the same key */
/* Symmetrical operation: same function for encrypting as for decrypting. Note
* any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];

@ -7,10 +7,11 @@
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled
// simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
// #ifndef CBC
// The #ifndef-guard allows it to be configured before #include'ing or at
// compile time. #ifndef CBC
// #define CBC 1
// #endif
@ -53,7 +54,8 @@ struct AES_ctx {
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key,
const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
@ -68,8 +70,9 @@ void AES_ECB_decrypt(struct AES_ctx* ctx, uint8_t* buf);
#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for
// padding scheme NOTES: you need to set IV in ctx via AES_init_ctx_iv() or
// AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
@ -79,8 +82,9 @@ void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#if defined(CTR) && (CTR == 1)
// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// IV is incremented for every block, and used after encryption as
// XOR-compliment for output Suggesting
// https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);

@ -9,6 +9,7 @@
CTransProtocal::CTransProtocal() = default;
constexpr uint8_t kz = 16;
static bool use_encrypt = true;
CTransProtocal::~CTransProtocal() = default;
/*
@ -47,7 +48,8 @@ CFrameBuffer* CTransProtocal::parse(CMutBuffer& buffer)
int32_t len{};
std::memcpy(&type, buffer.get_data() + find + 2, sizeof(type));
std::memcpy(&mark, buffer.get_data() + find + 2 + 2, sizeof(mark));
std::memcpy(&len, buffer.get_data() + find + 2 + 2 + 1 + 32 + 32, sizeof(len));
std::memcpy(&len, buffer.get_data() + find + 2 + 2 + 1 + 32 + 32,
sizeof(len));
int32_t tail_index = find + 2 + 2 + 1 + 32 + 32 + 4 + len;
if (buffer.get_len() - 2 < tail_index || len < 0) {
@ -66,7 +68,8 @@ CFrameBuffer* CTransProtocal::parse(CMutBuffer& buffer)
result->mark_ = mark;
result->type_ = static_cast<FrameType>(type);
if (len > 0) {
std::memcpy(result->data_, buffer.get_data() + find + 2 + 2 + 1 + 4 + 32 + 32, len);
std::memcpy(result->data_,
buffer.get_data() + find + 2 + 2 + 1 + 4 + 32 + 32, len);
}
buffer.remove_of(0, tail_index + 2);
return result;
@ -92,7 +95,8 @@ bool CTransProtocal::pack(CFrameBuffer* buf, char** out_buf, int& len)
std::memcpy(*out_buf + 2 + 2 + 1, buf->fid_.data(), buf->fid_.size());
}
if (!buf->tid_.empty()) {
std::memcpy(*out_buf + 2 + 2 + 1 + 32, buf->tid_.data(), buf->tid_.size());
std::memcpy(*out_buf + 2 + 2 + 1 + 32, buf->tid_.data(),
buf->tid_.size());
}
std::memcpy(*out_buf + 2 + 2 + 1 + 32 + 32, &buf->len_, 4);
if (buf->data_ != nullptr) {
@ -133,7 +137,7 @@ CFrameBuffer::~CFrameBuffer()
len_ = 0;
}
void serialize(CMessageInfo& msg_info, char** out_buf, int& len)
void serialize(CMessageInfo& msg_info, char** out_buf, int& len, bool reuse_mem)
{
auto& info = msg_info;
info.id = localtou8(info.id);
@ -141,10 +145,26 @@ void serialize(CMessageInfo& msg_info, char** out_buf, int& len)
info.str = localtou8(info.str);
// 计算总长度
len = sizeof(int) * 4 + info.id.size() + info.uuid.size() + info.str.size() + info.data.size() + kz;
*out_buf = new char[len]{}; // 分配内存(调用方负责释放)
len = sizeof(int) * 4 + info.id.size() + info.uuid.size() +
info.str.size() + info.data.size() + kz + 1;
char* ptr = *out_buf + kz;
// 《这里为了效率》,
// 认为如果 *out_buf 不为空,则直接使用,且长度符合要求
// 调用方负责确保内存够用性(len <= 可用最大空间长度)和内存可用性。
// 即,如果调用方及高频率调用 serialize, 且每次 len <=
// 已分配空间就复用内存,完了再释放。 低频率或者 len
// 不固定时,每次都释放内存,并置 nullptr。
if (*out_buf) {
if (!reuse_mem) {
delete[] *out_buf;
*out_buf = new char[len]; // 分配内存(调用方负责释放)
}
} else {
*out_buf = new char[len];
}
std::memset(*out_buf, 0x0, kz + 1);
char* ptr = *out_buf + kz + 1;
// 序列化 cmd
int id_size = static_cast<int>(info.id.size());
@ -173,29 +193,36 @@ void serialize(CMessageInfo& msg_info, char** out_buf, int& len)
ptr += sizeof(int);
memcpy(ptr, info.data.data(), o_size);
uint8_t ik[32]{};
hash(msg_info.id.c_str(), ik);
if (!encrypt(ik, (uint8_t*)(*out_buf), len)) {
char* mark = *out_buf;
if (!use_encrypt) {
mark[0] = 0x00;
return;
}
uint8_t ik[32]{};
hash(msg_info.id.c_str(), ik);
encrypt(ik, (uint8_t*)(*out_buf + 1), len - 1);
mark[0] = 0x01;
}
bool deserialize(char* data, int len, CMessageInfo& msg_info)
{
if (len < kz) {
return false;
}
uint8_t ik[32]{};
hash(msg_info.id.c_str(), ik);
if (!decrypt(ik, (uint8_t*)(data), len)) {
if (len < (kz + 1)) {
return false;
}
auto& info = msg_info;
char* ptr = data + kz;
char* ptr = data + kz + 1;
uint8_t mark = data[0];
int remaining = len;
if (mark != 0x00) {
uint8_t ik[32]{};
hash(msg_info.id.c_str(), ik);
if (!decrypt(ik, (uint8_t*)(data + 1), len - 1)) {
return false;
}
}
// 反序列化 cmd
if (remaining < static_cast<int>(sizeof(int))) {
return false;
@ -297,9 +324,16 @@ void hash(const char* data, uint8_t k[32])
void rdm(uint8_t* o, size_t size)
{
/*
random_device
mt19937 + uniform_int_distribution
*/
std::random_device rd;
// std::mt19937 gen(rd());
std::mt19937_64 gen(rd());
std::uniform_int_distribution<int> dist(0, 255);
std::generate(o, o + size, [&]() { return static_cast<uint8_t>(dist(rd)); });
std::generate(o, o + size,
[&]() { return static_cast<uint8_t>(dist(gen)); });
}
bool encrypt(const uint8_t* k, uint8_t* m, size_t len)
@ -312,7 +346,7 @@ bool encrypt(const uint8_t* k, uint8_t* m, size_t len)
rdm(nonce, sizeof(nonce) - 4);
memcpy(m, nonce, kz);
struct AES_ctx ctx;
struct AES_ctx ctx{};
AES_init_ctx_iv(&ctx, k, nonce);
AES_CTR_xcrypt_buffer(&ctx, m + kz, len - kz);
return true;
@ -327,12 +361,22 @@ bool decrypt(const uint8_t* k, uint8_t* m, size_t len)
uint8_t nonce[kz]{};
memcpy(nonce, m, kz);
struct AES_ctx ctx;
struct AES_ctx ctx{};
AES_init_ctx_iv(&ctx, k, nonce);
AES_CTR_xcrypt_buffer(&ctx, m + kz, len - kz);
return true;
}
void set_encrypt(bool encrypt)
{
use_encrypt = encrypt;
}
bool get_encrypt_status()
{
return use_encrypt;
}
CMessageInfo::CMessageInfo(const std::string& id) : id(id)
{
}

@ -44,7 +44,7 @@ enum FrameType : int16_t {
// 此结构体成员顺序不可变动,涉及到序列化反序列化。
struct CMessageInfo {
CMessageInfo(const std::string& id);
explicit CMessageInfo(const std::string& id);
CMessageInfo(const CMessageInfo& info);
CMessageInfo& operator=(const CMessageInfo& info);
std::string id;
@ -53,7 +53,8 @@ struct CMessageInfo {
std::string data;
};
void serialize(CMessageInfo& msg_info, char** out_buf, int& len);
void serialize(CMessageInfo& msg_info, char** out_buf, int& len,
bool reuse_mem = false);
bool deserialize(char* data, int len, CMessageInfo& msg_info);
std::string u8tolocal(const std::string& str);
std::string localtou8(const std::string& str);
@ -61,6 +62,8 @@ void hash(const char* data, uint8_t k[32]);
void rdm(uint8_t* o, size_t size);
bool encrypt(const uint8_t* k, uint8_t* m, size_t len);
bool decrypt(const uint8_t* k, uint8_t* m, size_t len);
void set_encrypt(bool encrypt);
bool get_encrypt_status();
using namespace ofen;
class CFrameBuffer
@ -70,8 +73,8 @@ public:
~CFrameBuffer();
public:
std::string fid_{};
std::string tid_{};
std::string fid_;
std::string tid_;
public:
FrameType type_{};
@ -108,43 +111,54 @@ inline std::string now_str()
{
auto now = std::chrono::system_clock::now();
auto time_t_now = std::chrono::system_clock::to_time_t(now);
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) %
1000;
std::ostringstream timestamp;
timestamp << std::put_time(std::localtime(&time_t_now), "[%m-%d %H:%M:%S") << "." << std::setfill('0')
<< std::setw(3) << milliseconds.count() << "] ";
timestamp << std::put_time(std::localtime(&time_t_now), "[%m-%d %H:%M:%S")
<< "." << std::setfill('0') << std::setw(3)
<< milliseconds.count() << "] ";
return timestamp.str();
}
template <typename... Args> void TLOGI(const std::string& format, Args&&... args)
template <typename... Args>
void TLOGI(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Green << fmt::format(now_str() + format, std::forward<Args>(args)...)
std::cout << ConsoleColor::Green
<< fmt::format(now_str() + format, std::forward<Args>(args)...)
<< std::endl;
fc_unlock_print();
}
template <typename... Args> void TLOGW(const std::string& format, Args&&... args)
template <typename... Args>
void TLOGW(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Yellow << fmt::format(now_str() + format, std::forward<Args>(args)...)
std::cout << ConsoleColor::Yellow
<< fmt::format(now_str() + format, std::forward<Args>(args)...)
<< std::endl;
fc_unlock_print();
}
template <typename... Args> void TLOGE(const std::string& format, Args&&... args)
template <typename... Args>
void TLOGE(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Red << fmt::format(now_str() + format, std::forward<Args>(args)...)
std::cout << ConsoleColor::Red
<< fmt::format(now_str() + format, std::forward<Args>(args)...)
<< std::endl;
fc_unlock_print();
}
template <typename... Args> void TLOGD(const std::string& format, Args&&... args)
template <typename... Args>
void TLOGD(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Cyan << fmt::format(now_str() + format, std::forward<Args>(args)...)
std::cout << ConsoleColor::Cyan
<< fmt::format(now_str() + format, std::forward<Args>(args)...)
<< std::endl;
fc_unlock_print();
}