Compare commits

..

No commits in common. "main" and "v1.2.1" have entirely different histories.
main ... v1.2.1

56 changed files with 648 additions and 40472 deletions

View File

@ -11,7 +11,7 @@ ReflowComments: true
SpacesBeforeTrailingComments: 3
TabWidth: 4
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ColumnLimit: 130
ColumnLimit: 110
AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: None
AllowShortEnumsOnASingleLine: false

4
.gitignore vendored
View File

@ -1,9 +1,7 @@
buil*
build
.vs
.cache
cmake-*
.xmake
compile_commands.json
out
xpbuild
cbuild

3
.gitmodules vendored
View File

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

81
.vscode/settings.json vendored
View File

@ -1,8 +1,10 @@
{
"files.autoSave": "onFocusChange",
"editor.fontSize": 13,
"editor.fontFamily": "'Monaspace Krypton Light', 'Monaspace Krypton Light', 'Monaspace Krypton Light'",
"terminal.integrated.fontFamily": "Monaspace Krypton Light",
"editor.fontSize": 14,
"editor.fontFamily": "'Source Code Pro', 'Source Code Pro', monospace",
"terminal.integrated.fontFamily": "Source Code Pro",
"editor.fontLigatures": true,
//"C_Cpp.default.configurationProvider": "tboox.xmake-vscode",
"cmake.configureOnOpen": true,
"cmake.debugConfig": {
"console": "integratedTerminal",
@ -17,20 +19,18 @@
"ignoreFailures": true
}
],
"visualizerFile": "${workspaceRoot}/.vscode/qt5.natvis",
"args": [
"9999"
"-n", "1"
]
},
"cmake.configureArgs": [
"-Wno-dev",
"-DGRAB_CRASH=ON"
],
"cmake.configureSettings": {
//"CMAKE_TOOLCHAIN_FILE": "${env:VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
"cmake.environment": {
"PATH": "${env:PATH};"
},
"cmake.options.statusBarVisibility": "visible",
"cmake.generator": "Ninja",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.default.compileCommands": "${workspaceRoot}/build/compile_commands.json",
"C_Cpp.default.cppStandard": "c++17",
"editor.inlayHints.enabled": "off",
"editor.unicodeHighlight.allowedLocales": {
"ja": true,
@ -38,27 +38,22 @@
"zh-hans": true
},
"files.associations": {
".clang-tidy": "yaml",
"filesystem": "cpp",
"regex": "cpp",
"functional": "cpp",
"xstring": "cpp",
"vector": "cpp",
"string": "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",
@ -68,13 +63,13 @@
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"expected": "cpp",
"resumable": "cpp",
"format": "cpp",
"filesystem": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
@ -98,22 +93,21 @@
"ratio": "cpp",
"set": "cpp",
"shared_mutex": "cpp",
"source_location": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"string_view": "cpp",
"strstream": "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,9 +119,34 @@
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"xmemory0": "cpp",
"xstddef": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "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"
},
"makefile.configureOnOpen": false,
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}

View File

@ -1,225 +0,0 @@
// Based on https://gitee.com/whatitis/ColorfulConsoleIO/ modification
#include <iostream>
#include <string>
#if defined(_WIN32)
#include <windows.h>
#endif // OS_TYPE_WINDOWS_CC
// Enum class for console text color
enum class ConsoleColor {
Green,
Red,
Blue,
White,
Black,
Yellow,
Purple,
Gray,
Cyan,
None,
GreenIntensity,
RedIntensity,
BlueIntensity,
WhiteIntensity,
BlackIntensity,
YellowIntensity,
PurpleIntensity,
GrayIntensity,
CyanIntensity
};
// Enum class for console background color
enum class ConsoleBackgroundColor {
Green,
Red,
Blue,
White,
Black,
Yellow,
Purple,
Gray,
Cyan,
None
};
#if defined(_WIN32)
// Get the Windows color code for a given ConsoleColor
inline WORD GetColorCode(ConsoleColor color)
{
switch (color) {
case ConsoleColor::Green:
return FOREGROUND_GREEN;
case ConsoleColor::Black:
return 0;
case ConsoleColor::Blue:
return FOREGROUND_BLUE;
case ConsoleColor::Gray:
return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
case ConsoleColor::Purple:
return FOREGROUND_BLUE | FOREGROUND_RED;
case ConsoleColor::Red:
return FOREGROUND_RED;
case ConsoleColor::White:
return FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
case ConsoleColor::Cyan:
return FOREGROUND_BLUE | FOREGROUND_GREEN;
case ConsoleColor::Yellow:
return FOREGROUND_RED | FOREGROUND_GREEN;
case ConsoleColor::None:
return FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
case ConsoleColor::GreenIntensity:
return FOREGROUND_GREEN | FOREGROUND_INTENSITY;
case ConsoleColor::BlackIntensity:
return 0;
case ConsoleColor::BlueIntensity:
return FOREGROUND_BLUE | FOREGROUND_INTENSITY;
case ConsoleColor::GrayIntensity:
return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
case ConsoleColor::PurpleIntensity:
return FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
case ConsoleColor::RedIntensity:
return FOREGROUND_RED | FOREGROUND_INTENSITY;
case ConsoleColor::WhiteIntensity:
return FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
case ConsoleColor::YellowIntensity:
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
case ConsoleColor::CyanIntensity:
return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
default:
return 0;
}
}
#else
// Get the ANSI escape code for a given ConsoleColor
inline std::string GetColorCode(ConsoleColor color)
{
switch (color) {
case ConsoleColor::Green:
return "\033[32m";
case ConsoleColor::Black:
return "\033[30m";
case ConsoleColor::Blue:
return "\033[34m";
case ConsoleColor::Gray:
return "\033[37m";
case ConsoleColor::Purple:
return "\033[35m";
case ConsoleColor::Red:
return "\033[31m";
case ConsoleColor::White:
return "\033[37m";
case ConsoleColor::Cyan:
return "\033[36m";
case ConsoleColor::Yellow:
return "\033[33m";
case ConsoleColor::None:
return "\033[0m";
case ConsoleColor::GreenIntensity:
return "\033[32m;1m";
case ConsoleColor::BlackIntensity:
return "\033[30m;1m";
case ConsoleColor::BlueIntensity:
return "\033[34m;1m";
case ConsoleColor::GrayIntensity:
return "\033[37m;1m";
case ConsoleColor::PurpleIntensity:
return "\033[35m;1m";
case ConsoleColor::RedIntensity:
return "\033[31m;1m";
case ConsoleColor::WhiteIntensity:
return "\033[37m;1m";
case ConsoleColor::YellowIntensity:
return "\033[33m;1m";
case ConsoleColor::CyanIntensity:
return "\033[36m;1m";
default:
return 0;
}
}
#endif
#if defined(_WIN32)
// Get the Windows color code for a given ConsoleBackgroundColor
inline WORD GetBackgroundColorCode(ConsoleBackgroundColor color)
{
switch (color) {
case ConsoleBackgroundColor::Green:
return BACKGROUND_GREEN;
case ConsoleBackgroundColor::Black:
return 0;
case ConsoleBackgroundColor::Blue:
return BACKGROUND_BLUE;
case ConsoleBackgroundColor::Gray:
return 0;
case ConsoleBackgroundColor::Purple:
return BACKGROUND_RED | BACKGROUND_BLUE;
case ConsoleBackgroundColor::Red:
return BACKGROUND_RED;
case ConsoleBackgroundColor::White:
return BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN;
case ConsoleBackgroundColor::Cyan:
return BACKGROUND_BLUE | BACKGROUND_GREEN;
case ConsoleBackgroundColor::Yellow:
return BACKGROUND_RED | BACKGROUND_GREEN;
case ConsoleBackgroundColor::None:
return 0;
default:
return 0;
}
}
#else
// Get the ANSI escape code for a given ConsoleBackgroundColor
inline std::string GetBackgroundColorCode(ConsoleBackgroundColor color)
{
switch (color) {
case ConsoleBackgroundColor::Green:
return "\033[42m";
case ConsoleBackgroundColor::Black:
return "\033[40m";
case ConsoleBackgroundColor::Blue:
return "\033[44m";
case ConsoleBackgroundColor::Gray:
return "\033[40m";
case ConsoleBackgroundColor::Purple:
return "\033[45m";
case ConsoleBackgroundColor::Red:
return "\033[41m";
case ConsoleBackgroundColor::White:
return "\033[47m";
case ConsoleBackgroundColor::Cyan:
return "\033[46m";
case ConsoleBackgroundColor::Yellow:
return "\033[43m";
case ConsoleBackgroundColor::None:
return "\033[40m";
default:
return 0;
}
}
#endif
// Operator overloading for console text color
inline std::ostream& operator<<(std::ostream& os, ConsoleColor data)
{
#if defined(_WIN32)
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, GetColorCode(data));
#else
std::cout << GetColorCode(data);
#endif
return os;
}
// Operator overloading for console background color
inline std::ostream& operator<<(std::ostream& os, ConsoleBackgroundColor data)
{
#if defined(_WIN32)
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, GetBackgroundColorCode(data));
#else
std::cout << GetBackgroundColorCode(data);
#endif
return os;
}

View File

@ -70,7 +70,7 @@ public:
static std::size_t hardware_concurrency()
{
SYSTEM_INFO system_info;
::GetNativeSystelog_->info(&system_info);
::GetNativeSystemInfo(&system_info);
return system_info.dwNumberOfProcessors;
}

View File

@ -70,7 +70,7 @@ public:
static std::size_t hardware_concurrency()
{
SYSTEM_INFO system_info;
::GetSystelog_->info(&system_info);
::GetSystemInfo(&system_info);
return system_info.dwNumberOfProcessors;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,58 +1,35 @@
cmake_minimum_required(VERSION 3.16)
project(transm VERSION 1.5.2 LANGUAGES CXX)
project(transm LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(config/MSystem.cmake)
set(PROJECT_URL "https://www.sinxmiao.cn/taynpg/transm")
# CMAKE_SYSTEM_PROCESSOR
# string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR_LOWER)
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")
message(STATUS "add mingw param.")
message(status "add mingw param.")
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)
endif()
string(TOLOWER ${COMPILER_ID} COMPILER_ID)
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)
else()
add_definitions(-D_WIN32_WINNT=0x0601)
endif()
set(CMAKE_DEBUG_POSTFIX "d")
message(STATUS "System: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Compiler CXX ID: ${CMAKE_CXX_COMPILER_ID}")
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE})
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)
add_definitions(-D_WIN32_WINNT=0x0601)
include_directories(3rd)
include_directories(.)
include_directories(${PROJECT_BINARY_DIR})
@ -62,20 +39,6 @@ add_subdirectory(util)
add_subdirectory(server)
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()
string(TIMESTAMP VERSION_BUILD_DATE "%Y-%m-%d %H:%M")
execute_process(
@ -91,58 +54,9 @@ execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(version.h.in version.h)
message(STATUS "${CMAKE_SYSTEM_NAME} build dir:${PROJECT_BINARY_DIR}")
message(STATUS "VERSION_GIT_BRANCH: ${VERSION_GIT_BRANCH}")
message(STATUS "VERSION_GIT_HASH: ${VERSION_GIT_HASH}")
message(STATUS "build dir:${PROJECT_BINARY_DIR}")
install(TARGETS tsc DESTINATION bin)
install(TARGETS tss 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_BOOST)
install(FILES ${MINGW32_DLLS} DESTINATION bin)
endif()
if (DEFINED USE_GUI)
message(STATUS "USE GUI ${USE_GUI}")
#add_subdirectory(gui)
endif()
# ********************************************************** pack infomation
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ARCH_BIT "64Bit")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(ARCH_BIT "32Bit")
else()
message(FATAL_ERROR "Unknown target architecture")
endif()
set(CPACK_PACKAGE_NAME "transm")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_CONTACT "taynpg <taynpg@163.com>")
set(CPACK_PACKAGE_DESCRIPTION "A simple tool that uses a server (tss) as a relay center to indirectly transfer files between two (or more) clients (tsc).")
set(CPACK_PACKAGE_VENDOR "taynpg")
if(WIN32)
set(CPACK_NSIS_MODIFY_PATH ON)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(CPACK_GENERATOR "NSIS64;ZIP")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CPACK_GENERATOR "NSIS;ZIP")
else()
message(FATAL_ERROR "Unknown target architecture")
endif()
set(CPACK_NSIS_INSTALL_ROOT "$PROFILE")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "transm")
elseif(UNIX AND NOT APPLE)
set(CPACK_GENERATOR "DEB;TGZ")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "taynpg <taynpg@163.com>")
elseif(APPLE)
set(CPACK_GENERATOR "DragNDrop")
endif()
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-v${PROJECT_VERSION}-${COMPILER_ID}.${SYSTEM_ARCH}.${ARCH_BIT}")
include(CPack)
# add_executable(transm_test1 test1.cpp)
# target_link_libraries(transm_test1 PRIVATE trans_net trans_util)
# add_executable(transm_test2 test2.cpp)
# target_link_libraries(transm_test2 PRIVATE trans_net trans_util)

117
README.md
View File

@ -1,127 +1,82 @@
# transm
一个简易的使用服务端(`tss`)作为中转中心,间接在两个(多)客户端(`tsc`)之间传输文件的工具。
一个简易的使用服务端(`tss`)作为中转中心,间接在两个客户端(`tsc`)之间传输文件的工具。
平台支持:`Windows``Linux``MacOS``Termux``iPhone iSh`及理论上其他任意支持`c++17`标准编译器的平台。
# 一、简要介绍
低版本编译器,可选择`boost`+`c++11`标准组合。
**注**:使用安装包的用户,如果想变更安装位置的话,请先卸载旧版本(原位置更新最新版不用卸载)。
# 一、说明
**注意:** 当前说明文档仅针对`v1.4.1`及其以后的版本,此前的版本需要检出对应`tag``README.md`或者下载对应发布版本的源码,然后解压其中的`README.md`
服务端程序支持任意个客户端相互之间**同时连接**和**同时传输**文件,吞吐瓶颈在服务端主机网络上。
| 主要功能序号 | 简介 |
| ------------ | ------------------------------------------------------------ |
| 1 | A端提交文件列表到服务端,B端可以从服务端查阅有哪些客户端提交的哪些任务,自行选择下载。 |
| 2 | A端可以提交一个下载任务文件给B端,B端会自动下载列表中的文件(可用作更新远端文件)。 |
- `tss``tsc`均为命令行端程序,无GUI。
## 一些特点(基于最新版本)
- `tsc``tss`下载文件的时候,如果本地有已存在则会被**覆盖**(注意)。
- 易编译。
- 通配符传输。
- 公网传输支持加密。
- 广泛的平台支持。
- 终端自动文件补全。
- 自动检测对方掉线。
- 极小的、单个文件。
- 支持相对路径处理。
- 单端可以操作下载发送。
- 支持单客户端操作收发本地文件。
- 多客户端可以同时互相收发文件。
- 服务端仅转发数据,不存储数据。
- 运行时无其他三方二进制库依赖。
- `Linux``Linux`文件传输保留原权限。
- 干净,不会在客户机环境到处遗留临时文件。
- 临时公网传输,无需使用三方需要登陆软件。
- 服务端自我安全防御(若部署到云端防止常规攻击)。
- 支持拒绝同时操作同设备同路径文件,防止文件内容丢失。
- 介绍所指的客户端`A``B`是泛指,实际服务端程序支持任意个客户端相互之间**同时连接**和**同时传输**文件,吞吐瓶颈在服务端主机网络上。
# 二、使用说明
## 1.程序启动
- 对于服务端程序`tss`,绑定默认绑定`0.0.0.0``9898`端口,如果需要修改端口,使用参数启动,示例:`tss 9898`
- 对于客户端程序`tsc`,请使用`tsc --help`查看如何启动。
- 对于客户端程序`tsc`,请使用`tsc --help`查看使用方式。
- `Up`指令后面的文件名路径,如果是非全路径(即相对路径),程序会自动拼接到当前`tsc`工作目录(如`Up dira/test.txt`也是可以的)。
## 2.使用
## 2.命令使用(截图可能过时,但使用方式大致如此)
`tsc`连接到服务端`tss`后,可以输入`h`查看所有指令的具体介绍。
![Screenshot](https://www.sinxmiao.cn/taynpg/transm/raw/branch/main/img/base_intro.png)
![tsc](https://www.sinxmiao.cn/taynpg/transm/raw/branch/main/img/tsc_use.png)
### Update功能
## 3.介绍补充
命令格式为:`Update 客户端标号 列表文件`
- 需要传入存储路径的指令,如果未填写,默认`tsc`当前工作目录。
- `任务文件`格式统一为`UTF-8`编码。
- 支持相对路径处理。
- `UpTask``DownTask`指令`任务文件`格式完全一致,均为左文件,右目录,尽管是下载别人文件,目的是为了支持同一个任务文件既可以发也可以收。
`Update`的提交的列表文件格式为`txt`,内容为每一行格式是`A|B`,其中`A`为提交端的文件路径,`B`为要放到下载端的哪个目录 **(下载端必须存在这个目录,否则下载端拒绝自动下载)**。
具体`任务文件`内容格式如下(左侧发送端,右侧接受端),内容支持变量:
示例执行:`Update 1 task.txt`,其中`task.txt`内容示例如下:
```txt
D:/文件/abc.zip|/home/zhangsan/downlaod
D:/截图/Ni.jpg|/home/zhangsan/picture
```
#### update新增(一)
`v1.2.1`版本起(含),`task.txt`支持以下变量:
- ${HOME},用户目录(发送端接收端均支持)。
- ${CURRENT},任务文件所在目录(该变量仅支持发送端,也就是`|`左侧,因为接收端没有任务文件所在路径)
- ${CURRENT},任务文件所在目录(`task.txt`所在目录,该变量仅支持发送端,也就是`|`左侧,因为接收端没有任务文件所在路径)
```txt
${HOME}/截图/Ni.jpg|${HOME}/dira
${CURRENT}/xxx.zip|D:\
```
### 3.1 版本内容补充
**NOTE**: `列表文件`的格式为`UTF-8`编码。
- `v1.5.0`及其以后版本:支持数据加密传输功能(会影响速度,默认开启,可关闭)。
# 注意
```c++
/*
测试环境 ==>
Microsoft Windows 10 Professional (x64) Build 19045.5608 (22H2)
13th Gen Intel(R) Core(TM) i5-13500H 3200.0 MHz
- 如果两个`tsc`客户端在同一台机器上同时收发同一个文件将导致文件丢失损坏。
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`构建工具。
```shell
git clone --recursive https://www.sinxmiao.cn/taynpg/transm
```
当前项目支持`xmake``cmake`构建工具。
## 1.常规编译
xmake:`xmake`
cmake:`cmake -Bbuild -DCMAKE_BUILD_TYPE=Release``cmake --build build --config Release`
## 2.XP系统编译
### 前提
- `cmake`支持windows XP的版本过低,故使用`xmake`
- 支持`XP``msvc`编译器不支持新`C++语法标准`,故使用`mingw32`编译器。
- 支持`xp``mingw32`编译器的`c++17`标准的`filesystem`模块还是实验性质且有编译`BUG`,故使用`boost-filesystem`
- 需要一个最低`3.16`版本的`cmake`
### 编译
请参照:`xp_build.bat`
- 构建选项:`xmake f -p mingw -a i386 --boost=y`
- 环境变量定义:`BOOST_HEADER_DIR``BOOST_LIB_DIR``BOOST_LIBS`

View File

@ -1,12 +1,2 @@
# 默认构建命令
build_command="cmake -Bbuild -DCMAKE_BUILD_TYPE=Release"
build_command_release="cmake --build build --config Release"
# 检查是否传入 -ish 参数
if [[ "$1" == "-ish" ]]; then
build_command="$build_command -DIOS_ISH=True"
fi
# 执行命令
$build_command
$build_command_release
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

View File

@ -4,12 +4,7 @@ project(tsc LANGUAGES CXX)
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})
add_compile_options(/source-charset:utf-8)
endif()
add_executable(tsc main.cpp client.h client.cpp config.h config.cpp)
@ -20,22 +15,3 @@ 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})
endif()
if (DEFINED GRAB_CRASH)
message(STATUS "tsc link crashelper")
target_link_libraries(tsc PRIVATE crashelper)
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 "tsc on musl static link")
target_link_libraries(tsc PRIVATE -static;-static-libstdc++)
endif()
endif()

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
#pragma once
#include <cstdint>
#include <filecomplete.h>
#include <fstream>
#include <map>
@ -8,14 +7,12 @@
#include <net_base.h>
#include <of_util.h>
#include <string>
#include <unordered_map>
#include <util.h>
#include <vector>
using namespace ofen;
struct DownClientInfo {
std::vector<std::string> files;
std::string uuid;
std::string id;
};
@ -32,54 +29,39 @@ struct TransInfomation {
std::string cur_remote_file_;
std::string cur_file_;
std::fstream file_{};
uint16_t permissions{};
uint16_t remote_plat{};
TransState trans_state_{TRANS_FAILED};
};
constexpr int down_check_wait = 100; // millsec
class TransmClient
class CClient
{
public:
TransmClient();
~TransmClient();
CClient();
~CClient();
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 cmd_sub_task(const std::string& param, bool is_send);
bool cmd_ls(const std::string& param);
bool cmd_down_list(const std::string& param);
void run(const std::string& ip, const std::string& port);
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);
public:
bool get_task_list();
bool down_task(const std::string& param);
bool up_task(const std::string& param);
bool cancel_task();
bool down_one_file(const std::string& id, const std::string& file, const std::string& local_dir = "");
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 = "");
bool request_update_list(const std::string& param);
bool check_update_list(const std::string& content, std::map<std::string, std::string>& files);
bool down_update_file(const std::map<std::string, std::string>& files);
private:
bool send_frame(CFrameBuffer* buf);
void save_line_his(const std::string& input);
std::vector<std::string> load_line_his();
std::string variable_and_reverse_files(const std::string& source);
bool save_uuid();
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_send);
private:
std::mutex mutex_;
@ -91,47 +73,20 @@ private:
bool will_receive_{false};
bool downloading_{false};
asio::io_context io_context_;
CMessageInfo msg_info_;
std::shared_ptr<CTcpClient> client_;
std::map<int, std::shared_ptr<DownClientInfo>> clients_;
std::map<int, std::shared_ptr<DownClientInfo>> task_list_;
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_{};
private:
std::string list_file_;
std::string list_server_id_;
std::string list_server_uuid_;
std::string list_serve_id_;
std::thread update_list_th_;
std::string own_id_{};
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
@ -141,5 +96,5 @@ public:
~CFileOpr();
public:
static bool get_file_list(const std::string& input, std::vector<std::string>& out);
static std::vector<std::string> get_file_list(const std::string& input);
};

View File

@ -1,8 +1,7 @@
#include "config.h"
#include <cassert>
#ifdef USE_BOOST
#ifdef USE_BOOST_FILESYSTEM
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
@ -10,35 +9,32 @@ namespace fs = boost::filesystem;
namespace fs = std::filesystem;
#endif
ClientConfig::ClientConfig()
{
}
CServerConfig::CServerConfig() = default;
ClientConfig::~ClientConfig() = default;
CServerConfig::~CServerConfig() = default;
bool ClientConfig::baseInit()
bool CServerConfig::baseInit()
{
fs::path tpath(COfPath::get_config_dir("transm", true));
config_dir_ = tpath.string();
config_path_ = tpath.append("transm.ini").string();
if (!fs::exists(config_path_)) {
gen_default_ini(config_path_);
}
SI_Error ret = ini_handle_.LoadFile(config_path_.c_str());
if (ret != SI_OK) {
TLOGE("Load Ini [{}] Failed.", config_path_);
mperror("Load Ini [{}] Failed.", config_path_);
return false;
}
init_ = true;
return true;
}
bool ClientConfig::read_ini(std::vector<TransmSet>& set)
bool CServerConfig::read_ini(std::vector<TransmSet>& set)
{
assert(init_ == true);
long groups = ini_handle_.GetLongValue("BASE", "GROUPS");
if (groups < 1) {
TLOGE("GROUPS num < 1.");
mperror("GROUPS num < 1.");
return false;
}
set.clear();
@ -49,67 +45,36 @@ bool ClientConfig::read_ini(std::vector<TransmSet>& set)
ts.grp_id = i;
ts.ip = ini_handle_.GetValue(key.c_str(), "IP");
ts.port = ini_handle_.GetLongValue(key.c_str(), "PORT");
if (!ini_handle_.KeyExists(key.c_str(), "COMMENT")) {
ts.comment = "default";
} else {
ts.comment = ini_handle_.GetValue(key.c_str(), "COMMENT");
}
set.push_back(ts);
}
return true;
}
long ClientConfig::have_ini(const std::vector<TransmSet>& set, const std::string& ip, long port)
{
long id = -1;
for (const auto& item : set) {
if (item.ip == ip && item.port == port) {
id = item.grp_id;
break;
}
}
return id;
}
bool ClientConfig::write_ini(const std::vector<TransmSet>& set)
bool CServerConfig::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_.SaveFile(config_path_.c_str());
return true;
}
long ClientConfig::append_ini(const std::string& ip, long port, const std::string& comment)
bool CServerConfig::append_ini(const std::string& ip, long port)
{
assert(init_ == true);
long id = -1;
std::vector<TransmSet> set;
if (!read_ini(set)) {
return false;
}
id = have_ini(set, ip, port);
if (id >= 0) {
std::string node_name = "GROUP" + std::to_string(id);
ini_handle_.SetValue(node_name.c_str(), "COMMENT", comment.c_str());
ini_handle_.SaveFile(config_path_.c_str());
return true;
}
id = ini_handle_.GetLongValue("BASE", "GROUPS");
std::string node_name = "GROUP" + std::to_string(id);
int group = ini_handle_.GetLongValue("BASE", "GROUPS");
std::string node_name = "GROUP" + std::to_string(group);
ini_handle_.SetValue(node_name.c_str(), "IP", ip.c_str());
ini_handle_.SetLongValue(node_name.c_str(), "PORT", port);
ini_handle_.SetValue(node_name.c_str(), "COMMENT", comment.c_str());
ini_handle_.SetLongValue("BASE", "GROUPS", id + 1);
ini_handle_.SetLongValue("BASE", "GROUPS", group + 1);
ini_handle_.SaveFile(config_path_.c_str());
return id;
return true;
}
bool ClientConfig::remove_ini(long num)
bool CServerConfig::remove_ini(long num)
{
assert(init_ == true);
std::vector<TransmSet> set;
@ -124,7 +89,7 @@ bool ClientConfig::remove_ini(long num)
return write_ini(set);
}
bool ClientConfig::get_ini(const std::vector<TransmSet>& set, long num, TransmSet& use)
bool CServerConfig::get_ini(const std::vector<TransmSet>& set, long num, TransmSet& use)
{
bool find = false;
for (const auto& item : set) {
@ -137,35 +102,9 @@ bool ClientConfig::get_ini(const std::vector<TransmSet>& set, long num, TransmSe
return find;
}
std::string ClientConfig::get_config_dir() const
void CServerConfig::gen_default_ini(const std::string& path)
{
return config_dir_;
}
bool ClientConfig::save_last_use(const std::string& ip, long port)
{
assert(init_ == true);
ini_handle_.SetValue("Base", "LastUseIP", ip.c_str());
ini_handle_.SetLongValue("Base", "LastUsePort", port);
ini_handle_.SaveFile(config_path_.c_str());
return true;
}
bool ClientConfig::get_last_use(std::string& ip, long& port)
{
assert(init_ == true);
if (!ini_handle_.KeyExists("Base", "LastUseIP") || !ini_handle_.KeyExists("Base", "LastUsePort")) {
TLOGE("Not Found Last Use Record.");
return false;
}
ip = ini_handle_.GetValue("Base", "LastUseIP");
port = ini_handle_.GetLongValue("Base", "LastUsePort");
return true;
}
void ClientConfig::gen_default_ini(const std::string& path)
{
TLOGW("Gen Default Setting Ini in [{}].", path);
mpwarn("Gen Default Setting Ini in [{}].", path);
ini_handle_.LoadFile(path.c_str());
ini_handle_.SetLongValue("BASE", "GROUPS", 1);
ini_handle_.SetValue("GROUP0", "IP", "127.0.0.1");

View File

@ -8,7 +8,6 @@ using namespace ofen;
struct TransmSet {
std::string group;
std::string ip{};
std::string comment{};
long port{};
long grp_id{};
};
@ -16,34 +15,24 @@ struct TransmSet {
struct CmdParam {
std::string removeValue;
std::string appendValue;
std::string connectValue;
bool showValue{false};
long use_config{-1};
bool parsed{false};
bool direct_use{false};
bool last_use{false};
bool null_use{false};
};
class ClientConfig
class CServerConfig
{
public:
ClientConfig();
~ClientConfig();
CServerConfig();
~CServerConfig();
public:
bool baseInit();
bool read_ini(std::vector<TransmSet>& set);
long have_ini(const std::vector<TransmSet>& set, const std::string& ip, long port);
bool write_ini(const std::vector<TransmSet>& set);
long append_ini(const std::string& ip, long port, const std::string& comment);
bool append_ini(const std::string& ip, long port);
bool remove_ini(long num);
static bool get_ini(const std::vector<TransmSet>& set, long num, TransmSet& use);
std::string get_config_dir() const;
public:
bool save_last_use(const std::string& ip, long port);
bool get_last_use(std::string& ip, long& port);
private:
void gen_default_ini(const std::string& path);
@ -52,5 +41,4 @@ private:
bool init_{false};
CSimpleIniA ini_handle_{};
std::string config_path_{};
std::string config_dir_{};
};

View File

@ -1,11 +1,9 @@
#include <CLI11.hpp>
#include <iostream>
#include <of_util.h>
#include <regex>
#include "client.h"
#include "config.h"
#include "version.h"
#include <CLI11.hpp>
#include <iostream>
#include <regex>
#ifdef _WIN32
#include <fcntl.h>
@ -16,31 +14,17 @@
#endif
#endif
#if defined(GRAB_CRASH)
#ifdef USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
#include <crashelper.h>
#endif
std::shared_ptr<CServerConfig> g_Config = nullptr;
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));
std::string intro("transmc cmd introduce.");
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("-n, --number", 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_flag("-n, --null", param.null_use, "先运行在选择服务器。");
app.add_option("-c, --connect", param.connectValue, "直连服务器((地址格式:127.0.0.1:9898)。");
if (argc == 1) {
std::cout << app.help() << std::endl;
@ -57,59 +41,7 @@ int parse_cmd(int argc, char** argv, CmdParam& param)
return 0;
}
bool select_server(const std::vector<TransmSet>& sets, std::string& ip, long& port)
{
TLOGI("Please Select a Server:");
if (sets.empty()) {
TLOGE("No servers available to select.");
return false;
}
// 打印所有服务器选项(带编号)
for (size_t i = 0; i < sets.size(); ++i) {
const auto& server = sets[i];
if (server.comment.empty()) {
TLOGI("[{}] {}:{}", i + 1, server.ip, server.port);
} else {
TLOGI("[{}] {}:{} ({})", i + 1, server.ip, server.port, server.comment);
}
}
while (true) {
TLOGW("Enter server number (or 'exit' to cancel): ");
std::string input;
std::getline(std::cin, input);
// 检查是否输入了退出命令
if (input == "exit" || input == "quit" || input == "q") {
TLOGD("User canceled server selection.");
return false;
}
// 检查输入是否为空或非数字
if (input.empty() || !std::all_of(input.begin(), input.end(), ::isdigit)) {
TLOGE("Invalid input '{}'. Please enter a valid number or 'exit'.", input);
continue;
}
// 转换为数字
int choice = std::stoi(input);
// 检查数字是否在有效范围内
if (choice < 1 || choice > static_cast<int>(sets.size())) {
TLOGE("Invalid choice '{}'. Please select a number between 1 and {}.", choice, sets.size());
continue;
}
// 选择成功,返回对应的 IP 和端口
const auto& selected = sets[choice - 1];
ip = selected.ip;
port = selected.port;
TLOGI("Selected server: {}:{} ({})", ip, port, selected.comment);
return true;
}
}
bool exec_cmd(CmdParam& param, bool& run)
bool exec_cmd(const CmdParam& param, bool& run)
{
run = false;
// 如果是展示
@ -119,7 +51,7 @@ bool exec_cmd(CmdParam& param, bool& run)
return false;
}
for (const auto& item : set) {
TLOGI("{} => {}:{} {}", item.group, item.ip, item.port, item.comment);
mpinfo("{} => {}:{}", item.group, item.ip, item.port);
}
return true;
}
@ -128,51 +60,39 @@ bool exec_cmd(CmdParam& param, bool& run)
return true;
}
if (!param.appendValue.empty() && !param.removeValue.empty()) {
TLOGW("append and remove can't simultaneous operate!");
mperror("append and remove can't simultaneous operate!");
return false;
}
if (!param.appendValue.empty()) {
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)) {
ip = matches[1].str();
port = matches[2].str();
comment = matches.size() > 3 ? matches[3].str() : "default";
}
auto id = g_Config->append_ini(ip, std::stol(port), comment);
if (id < 0) {
TLOGW("add {}:{} failed.", ip, port);
std::regex rg(R"(([^:]+):(\d+))");
std::smatch match;
if (!std::regex_search(param.appendValue, match, rg)) {
mperror("append invalid format!");
return false;
}
TLOGI("add {}:{} success.", ip, port);
param.use_config = id;
std::string ip = match[1].str();
std::string port = match[2].str();
if (!g_Config->append_ini(ip, std::stol(port))) {
mperror("add {}:{} failed.", ip, port);
return false;
}
mpinfo("add {}:{} success.", ip, port);
return true;
}
if (!param.removeValue.empty()) {
if (!g_Config->remove_ini(std::stol(param.removeValue))) {
TLOGW("remove config num=[{}] failed, please check!", param.removeValue);
mperror("remove config num=[{}] failed, please check!", param.removeValue);
return false;
}
TLOGI("remove config num=[{}] success!", param.removeValue);
mpinfo("remove config num=[{}] success!", param.removeValue);
return true;
}
if (param.last_use || param.null_use) {
return true;
}
TLOGW("not matched!", param.removeValue);
mperror("not matched!", param.removeValue);
return false;
}
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);
@ -182,12 +102,7 @@ int main(int argc, char* argv[])
SetConsoleMode(hConsole, mode);
#endif
std::shared_ptr<int> deleter(new int(), [](int* p) {
fc_recovery_color();
delete p;
});
g_Config = std::make_shared<ClientConfig>();
g_Config = std::make_shared<CServerConfig>();
if (!g_Config->baseInit()) {
return -1;
}
@ -198,62 +113,27 @@ int main(int argc, char* argv[])
if (!param.parsed) {
return 0;
}
std::string ip;
long port{};
if (!param.connectValue.empty()) {
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) {
ip = matches[1].str();
port = std::stol(matches[2].str());
run = true;
} else {
TLOGE("Invalid connect value [{}]", param.connectValue);
if (!exec_cmd(param, run)) {
mperror("exec_cmd failed!");
return -1;
}
} else if (!exec_cmd(param, run)) {
TLOGW("exec_cmd failed!");
return -1;
}
if (!run && !param.direct_use && !param.last_use && !param.null_use) {
if (!run) {
return 0;
}
if (ip.empty()) {
std::vector<TransmSet> sets;
if (!g_Config->read_ini(sets)) {
std::vector<TransmSet> set;
if (!g_Config->read_ini(set)) {
return -1;
}
if (param.null_use) {
if (!select_server(sets, ip, port)) {
return -1;
}
} else {
if (param.last_use) {
if (!g_Config->get_last_use(ip, port)) {
return -1;
}
} else {
TransmSet use;
if (!g_Config->get_ini(sets, param.use_config, use)) {
TLOGW("Not found config by num:[{}]", param.use_config);
if (!g_Config->get_ini(set, param.use_config, use)) {
mperror("Not found config by num:[{}]", param.use_config);
return -1;
}
ip = use.ip;
port = use.port;
}
}
g_Config->save_last_use(ip, port);
}
TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
TLOGI("use ip => [{}], port => [{}]", ip, port);
std::shared_ptr<TransmClient> client = std::make_shared<TransmClient>();
client->run(ip, std::to_string(port), g_Config->get_config_dir());
TLOGI("exit ==========");
mpinfo("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
mpinfo("use ip => [{}], port => [{}]", use.ip, use.port);
CClient client;
client.run(use.ip, std::to_string(use.port));
mpinfo("exit ==========");
return 0;
}

26
client/xmake.lua Normal file
View File

@ -0,0 +1,26 @@
add_rules("mode.debug", "mode.release")
set_languages("c++17")
add_includedirs(os.scriptdir())
target("tsc")
add_options("boost")
set_kind("binary")
add_files("*.cpp")
add_deps("ofen")
add_deps("trans_util")
add_deps("trans_net")
add_deps("filecomplete")
if is_plat("mingw") then
add_links("ws2_32", "wsock32")
end
option("boost")
set_default(false)
set_showmenu(true)
boost_root_dir = os.getenv("BOOST_HEADER_DIR")
boost_lib_dir = os.getenv("BOOST_LIB_DIR")
boost_libs = os.getenv("BOOST_LIBS")
add_includedirs(boost_root_dir)
add_defines("USE_BOOST_FILESYSTEM")
add_linkdirs(boost_lib_dir)
add_links(boost_libs)

7
config.h.in Normal file
View File

@ -0,0 +1,7 @@
#ifndef VERSION_H
#define VERSION_H
#define VERSION_GIT_COMMIT "${GIT_COMMIT}"
#define VERSION_GIT_BRANCH "${GIT_BRANCH}"
#endif

View File

@ -1,9 +0,0 @@
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"
"${CXX_COMPILER_PATH}/libstdc++-6.dll"
"${CXX_COMPILER_PATH}/libwinpthread-1.dll"
)

View File

@ -1,46 +0,0 @@
#
if (UNIX)
execute_process(
COMMAND uname -a
OUTPUT_VARIABLE UNAME_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(UNAME_OUTPUT MATCHES "x86_64")
set(SYSTEM_ARCH "x86_64")
elseif(UNAME_OUTPUT MATCHES "aarch64")
set(SYSTEM_ARCH "aarch64")
elseif(UNAME_OUTPUT MATCHES "armv7l")
set(SYSTEM_ARCH "armv7l")
elseif(UNAME_OUTPUT MATCHES "i686")
set(SYSTEM_ARCH "i686")
elseif(UNAME_OUTPUT MATCHES "riscv64")
set(SYSTEM_ARCH "riscv64")
else()
set(SYSTEM_ARCH "unknown")
endif()
if(UNAME_OUTPUT MATCHES "alpine" OR UNAME_OUTPUT MATCHES "Alpine")
set(SYSTEM_ARCH "musl_${SYSTEM_ARCH}")
endif()
elseif (WIN32)
# ver
execute_process(COMMAND cmd /c ver
OUTPUT_VARIABLE VER_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(VER_OUTPUT MATCHES "XP")
message(STATUS "Windows XP platform.")
set(SYSTEM_ARCH "x86")
else()
execute_process(COMMAND cmd /c wmic os get osarchitecture
OUTPUT_VARIABLE WMIC_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(WMIC_OUTPUT MATCHES "64")
set(SYSTEM_ARCH "x86_64")
else()
set(SYSTEM_ARCH "x86")
endif()
endif()
else()
message(FATAL_ERROR "unknow system type.")
endif()
message(STATUS "SYSTEM_ARCH: ${SYSTEM_ARCH}")

@ -1 +0,0 @@
Subproject commit 2cf72f3e5c5313527b5d1442017caf06d51a3c02

@ -1 +1 @@
Subproject commit 4b6612cc63f21b4d092a0b5731ceb7f817f20d23
Subproject commit 909c68cf6e376acb975af21af7709b1129e5d0db

View File

@ -1,26 +0,0 @@
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()

View File

@ -1,264 +0,0 @@
#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;
}

BIN
img/base_intro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

View File

@ -12,11 +12,11 @@ bool CTcpClient::connect(const std::string& host, const std::string& port)
asio::ip::tcp::resolver resolver(io_context_);
asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(host, port);
asio::connect(socket_, endpoints);
TLOGI("Connected to {}:{}", host, port);
mpinfo("Connected to {}:{}", host, port);
is_normal_ = true;
return true;
} catch (const std::exception& ex) {
TLOGE("Connection failed: {}", ex.what());
mperror("Connection failed: {}", ex.what());
return false;
}
}
@ -27,9 +27,9 @@ void CTcpClient::disconnect()
try {
socket_.shutdown(asio::ip::tcp::socket::shutdown_both);
socket_.close();
TLOGI("Disconnected.");
mpinfo("Disconnected.");
} catch (const std::exception& ex) {
TLOGE("Error during disconnection: {}", ex.what());
mperror("Error during disconnection: {}", ex.what());
}
}
}
@ -37,15 +37,15 @@ void CTcpClient::disconnect()
bool CTcpClient::send(const char* data, int len)
{
if (!is_normal_) {
TLOGE("abnormal state, will not send.");
mperror("abnormal state, will not send.");
return false;
}
try {
auto send_size = asio::write(socket_, asio::buffer(data, len));
// TLOGI("Need Send len: {} Real Send len: {}", len, send_size);
// mpinfo("Need Send len: {} Real Send len: {}", len, send_size);
return static_cast<int>(send_size) == len;
} catch (const std::exception& ex) {
TLOGE("Send failed: {}", ex.what());
mperror("Send failed: {}", ex.what());
return false;
}
}
@ -66,10 +66,10 @@ void CTcpClient::async_recv()
return;
}
if (ec.value() == 125) {
TLOGI("{} exit.", __FUNCTION__);
mpinfo("{} exit.", __FUNCTION__);
return;
}
TLOGE("{} {} error => {}", __FUNCTION__, ec.value(), ec.message());
mperror("{} {} error => {}", __FUNCTION__, ec.value(), ec.message());
} else {
buffer_.push(tmp_buf_.data(), length);
while (true) {

View File

@ -1,11 +1,10 @@
#pragma once
#include "util.h"
#include <asio.hpp>
#include <mutex>
#include <of_util.h>
#include "util.h"
using namespace ofen;
class CTcpClient : public std::enable_shared_from_this<CTcpClient>
{

15
net/xmake.lua Normal file
View File

@ -0,0 +1,15 @@
add_rules("mode.debug", "mode.release")
set_languages("c++17")
if is_mode("debug") then
set_suffixname("d")
end
add_includedirs(os.scriptdir(), {public = true})
target("trans_net")
set_kind("static")
add_files("*.cpp")
add_deps("ofen")
add_deps("trans_util")
if is_plat("mingw") then
add_links("ws2_32", "wsock32")
end

2
ofen

@ -1 +1 @@
Subproject commit fbc26d565f7b73418dd9bfaee3fc67b77a56daba
Subproject commit cf634e27daf2f4435fc043d372bc5202fe164766

View File

@ -6,34 +6,12 @@ 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
OUTPUT_VARIABLE UNAME_OUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(UNAME_OUT MATCHES "alpine" OR UNAME_OUT MATCHES "Alpine")
message(STATUS "on musl static link")
target_link_libraries(tss PRIVATE -static;-static-libstdc++)
endif()
endif()

View File

@ -1,9 +1,6 @@
#include <csignal>
#include <iostream>
#include "server.h"
#include "version.h"
#include <of_path.h>
#include <iostream>
#ifdef _WIN32
#include <fcntl.h>
@ -14,36 +11,8 @@
#endif
#endif
#if defined(GRAB_CRASH)
#include <crashelper.h>
#endif
#ifdef USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
void msignal_handler(int signal)
{
if (signal == SIGINT) {
fc_recovery_color();
exit(signal);
}
}
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();
sh.register_user_sig_handler([](int sig) { msignal_handler(sig); });
#endif
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
@ -53,22 +22,17 @@ int main(int argc, char* argv[])
SetConsoleMode(hConsole, mode);
#endif
std::shared_ptr<int> deleter(new int(), [](int* p) {
fc_recovery_color();
delete p;
});
TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
mpinfo("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
int port = 9898;
if (argc < 2) {
TLOGI("Use Default Port:{}", port);
mpinfo("Use Default Port:{}", port);
} else {
std::string str_port(argv[1]);
port = std::stoi(str_port);
TLOGI("Use Port:{}", port);
mpinfo("Use Port:{}", port);
}
asio::io_context io_context;
TransmServer server(io_context);
CTcpServer server(io_context);
if (!server.start(port)) {
return -1;
}

View File

@ -1,19 +1,17 @@
#include "server.h"
#include <of_str.h>
#include <of_util.h>
#include <version.h>
using namespace ofen;
constexpr int check_idle_percycle = 1000 * 30; // 毫秒
constexpr int remove_after_time = 60; // 秒
TransmServer::TransmServer(asio::io_context& io_context) : io_context_(io_context), acceptor_(io_context)
CTcpServer::CTcpServer(asio::io_context& io_context) : io_context_(io_context), acceptor_(io_context)
{
th_run_ = true;
sleep_.set_timeout(check_idle_percycle);
}
TransmServer::~TransmServer()
CTcpServer::~CTcpServer()
{
th_run_ = false;
sleep_.contiune();
@ -22,48 +20,47 @@ TransmServer::~TransmServer()
}
}
bool TransmServer::start(unsigned short port)
bool CTcpServer::start(unsigned short port)
{
asio::ip::tcp::resolver resolver(io_context_);
asio::ip::tcp::resolver::query query(asio::ip::host_name(), "");
TLOGI("version: {}", VERSION_NUM);
TLOGI("opensource: {}", VERSION_URL);
try {
auto it = resolver.resolve(query);
TLOGD("Here are the local IP addresses you may use.");
TLOGD("===========================================");
mpdebug("Here are the local IP addresses you may use.");
mpdebug("===========================================");
int i = 1;
while (!it.empty()) {
asio::ip::address addr = it->endpoint().address();
TLOGI("({}){}", i, addr.to_string());
mpinfo("({}){}", i, addr.to_string());
++it;
++i;
}
TLOGD("===========================================");
mpdebug("===========================================");
} catch (const std::exception& e) {
TLOGW("{}", e.what());
TLOGI("will not show local IP.");
mpwarn("{}", e.what());
mpinfo("will not show local IP.");
}
asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
try {
acceptor_.open(endpoint.protocol());
// acceptor_.set_option(asio::socket_base::reuse_address(true));
acceptor_.set_option(asio::socket_base::reuse_address(true));
acceptor_.bind(endpoint);
acceptor_.listen();
} catch (const asio::system_error& e) {
TLOGE("Failed to bind to {}: {}", endpoint.address().to_string(), e.what());
mperror("Failed to bind to {}: {}", endpoint.address().to_string(), e.what());
return false;
}
auto bound_endpoint = acceptor_.local_endpoint();
server_ip_ = bound_endpoint.address().to_string() + ":" + std::to_string(bound_endpoint.port());
accept_client();
th_monitor_idle_ = std::thread([this]() { monitor_idle(); });
TLOGI("Server started on port {}", port);
mpinfo("Server started on port {}", port);
return true;
}
void TransmServer::stop()
void CTcpServer::stop()
{
acceptor_.close();
std::unique_lock<std::shared_mutex> lock(cli_mut_);
@ -75,48 +72,42 @@ void TransmServer::stop()
client_threads_.clear();
}
void TransmServer::get_client_list(CMessageInfo& msg_info)
std::vector<TaskList> CTcpServer::get_clients()
{
struct TmpInfo {
std::string id;
std::string online_time;
std::string uuid;
std::string task;
uint64_t timestamp{};
};
std::vector<TmpInfo> vec;
std::string msg;
{
std::vector<TaskList> result;
std::shared_lock<std::shared_mutex> lock(cli_mut_);
for (const auto& item : client_map_) {
TmpInfo tmp;
tmp.id = item.first;
tmp.online_time = item.second->online_time_;
tmp.timestamp = item.second->timestamp;
tmp.uuid = item.second->uuid;
tmp.task = item.second->task_;
vec.push_back(tmp);
}
TaskList t;
t.id_ = item.first;
t.task_ = item.second->task_;
t.task_time_ = item.second->task_time_;
t.online_time_ = item.second->online_time_;
result.push_back(t);
}
return result;
}
// 排序 vec 根据 client->timestamp
std::sort(vec.begin(), vec.end(), [](const TmpInfo& a, const TmpInfo& b) { return a.timestamp < b.timestamp; });
void CTcpServer::get_client_list(CFrameBuffer** buf)
{
CFrameBuffer* tbuf = *buf;
auto vec = get_clients();
std::string msg;
int index = 1;
for (const auto& item : vec) {
msg.append(fmt::format("[{}][{}][{}][{}]", index, item.id, item.online_time, item.uuid));
auto files = COfStr::split(item.task, "|");
msg.append(fmt::format("[{}][{}][{}][{}]", index, item.id_, item.online_time_, item.task_time_));
auto files = COfStr::split(item.task_, "|");
for (const auto& file : files) {
msg.append("\n" + file);
}
msg.append("\n");
++index;
}
msg_info.str = msg;
tbuf->data_ = new char[msg.size() + 1];
std::memset(tbuf->data_, 0x0, msg.size() + 1);
tbuf->len_ = std::snprintf(tbuf->data_, msg.size() + 1, "%s", msg.data());
}
void TransmServer::trans_data(CFrameBuffer* buf)
void CTcpServer::trans_data(CFrameBuffer* buf)
{
std::shared_ptr<ClientCache> fcli = nullptr;
std::shared_ptr<ClientCache> tcli = nullptr;
@ -133,31 +124,29 @@ void TransmServer::trans_data(CFrameBuffer* buf)
switch (buf->type_) {
case TYPE_GET_LIST: {
TLOGI("[{}] GetList.", buf->fid_);
CMessageInfo msg_info(server_ip_);
get_client_list(msg_info);
buf->fid_ = server_ip_;
serialize(msg_info, &buf->data_, buf->len_);
mpinfo("[{}] GetList.", buf->fid_);
get_client_list(&buf);
if (fcli && !send_frame(fcli->socket_, buf)) {
TLOGE("GetList send failed.");
mperror("GetList send failed.");
}
break;
}
case TYPE_UP_LIST: {
CMessageInfo msg_info(buf->fid_);
if (!deserialize(buf->data_, buf->len_, msg_info)) {
TLOGE("{} GetList deserialize failed.", __LINE__);
break;
}
std::string files_path = std::string(buf->data_, buf->len_);
#ifdef _WIN32
std::string turn_files_path = CCodec::u8_to_ansi(files_path);
#else
std::string turn_files_path(files_path);
#endif
mpinfo("[{}] UpList. {}", buf->fid_, turn_files_path);
if (fcli) {
fcli->task_ = msg_info.str;
fcli->uuid = msg_info.uuid;
fcli->task_ = files_path;
fcli->task_time_ = OfUtil::now_time();
}
break;
}
case TYPE_CANCEL_LIST: {
TLOGI("[{}] Cancle Task.", buf->fid_);
mpinfo("[{}] Cancle Task.", buf->fid_);
if (fcli) {
fcli->task_.clear();
fcli->task_time_.clear();
@ -183,13 +172,14 @@ void TransmServer::trans_data(CFrameBuffer* buf)
}
default:
if (check_double(buf, fcli, tcli) && tcli && !send_frame(tcli->socket_, buf)) {
TLOGE("Send from {} to {} failed Or One Offline.", buf->fid_, buf->tid_);
mperror("Send from {} to {} failed Or One Offline.", buf->fid_, buf->tid_);
}
break;
}
}
bool TransmServer::check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>& fcli, std::shared_ptr<ClientCache>& tcli)
bool CTcpServer::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_)) {
@ -200,25 +190,25 @@ bool TransmServer::check_double(CFrameBuffer* buf, std::shared_ptr<ClientCache>&
}
if (fcli == nullptr && tcli) {
buf->type_ = TYPE_OFFLINE;
TLOGW("A Notic {} That {} Offline.", buf->tid_, buf->fid_);
mpwarn("A Notic {} That {} Offline.", buf->tid_, buf->fid_);
send_frame(tcli->socket_, buf);
return false;
}
if (tcli == nullptr && fcli) {
std::swap(buf->fid_, buf->tid_);
buf->type_ = TYPE_OFFLINE;
TLOGW("B Notic {} That {} Offline.", buf->tid_, buf->fid_);
mpwarn("B Notic {} That {} Offline.", buf->tid_, buf->fid_);
send_frame(fcli->socket_, buf);
return false;
}
if (tcli == nullptr && fcli == nullptr) {
TLOGW("Both Offline.", buf->fid_, buf->tid_);
mpwarn("Both Offline.", buf->fid_, buf->tid_);
return false;
}
return true;
}
void TransmServer::accept_client()
void CTcpServer::accept_client()
{
auto socket = std::make_shared<asio::ip::tcp::socket>(io_context_);
acceptor_.async_accept(*socket, [this, socket](const asio::error_code& error) {
@ -230,14 +220,13 @@ void TransmServer::accept_client()
{
std::unique_lock<std::shared_mutex> lock(cli_mut_);
if (client_map_.size() >= 100) {
TLOGI("Max client connections reached. Closing connection from {}", client_key);
mpinfo("Max client connections reached. Closing connection from {}", client_key);
socket->close();
} else {
TLOGI("New connection from {}", client_key);
mpinfo("New connection from {}", client_key);
auto cache = std::make_shared<ClientCache>();
cache->socket_ = socket;
cache->online_time_ = OfUtil::now_time();
cache->timestamp = OfUtil::get_timestamp_ms();
cache->last_active_time_ = std::chrono::high_resolution_clock::now();
client_map_[client_key] = cache;
can = true;
@ -246,14 +235,15 @@ void TransmServer::accept_client()
if (!can) {
std::this_thread::sleep_for(std::chrono::minutes(1));
} else {
client_threads_[client_key] = std::thread(&TransmServer::th_client, this, socket, client_key);
client_threads_[client_key] = std::thread(&CTcpServer::th_client, this, socket, client_key);
}
}
accept_client();
});
}
void TransmServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socket, const std::string& client_key)
void CTcpServer::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_);
@ -263,7 +253,7 @@ void TransmServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socke
client_threads_.at(client_key).detach();
client_threads_.erase(client_key);
}
TLOGW("th_client deleter client {} exit.", client_key);
mpwarn("th_client deleter client {} exit.", client_key);
});
try {
@ -272,7 +262,7 @@ void TransmServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socke
{
std::shared_lock<std::shared_mutex> lock(cli_mut_);
if (!client_map_.count(client_key)) {
TLOGE("Not Find Client{} in cache.", client_key);
mperror("Not Find Client{} in cache.", client_key);
return;
}
cache = client_map_[client_key];
@ -300,19 +290,6 @@ void TransmServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socke
delete frame;
continue;
}
if (frame->type_ == TYPE_GET_ID) {
CMessageInfo msg_info("");
if (!deserialize(frame->data_, frame->len_, msg_info)) {
TLOGE("{} GetId deserialize failed.", __LINE__);
delete frame;
continue;
}
std::unique_lock<std::shared_mutex> lock(cli_mut_);
if (client_map_.count(client_key)) {
auto& cli = client_map_[client_key];
cli->uuid = msg_info.uuid;
}
}
frame->fid_ = client_key;
// 直接转发,不加入缓存。
trans_data(frame);
@ -323,11 +300,11 @@ void TransmServer::th_client(const std::shared_ptr<asio::ip::tcp::socket>& socke
}
}
} catch (std::exception& e) {
TLOGE("Error with client {}: {}", client_key, e.what());
mperror("Error with client {}: {}", client_key, e.what());
}
}
bool TransmServer::send_frame(const std::shared_ptr<asio::ip::tcp::socket>& socket, CFrameBuffer* buf)
bool CTcpServer::send_frame(const std::shared_ptr<asio::ip::tcp::socket>& socket, CFrameBuffer* buf)
{
char* out_buf{};
int out_len{};
@ -336,25 +313,26 @@ bool TransmServer::send_frame(const std::shared_ptr<asio::ip::tcp::socket>& sock
}
if (!CTransProtocal::pack(buf, &out_buf, out_len)) {
TLOGE("{} pack failed.", __FUNCTION__);
mperror("{} pack failed.", __FUNCTION__);
return false;
}
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_);
mperror("{} 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_);
mperror("send failed, type:{}, fid:{}, tid:{}, mark:{}", static_cast<int>(buf->type_), buf->fid_,
buf->tid_, buf->mark_);
}
delete[] out_buf;
return true;
}
void TransmServer::monitor_idle()
void CTcpServer::monitor_idle()
{
while (th_run_) {
sleep_.sleep();
@ -365,9 +343,11 @@ void TransmServer::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);
mpwarn("OnLine Time [{}] sec, Proactively disconnect:{}", duration, item.first);
remove_vec.push_back(item.first);
item.second->socket_->shutdown(asio::ip::tcp::socket::shutdown_both);
item.second->socket_->close();

View File

@ -13,33 +13,39 @@ 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_;
uint64_t timestamp{};
std::string task_{};
std::string task_time_{};
std::string online_time_{};
high_c last_active_time_;
FrameType cur_type_{TYPE_DEFAULT};
};
struct TaskList {
std::string id_{};
std::string task_{};
std::string task_time_{};
std::string online_time_{};
};
class TransmServer
class CTcpServer
{
public:
explicit TransmServer(asio::io_context& io_context);
~TransmServer();
CTcpServer(asio::io_context& io_context);
~CTcpServer();
public:
bool start(unsigned short port);
void stop();
private:
void get_client_list(CMessageInfo& msg_info);
std::vector<TaskList> get_clients();
void get_client_list(CFrameBuffer** buf);
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();

13
server/xmake.lua Normal file
View File

@ -0,0 +1,13 @@
add_rules("mode.debug", "mode.release")
set_languages("c++17")
add_includedirs(os.scriptdir())
target("tss")
set_kind("binary")
add_files("*.cpp")
add_deps("ofen")
add_deps("trans_util")
add_deps("trans_net")
if is_plat("mingw") then
add_links("ws2_32", "wsock32")
end

View File

@ -1,42 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(transm_test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
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)

View File

@ -1,228 +0,0 @@
#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);
}
}

View File

@ -1,41 +0,0 @@
#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);
}
}

View File

@ -1,185 +0,0 @@
#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);
}
}

View File

@ -1,77 +0,0 @@
#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();
}

View File

@ -1,109 +0,0 @@
#ifndef ASSISTANT_H
#define ASSISTANT_H
#include <fstream>
#include <functional>
#include <random>
#include <string>
#include <thread>
#ifdef USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
/**
* @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

View File

@ -1,9 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(tinyaes LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) #
add_library(tinyaes STATIC aes.h aes.c aes.hpp)
target_include_directories(tinyaes PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -1,539 +0,0 @@
/*
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
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include "aes.h"
#include <stdint.h>
#include <string.h> // CBC mode, for memset
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// 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)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif
// 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
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// 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.
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};
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};
// 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};
/*
* 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."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[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.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i) {
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i) {
{
k = (i - 1) * 4;
tempa[0] = RoundKey[k + 0];
tempa[1] = RoundKey[k + 1];
tempa[2] = RoundKey[k + 2];
tempa[3] = RoundKey[k + 3];
}
if (i % Nk == 0) {
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// 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.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i / Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4) {
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4;
k = (i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, 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)
{
KeyExpansion(ctx->RoundKey, key);
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, uint8_t* RoundKey)
{
uint8_t i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i) {
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
Tm = (*state)[i][0] ^ (*state)[i][1];
Tm = xtime(Tm);
(*state)[i][0] ^= Tm ^ Tmp;
Tm = (*state)[i][1] ^ (*state)[i][2];
Tm = xtime(Tm);
(*state)[i][1] ^= Tm ^ Tmp;
Tm = (*state)[i][2] ^ (*state)[i][3];
Tm = xtime(Tm);
(*state)[i][2] ^= Tm ^ Tmp;
Tm = (*state)[i][3] ^ t;
Tm = xtime(Tm);
(*state)[i][3] ^= Tm ^ Tmp;
}
}
// 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
#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))) ^
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
((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))))))
#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.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i) {
a = (*state)[i][0];
b = (*state)[i][1];
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);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = 1; round < Nr; ++round) {
SubBytes(state);
ShiftRows(state);
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes(state);
ShiftRows(state);
AddRoundKey(Nr, state, RoundKey);
}
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = (Nr - 1); round > 0; --round) {
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
InvMixColumns(state);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(0, state, RoundKey);
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)
void AES_ECB_encrypt(struct AES_ctx* ctx, uint8_t* buf)
{
// 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.
InvCipher((state_t*)buf, ctx->RoundKey);
}
#endif // #if defined(ECB) && (ECB == 1)
#if defined(CBC) && (CBC == 1)
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
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t* Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN) {
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
// printf("Step %d - %d", i/16, i);
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN) {
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(CBC) && (CBC == 1)
#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 */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];
unsigned i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) {
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer, ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) {
/* inc will owerflow */
if (ctx->Iv[bi] == 255) {
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(CTR) && (CTR == 1)

View File

@ -1,90 +0,0 @@
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// 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.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
// #ifndef CBC
// #define CBC 1
// #endif
// #ifndef ECB
// #define ECB 1
// #endif
// #ifndef CTR
// #define CTR 1
// #endif
// 只启用 CTR
#define CBC 0
#define CTR 1
#define ECB 0
#define AES128 1
// #define AES192 1
// #define AES256 1
#define AES_BLOCKLEN 16 // Block length in bytes AES is 128b block only
#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16 // Key length in bytes
#define AES_keyExpSize 176
#endif
struct AES_ctx {
uint8_t RoundKey[AES_keyExpSize];
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
uint8_t Iv[AES_BLOCKLEN];
#endif
};
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_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(struct AES_ctx* ctx, uint8_t* buf);
#endif // #if defined(ECB) && (ECB == !)
#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()
// 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);
#endif // #if defined(CBC) && (CBC == 1)
#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
// 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);
#endif // #if defined(CTR) && (CTR == 1)
#endif //_AES_H_

View File

@ -1,12 +0,0 @@
#ifndef _AES_HPP_
#define _AES_HPP_
#ifndef __cplusplus
#error Do not include the hpp header in a c project!
#endif //__cplusplus
extern "C" {
#include "aes.h"
}
#endif //_AES_HPP_

View File

@ -1,3 +0,0 @@
# 说明
来自:`https://github.com/kokke/tiny-AES-c`

View File

@ -9,5 +9,5 @@ endif()
set(SOURCES util.h util.cpp)
add_library(trans_util STATIC ${SOURCES})
target_link_libraries(trans_util PUBLIC ofen filecomplete tinyaes)
target_link_libraries(trans_util PUBLIC ofen)
target_include_directories(trans_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -1,16 +1,13 @@
#include "util.h"
#include <aes.hpp>
#include <cstdint>
#include <iostream>
#include <of_util.h>
#include <random>
#include <thread>
CTransProtocal::CTransProtocal() = default;
constexpr uint8_t kz = 16;
static bool use_encrypt = true;
CTransProtocal::~CTransProtocal() = default;
CTransProtocal::CTransProtocal()
= default;
CTransProtocal::~CTransProtocal()
= default;
/*
transm TCP
@ -126,274 +123,11 @@ void CTransProtocal::display_progress(float percent)
std::cout.flush();
}
CFrameBuffer::CFrameBuffer() = default;
CFrameBuffer::CFrameBuffer()
= default;
CFrameBuffer::~CFrameBuffer()
{
delete[] data_;
len_ = 0;
}
void serialize(CMessageInfo& msg_info, char** out_buf, int& len, bool reuse_mem)
{
auto& info = msg_info;
info.id = localtou8(info.id);
info.uuid = localtou8(info.uuid);
info.str = localtou8(info.str);
// 计算总长度
len = sizeof(int) * 4 + info.id.size() + info.uuid.size() + info.str.size() + info.data.size() + kz + 1;
// 《这里为了效率》,
// 认为如果 *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());
memcpy(ptr, &id_size, sizeof(int));
ptr += sizeof(int);
memcpy(ptr, info.id.data(), id_size);
ptr += id_size;
// 序列化 uuid
int uuid_size = static_cast<int>(info.uuid.size());
memcpy(ptr, &uuid_size, sizeof(int));
ptr += sizeof(int);
memcpy(ptr, info.uuid.data(), uuid_size);
ptr += uuid_size;
// 序列化 str
int str_size = static_cast<int>(info.str.size());
memcpy(ptr, &str_size, sizeof(int));
ptr += sizeof(int);
memcpy(ptr, info.str.data(), str_size);
ptr += str_size;
// 序列化 o
int o_size = static_cast<int>(info.data.size());
memcpy(ptr, &o_size, sizeof(int));
ptr += sizeof(int);
memcpy(ptr, info.data.data(), o_size);
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 + 1)) {
return false;
}
auto& info = msg_info;
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;
}
int id_size{};
memcpy(&id_size, ptr, sizeof(int));
ptr += sizeof(int);
remaining -= sizeof(int);
if (remaining < id_size) {
return false;
}
info.id.assign(ptr, id_size);
ptr += id_size;
remaining -= id_size;
// 反序列化 uuid
if (remaining < static_cast<int>(sizeof(int))) {
return false;
}
int uuid_size{};
memcpy(&uuid_size, ptr, sizeof(int));
ptr += sizeof(int);
remaining -= sizeof(int);
if (remaining < uuid_size) {
return false;
}
info.uuid.assign(ptr, uuid_size);
ptr += uuid_size;
remaining -= uuid_size;
// 反序列化 str
if (remaining < static_cast<int>(sizeof(int))) {
return false;
}
int str_size{};
memcpy(&str_size, ptr, sizeof(int));
ptr += sizeof(int);
remaining -= sizeof(int);
if (remaining < str_size) {
return false;
}
info.str.assign(ptr, str_size);
ptr += str_size;
remaining -= str_size;
// 反序列化 o
if (remaining < static_cast<int>(sizeof(int))) {
return false;
}
int o_size{};
memcpy(&o_size, ptr, sizeof(int));
ptr += sizeof(int);
remaining -= sizeof(int);
if (remaining < o_size) {
return false;
}
info.data.assign(ptr, o_size);
info.id = u8tolocal(info.id);
info.uuid = u8tolocal(info.uuid);
info.str = u8tolocal(info.str);
return true;
}
std::string u8tolocal(const std::string& str)
{
#ifdef _WIN32
return CCodec::u8_to_ansi(str);
#else
return str;
#endif
}
std::string localtou8(const std::string& str)
{
#ifdef _WIN32
return CCodec::ansi_to_u8(str);
#else
return str;
#endif
}
void hash(const char* data, uint8_t k[32])
{
uint32_t h = 5381;
for (const char* p = data; *p; p++) {
h = ((h << 5) + h) + *p; // DJB2
}
for (int i = 0; i < 32; i++) {
k[i] = (h >> (i % 4 * 8)) & 0xFF;
}
}
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(gen)); });
}
bool encrypt(const uint8_t* k, uint8_t* m, size_t len)
{
if (len < kz) {
return false;
}
uint8_t nonce[kz]{};
rdm(nonce, sizeof(nonce) - 4);
memcpy(m, nonce, kz);
struct AES_ctx ctx{};
AES_init_ctx_iv(&ctx, k, nonce);
AES_CTR_xcrypt_buffer(&ctx, m + kz, len - kz);
return true;
}
bool decrypt(const uint8_t* k, uint8_t* m, size_t len)
{
if (len < kz) {
return false;
}
uint8_t nonce[kz]{};
memcpy(nonce, m, kz);
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)
{
}
CMessageInfo::CMessageInfo(const CMessageInfo& info)
{
if (&info == this) {
return;
}
id = info.id;
uuid = info.uuid;
str = info.str;
data.assign(info.data.begin(), info.data.end());
}
CMessageInfo& CMessageInfo::operator=(const CMessageInfo& info)
{
if (&info == this) {
return *this;
}
id = info.id;
uuid = info.uuid;
str = info.str;
data.assign(info.data.begin(), info.data.end());
return *this;
}

View File

@ -1,15 +1,13 @@
#pragma once
#include <ColorConsole.hpp>
#include "of_util.h"
#include <chrono>
#include <cstdint>
#include <filecomplete.h>
#include <fmt/format.h>
#include <fmt/color.h>
#include <fmt/printf.h>
#include <functional>
#include <iomanip>
#include <sstream>
#include "of_util.h"
constexpr size_t g_BuffSize = 102400;
const size_t MAX_FRAME_SIZE = 10 * g_BuffSize;
enum FrameType : int16_t {
@ -29,41 +27,15 @@ enum FrameType : int16_t {
TYPE_OFFLINE,
TYPE_JUDGE_ACTIVE,
TYPE_REQUEST_UPDATE_LIST,
TYPE_REQUEST_DOWN_UPDATE_LIST,
TYPE_CONFIRM_UPDATE_LIST,
TYPE_UNCONFIRM_UPDATE_LIST,
TYPE_DONE_UPDATE_LIST,
TYPE_BUSY_UPDATE_LIST,
TYPE_FAILED_UPDATE_LIST,
TYPE_GET_ID,
TYPE_FILE_INFO,
TYPE_GET_DIRFILES,
TYPE_GET_DIRFILES_DONE,
TYPE_GET_DIRFILES_FAILED,
TYPE_FILE_SIZE
};
// 此结构体成员顺序不可变动,涉及到序列化反序列化。
struct CMessageInfo {
explicit CMessageInfo(const std::string& id);
CMessageInfo(const CMessageInfo& info);
CMessageInfo& operator=(const CMessageInfo& info);
std::string id;
std::string uuid;
std::string str;
std::string data;
};
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);
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
{
@ -72,8 +44,8 @@ public:
~CFrameBuffer();
public:
std::string fid_;
std::string tid_;
std::string fid_{};
std::string tid_{};
public:
FrameType type_{};
@ -113,36 +85,25 @@ inline std::string now_str()
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), "[%Y-%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 mpdebug(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Green << fmt::format(now_str() + format, std::forward<Args>(args)...) << std::endl;
fc_unlock_print();
fmt::print(fg(fmt::color::steel_blue), now_str() + format + "\n", std::forward<Args>(args)...);
}
template <typename... Args> void TLOGW(const std::string& format, Args&&... args)
template <typename... Args> void mpinfo(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Yellow << fmt::format(now_str() + format, std::forward<Args>(args)...) << std::endl;
fc_unlock_print();
fmt::print(fg(static_cast<fmt::color>(0x0EA113)), now_str() + format + "\n", std::forward<Args>(args)...);
}
template <typename... Args> void TLOGE(const std::string& format, Args&&... args)
template <typename... Args> void mpwarn(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Red << fmt::format(now_str() + format, std::forward<Args>(args)...) << std::endl;
fc_unlock_print();
fmt::print(fg(fmt::color::yellow_green), now_str() + format + "\n", std::forward<Args>(args)...);
}
template <typename... Args> void TLOGD(const std::string& format, Args&&... args)
template <typename... Args> void mperror(const std::string& format, Args&&... args)
{
fc_lock_print();
std::cout << ConsoleColor::Cyan << fmt::format(now_str() + format, std::forward<Args>(args)...) << std::endl;
fc_unlock_print();
fmt::print(fg(fmt::color::orange_red), now_str() + format + "\n", std::forward<Args>(args)...);
}

13
util/xmake.lua Normal file
View File

@ -0,0 +1,13 @@
add_rules("mode.debug", "mode.release")
set_languages("c++17")
if is_plat("windows") then
add_cxxflags("/source-charset:utf-8")
end
if is_mode("debug") then
set_suffixname("d")
end
add_includedirs(os.scriptdir(), {public = true})
target("trans_util")
set_kind("static")
add_files("*.cpp")
add_deps("ofen")

View File

@ -3,7 +3,5 @@
#define VERSION_GIT_COMMIT "@VERSION_GIT_HASH@"
#define VERSION_GIT_BRANCH "@VERSION_GIT_BRANCH@"
#define VERSION_NUM "@PROJECT_VERSION@"
#define VERSION_URL "@PROJECT_URL@"
#endif

22
xmake.lua Normal file
View File

@ -0,0 +1,22 @@
add_rules("mode.debug", "mode.release")
set_languages("c++17")
if is_plat("windows") then
add_cxxflags("/source-charset:utf-8")
end
if is_plat("mingw") then
add_cxxflags("-Wno-unused-variable -finput-charset=utf-8 -fexec-charset=gbk")
end
if has_config("xp") then
add_defines("_WIN32_WINNT=0x0501")
else
add_defines("_WIN32_WINNT=0x0601")
end
add_defines("FMT_HEADER_ONLY")
add_includedirs("$(projectdir)/3rd", {public = true})
add_includedirs("$(projectdir)/build", {public = true})
includes("util", "ofen", "net", "server", "client", "filecomplete")
add_configfiles("config.h.in", {filename = "version.h"})
option("xp")
set_default(false)
set_showmenu(true)

View File

@ -1,10 +1,24 @@
@echo off
cmake -Bxpbuild -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DXP_SYSTEM=ON -DUSE_BOOST=ON
cd xpbuild
cpack
echo Setting environment variables...
set BOOST_HEADER_DIR=C:\boost\include\boost-1_83
set BOOST_LIB_DIR=C:\boost\lib
set BOOST_LIBS=boost_filesystem-mgw7-mt-x32-1_83
echo Configuring xmake...
xmake f -p mingw -a i386 --boost=y --xp=y
if %errorlevel% neq 0 (
echo Error: cmake build failed.
echo Error: xmake configuration failed.
pause
exit /b 1
)
echo BOOST_HEADER_DIR=%BOOST_HEADER_DIR%
echo BOOST_LIB_DIR=%BOOST_LIB_DIR%
echo BOOST_LIBS=%BOOST_LIBS%
echo Building project with xmake...
xmake
if %errorlevel% neq 0 (
echo Error: xmake build failed.
pause
exit /b 1
)