Compare commits
	
		
			2 Commits
		
	
	
		
			eea5eee933
			...
			3ded526713
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3ded526713 | |||
| f6e7596367 | 
@ -1,6 +1,6 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.16)
 | 
			
		||||
 | 
			
		||||
project(transm VERSION 1.4.0 LANGUAGES CXX)
 | 
			
		||||
project(transm VERSION 1.4.1 LANGUAGES CXX)
 | 
			
		||||
set(CMAKE_CXX_STANDARD 17)
 | 
			
		||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										98
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								README.md
									
									
									
									
									
								
							@ -8,35 +8,32 @@
 | 
			
		||||
 | 
			
		||||
**注**:使用安装包的用户,如果想变更安装位置的话,请先卸载旧版本(原位置更新最新版不用卸载)。
 | 
			
		||||
 | 
			
		||||
# 一、简要介绍
 | 
			
		||||
# 一、说明
 | 
			
		||||
 | 
			
		||||
| 主要功能序号 | 简介                                                         |
 | 
			
		||||
| ------------ | ------------------------------------------------------------ |
 | 
			
		||||
| 1            | A端提交文件列表到服务端,B端可以从服务端查阅有哪些客户端提交的哪些任务,自行选择下载。 |
 | 
			
		||||
| 2            | A端可以提交一个下载任务文件给B端,B端会自动下载列表中的文件(可用作更新远端文件)。 |
 | 
			
		||||
| 3            | A端可以直接给B端发送文件(v1.3.1及其以后版本)。
 | 
			
		||||
**注意:** 当前说明文档仅针对`v1.4.1`及其以后的版本,此前的版本需要检出对应`tag`的`README.md`或者下载对应发布版本的源码,然后解压其中的`README.md`。
 | 
			
		||||
 | 
			
		||||
服务端程序支持任意个客户端相互之间**同时连接**和**同时传输**文件,吞吐瓶颈在服务端主机网络上。
 | 
			
		||||
 | 
			
		||||
- `tss`和`tsc`均为命令行端程序,无GUI。
 | 
			
		||||
 | 
			
		||||
- `tsc`从`tss`下载文件的时候,如果本地有已存在则会被**覆盖**(注意)。
 | 
			
		||||
 | 
			
		||||
- 介绍所指的客户端`A`、`B`是泛指,实际服务端程序支持任意个客户端相互之间**同时连接**和**同时传输**文件,吞吐瓶颈在服务端主机网络上。
 | 
			
		||||
 | 
			
		||||
## 一些特点(基于最新版本)
 | 
			
		||||
 | 
			
		||||
- 通配符传输。
 | 
			
		||||
- 终端自动文件补全。
 | 
			
		||||
- 自动更新远程文件。
 | 
			
		||||
- 多客户端可以同时互相收发文件。
 | 
			
		||||
- 自动检测对方掉线。
 | 
			
		||||
- 服务端自我安全防御(若部署到云端防止常规攻击)。
 | 
			
		||||
- 广泛的平台支持。
 | 
			
		||||
- 服务端仅转发数据,不存储数据。
 | 
			
		||||
- 终端自动文件补全。
 | 
			
		||||
- 自动检测对方掉线。
 | 
			
		||||
- 极小的、单个文件。
 | 
			
		||||
- 干净,不会在客户机环境到处遗留临时文件。
 | 
			
		||||
- 支持相对路径处理。
 | 
			
		||||
- 单端可以操作下载发送。
 | 
			
		||||
- 支持单客户端操作收发本地文件。
 | 
			
		||||
- 多客户端可以同时互相收发文件。
 | 
			
		||||
- 服务端仅转发数据,不存储数据。
 | 
			
		||||
- 运行时无其他三方二进制库依赖。
 | 
			
		||||
- 临时公网传输,无需使用三方需要登陆软件。
 | 
			
		||||
- `Linux`到`Linux`文件传输保留原权限。
 | 
			
		||||
- 干净,不会在客户机环境到处遗留临时文件。
 | 
			
		||||
- 临时公网传输,无需使用三方需要登陆软件。
 | 
			
		||||
- 服务端自我安全防御(若部署到云端防止常规攻击)。
 | 
			
		||||
- 支持拒绝同时操作同设备同路径文件,防止文件内容丢失。
 | 
			
		||||
 | 
			
		||||
# 二、使用说明
 | 
			
		||||
 | 
			
		||||
@ -44,76 +41,31 @@
 | 
			
		||||
 | 
			
		||||
- 对于服务端程序`tss`,绑定默认绑定`0.0.0.0`的`9898`端口,如果需要修改端口,使用参数启动,示例:`tss 9898`。
 | 
			
		||||
- 对于客户端程序`tsc`,请使用`tsc --help`查看使用方式。
 | 
			
		||||
- `Up`指令后面的文件名路径,如果是非全路径(即相对路径),程序会自动拼接到当前`tsc`工作目录(如`Up dira/test.txt`也是可以的)。
 | 
			
		||||
 | 
			
		||||
## 2.命令使用(截图可能过时,但使用方式大致如此)
 | 
			
		||||
## 2.使用
 | 
			
		||||
 | 
			
		||||
### 2.1 客户端 tsc 简介
 | 
			
		||||
`tsc`连接到服务端`tss`后,可以输入`h`查看所有指令的具体介绍。
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 2.2 功能一简介
 | 
			
		||||
## 3.介绍补充
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
- 需要传入存储路径的指令,如果未填写,默认`tsc`当前工作目录。
 | 
			
		||||
- `任务文件`格式统一为`UTF-8`编码。
 | 
			
		||||
- 支持相对路径处理。
 | 
			
		||||
- `UpTask`和`DownTask`指令`任务文件`格式完全一致,均为左文件,右目录,尽管是下载别人文件。
 | 
			
		||||
 | 
			
		||||
### 2.3 功能二简介
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 3.1 Get功能
 | 
			
		||||
 | 
			
		||||
`v1.2.3`版本起(含),当某个客户端的列表文件数量过多时,只展示部分(会显示总数量)。
 | 
			
		||||
 | 
			
		||||
### 3.2 Up功能
 | 
			
		||||
 | 
			
		||||
`v1.2.3`版本起(含),`up`后路径支持`?`和`*`通配符:
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
up /home/zhang/png/cloud*.png|/home/zhang/download/202?-*.exe
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.3 Down功能
 | 
			
		||||
 | 
			
		||||
`v1.2.3`版本起(含),支持传入下载位置(默认命令所在目录):
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
down 1 dira/dirb
 | 
			
		||||
down 1 ../download
 | 
			
		||||
down 2 /home/zhang/document
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.4 Update功能
 | 
			
		||||
 | 
			
		||||
命令格式为:`Update 客户端标号 列表文件`
 | 
			
		||||
 | 
			
		||||
`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},任务文件所在目录(即`task.txt`所在目录,该变量仅支持发送端,也就是`|`左侧,因为接收端没有任务文件所在路径)
 | 
			
		||||
- ${CURRENT},任务文件所在目录(该变量仅支持发送端,也就是`|`左侧,因为接收端没有任务文件所在路径)
 | 
			
		||||
 | 
			
		||||
```txt
 | 
			
		||||
${HOME}/截图/Ni.jpg|${HOME}/dira
 | 
			
		||||
${CURRENT}/xxx.zip|D:\
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**NOTE**: `列表文件`的格式为`UTF-8`编码。
 | 
			
		||||
 | 
			
		||||
# 注意
 | 
			
		||||
 | 
			
		||||
- 如果两个`tsc`客户端在同一台机器上同时收发同一个文件将导致文件丢失损坏(如果收发操作的是同一个文件)。
 | 
			
		||||
 | 
			
		||||
# 编译
 | 
			
		||||
# 三、编译
 | 
			
		||||
 | 
			
		||||
当前项目支持`cmake`构建工具。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,78 @@ CClient::~CClient()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CClient::print_help(bool detail)
 | 
			
		||||
{
 | 
			
		||||
    TLOGI("version: {}", VERSION_NUM);
 | 
			
		||||
    TLOGI("opensource: {}", VERSION_URL);
 | 
			
		||||
    TLOGW("SupportCmd ==>");
 | 
			
		||||
 | 
			
		||||
    if (!detail) {
 | 
			
		||||
        TLOGW("Get|Who|Where|Ls|Sub|Fetch|Up|Down|UpTask|DownTask");
 | 
			
		||||
        TLOGI("You can use 'h' to show cmd's detail.");
 | 
			
		||||
        TLOGI("You can use 'end' or 'ctrl-c' to exit.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr char* sp = "==================================================";
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Get#");
 | 
			
		||||
    TLOGI("  des: Get all clients on server.");
 | 
			
		||||
    TLOGI("  cmd: Get get g G");
 | 
			
		||||
    TLOGI("  arg: NULL");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Who#");
 | 
			
		||||
    TLOGI("  des: Show current client's ID.");
 | 
			
		||||
    TLOGI("  cmd: Who who");
 | 
			
		||||
    TLOGI("  arg: NULL");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Where#");
 | 
			
		||||
    TLOGI("  des: Show current client's work dir.");
 | 
			
		||||
    TLOGI("  cmd: Where where wh");
 | 
			
		||||
    TLOGI("  arg: NULL");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Ls#");
 | 
			
		||||
    TLOGI("  des: List one client's all files and folders.");
 | 
			
		||||
    TLOGI("  cmd: Ls ls");
 | 
			
		||||
    TLOGI("  arg: @id @dir");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Sub#");
 | 
			
		||||
    TLOGI("  des: Submit local file's list on the server.");
 | 
			
		||||
    TLOGI("  cmd: Sub sub");
 | 
			
		||||
    TLOGI("  arg: @files(Separated by | if multi)");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Fetch#");
 | 
			
		||||
    TLOGI("  des: Fetch one client's submited files.");
 | 
			
		||||
    TLOGI("  cmd: Fetch fetch");
 | 
			
		||||
    TLOGI("  arg: @id @savedir");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Clear#");
 | 
			
		||||
    TLOGI("  des: clear submited list.");
 | 
			
		||||
    TLOGI("  cmd: Clear clear c C");
 | 
			
		||||
    TLOGI("  arg: NULL");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Up#");
 | 
			
		||||
    TLOGI("  des: Upload local files to one client directly.");
 | 
			
		||||
    TLOGI("  cmd: Up up");
 | 
			
		||||
    TLOGI("  arg: @id @files(Sep by | if multi) @savedir");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#Down#");
 | 
			
		||||
    TLOGI("  des: Down one client's files directly.");
 | 
			
		||||
    TLOGI("  cmd: Down down");
 | 
			
		||||
    TLOGI("  arg: @id @files(Sep by | if multi) @savedir(opt)");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#UpTask#");
 | 
			
		||||
    TLOGI("  des: Down one client's files directly.");
 | 
			
		||||
    TLOGI("  cmd: UpTask uptask ut");
 | 
			
		||||
    TLOGI("  arg: @id @taskfile(UTF-8)");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
    TLOGW("#DownTask#");
 | 
			
		||||
    TLOGI("  des: Down one client's files directly.");
 | 
			
		||||
    TLOGI("  cmd: DownTask downtask dt");
 | 
			
		||||
    TLOGI("  arg: @id @taskfile(UTF-8)");
 | 
			
		||||
    TLOGI("{}", sp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CClient::run(const std::string& ip, const std::string& port, const std::string& config_dir)
 | 
			
		||||
{
 | 
			
		||||
    fs::path fp(config_dir);
 | 
			
		||||
@ -80,16 +152,13 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
 | 
			
		||||
    std::thread thread([&]() { io_context_.run(); });
 | 
			
		||||
    th_down_active_ = std::thread([&]() { judget_down_active(); });
 | 
			
		||||
 | 
			
		||||
    get_id();
 | 
			
		||||
    if (ip == "127.0.0.1") {
 | 
			
		||||
        get_task_list();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TLOGI("version: {}", VERSION_NUM);
 | 
			
		||||
    TLOGI("opensource: {}", VERSION_URL);
 | 
			
		||||
    TLOGW("SupportCmd ==>");
 | 
			
		||||
    TLOGW("Get|Up|Down|Cancel|Update|Who|Where|Ls|Req");
 | 
			
		||||
    print_help(false);
 | 
			
		||||
    fc_append('|');
 | 
			
		||||
    get_id();
 | 
			
		||||
 | 
			
		||||
    if (ip == "127.0.0.1") {
 | 
			
		||||
        get_clients();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (1) {
 | 
			
		||||
        char* readline = fc_readline();
 | 
			
		||||
@ -104,6 +173,12 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
 | 
			
		||||
        fc_free(readline);
 | 
			
		||||
        std::cout << "" << std::endl;
 | 
			
		||||
        cmd_input = ofen::COfStr::trim(cmd_input);
 | 
			
		||||
 | 
			
		||||
        if (cmd_input == "help" || cmd_input == "h") {
 | 
			
		||||
            print_help(true);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cmd_input == "end" || cmd_input == "End") {
 | 
			
		||||
            th_run_ = false;
 | 
			
		||||
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
 | 
			
		||||
@ -119,11 +194,11 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (cmd_input == "Get" || cmd_input == "get" || cmd_input == "g" || cmd_input == "G") {
 | 
			
		||||
            get_task_list();
 | 
			
		||||
            get_clients();
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (cmd_input == "Cancel" || cmd_input == "cancel" || cmd_input == "c" || cmd_input == "C") {
 | 
			
		||||
            cancel_task();
 | 
			
		||||
        if (cmd_input == "Clear" || cmd_input == "clear" || cmd_input == "c" || cmd_input == "C") {
 | 
			
		||||
            cmd_clear_submited();
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        auto vec = COfStr::split(cmd_input, " ");
 | 
			
		||||
@ -135,28 +210,32 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
 | 
			
		||||
        std::string scmd = param.substr(0, param.find_first_of(" "));
 | 
			
		||||
        param.erase(0, param.find_first_of(" ") + 1);
 | 
			
		||||
 | 
			
		||||
        if (scmd == "Update" || scmd == "update") {
 | 
			
		||||
            request_update_list(param);
 | 
			
		||||
        if (scmd == "UpTask" || scmd == "uptask" || scmd == "ut") {
 | 
			
		||||
            cmd_sub_task(param, true);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Send" || scmd == "send") {
 | 
			
		||||
            send_files(param);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Down" || scmd == "down") {
 | 
			
		||||
            down_task(param);
 | 
			
		||||
        if (scmd == "DownTask" || scmd == "downtask" || scmd == "dt") {
 | 
			
		||||
            cmd_sub_task(param, false);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Up" || scmd == "up") {
 | 
			
		||||
            up_task(param);
 | 
			
		||||
            cmd_upload_files(param);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Fetch" || scmd == "fetch") {
 | 
			
		||||
            cmd_fetch_files(param);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Sub" || scmd == "sub") {
 | 
			
		||||
            cmd_sub_list(param);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Ls" || scmd == "ls") {
 | 
			
		||||
            require_dir_files(param);
 | 
			
		||||
            cmd_ls(param);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (scmd == "Req" || scmd == "req") {
 | 
			
		||||
            down_req_list(param);
 | 
			
		||||
        if (scmd == "Down" || scmd == "down") {
 | 
			
		||||
            cmd_down_list(param);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        TLOGE("No matched cmd, May be param size incorrect.");
 | 
			
		||||
@ -166,14 +245,14 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
 | 
			
		||||
    TLOGI("{} exit.", __FUNCTION__);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::get_task_list()
 | 
			
		||||
bool CClient::get_clients()
 | 
			
		||||
{
 | 
			
		||||
    std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
 | 
			
		||||
    buf->type_ = TYPE_GET_LIST;
 | 
			
		||||
    return send_frame(buf.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::down_task(const std::string& param)
 | 
			
		||||
bool CClient::cmd_fetch_files(const std::string& param)
 | 
			
		||||
{
 | 
			
		||||
    if (downloading_) {
 | 
			
		||||
        TLOGW("Have Task Downloading, Please wait.....");
 | 
			
		||||
@ -218,7 +297,7 @@ bool CClient::down_task(const std::string& param)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::up_task(const std::string& param)
 | 
			
		||||
bool CClient::cmd_sub_list(const std::string& param)
 | 
			
		||||
{
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
@ -270,7 +349,7 @@ bool CClient::up_task(const std::string& param)
 | 
			
		||||
    return send_frame(buf.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::cancel_task()
 | 
			
		||||
bool CClient::cmd_clear_submited()
 | 
			
		||||
{
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
@ -286,7 +365,7 @@ bool CClient::cancel_task()
 | 
			
		||||
    return send_frame(buf.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::send_files(const std::string& param)
 | 
			
		||||
bool CClient::cmd_upload_files(const std::string& param)
 | 
			
		||||
{
 | 
			
		||||
    auto tvec = COfStr::split(param, " ");
 | 
			
		||||
    if (tvec.size() < 3) {
 | 
			
		||||
@ -338,7 +417,7 @@ bool CClient::send_files(const std::string& param)
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto handel_ret = handle_user_select(mre);
 | 
			
		||||
    auto handel_ret = handle_user_select(mre, true);
 | 
			
		||||
    if (handel_ret.empty()) {
 | 
			
		||||
        TLOGE("handle_user_select not pass, abort action!");
 | 
			
		||||
        return false;
 | 
			
		||||
@ -470,7 +549,7 @@ void CClient::report_trans_ret(TransState state, const std::string& key)
 | 
			
		||||
 | 
			
		||||
    功能为,请求某个客户端,更新我所列出的文件,右侧是远端需要存储的目录(必须存在,不存在则不理会)
 | 
			
		||||
*/
 | 
			
		||||
bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
bool CClient::cmd_sub_task(const std::string& param, bool is_send)
 | 
			
		||||
{
 | 
			
		||||
    auto tvec = COfStr::split(param, " ");
 | 
			
		||||
    if (tvec.size() < 2) {
 | 
			
		||||
@ -490,20 +569,19 @@ bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto& sr = clients_[index];
 | 
			
		||||
    bool local_trans = false;
 | 
			
		||||
    if (sr->id == own_id_) {
 | 
			
		||||
        TLOGW("local_trans path handle mode!!!");
 | 
			
		||||
        local_trans = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 读取list文件
 | 
			
		||||
    std::string list_file_full = COfPath::to_full(list_file);
 | 
			
		||||
 | 
			
		||||
    if (fs::is_directory(list_file_full)) {
 | 
			
		||||
        TLOGE("{} is a directory, only support file.", list_file_full);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool local_trans = false;
 | 
			
		||||
    if (sr->uuid == uuid_) {
 | 
			
		||||
        TLOGW("local_trans path handle mode!!!");
 | 
			
		||||
        local_trans = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ifstream in(list_file_full);
 | 
			
		||||
    if (!in.is_open()) {
 | 
			
		||||
        TLOGE("Can't Open File:{}", list_file_full);
 | 
			
		||||
@ -515,6 +593,7 @@ bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
    in.close();
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    // 从文件中读取的内容还是要手动转码一下。
 | 
			
		||||
    content = CCodec::u8_to_ansi(content);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -533,27 +612,17 @@ bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
        if (v.size() >= 2) {
 | 
			
		||||
            auto pr = variable_handle(list_file_full, v[0], true);
 | 
			
		||||
            pr = COfPath::standardize(pr);
 | 
			
		||||
            if (!fs::exists(pr)) {
 | 
			
		||||
            if (is_send && !fs::exists(pr)) {
 | 
			
		||||
                TLOGE("file {} not exist.", pr);
 | 
			
		||||
                valid = false;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            TLOGI("--->check pass {}:{}", line, pr);
 | 
			
		||||
            auto sec = v[1];
 | 
			
		||||
            if (local_trans) {
 | 
			
		||||
                sec = variable_handle(list_file_full, v[1], true);
 | 
			
		||||
                sec = COfPath::standardize(sec);
 | 
			
		||||
                if (!fs::is_directory(sec) || !fs::exists(sec)) {
 | 
			
		||||
                    TLOGE("not directory or {} not exist.", pr);
 | 
			
		||||
                    valid = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                if (COfPath::is_same_dir(pr, sec)) {
 | 
			
		||||
                    TLOGE("Local Trans Can't Transfer {} to {}", pr, sec);
 | 
			
		||||
                    valid = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            if (is_send) {
 | 
			
		||||
                TLOGI("--->check pass {}:{}", line, pr);
 | 
			
		||||
            }
 | 
			
		||||
            auto sec = v[1];
 | 
			
		||||
            sec = variable_handle(list_file_full, sec, local_trans);
 | 
			
		||||
            sec = COfPath::standardize(sec);
 | 
			
		||||
            mre[line++] = pr + "|" + sec;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
@ -566,7 +635,7 @@ bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto handel_ret = handle_user_select(mre);
 | 
			
		||||
    auto handel_ret = handle_user_select(mre, is_send);
 | 
			
		||||
    if (handel_ret.empty()) {
 | 
			
		||||
        TLOGE("handle_user_select not pass, abort action!");
 | 
			
		||||
        return false;
 | 
			
		||||
@ -574,7 +643,12 @@ bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
 | 
			
		||||
    list_file_ = list_file_full;
 | 
			
		||||
    std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
 | 
			
		||||
    buf->type_ = TYPE_REQUEST_UPDATE_LIST;
 | 
			
		||||
 | 
			
		||||
    if (is_send) {
 | 
			
		||||
        buf->type_ = TYPE_REQUEST_UPDATE_LIST;
 | 
			
		||||
    } else {
 | 
			
		||||
        buf->type_ = TYPE_REQUEST_DOWN_UPDATE_LIST;
 | 
			
		||||
    }
 | 
			
		||||
    CMessageInfo msg_info;
 | 
			
		||||
    msg_info.str = handel_ret;
 | 
			
		||||
    serialize(msg_info, &buf->data_, buf->len_);
 | 
			
		||||
@ -587,7 +661,7 @@ bool CClient::request_update_list(const std::string& param)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::check_update_list(const std::string& content, std::map<std::string, std::string>& files)
 | 
			
		||||
bool CClient::variable_and_parse_files(const std::string& content, std::map<std::string, std::string>& files)
 | 
			
		||||
{
 | 
			
		||||
    auto vec = COfStr::split(content, "\n");
 | 
			
		||||
    bool valid = true;
 | 
			
		||||
@ -665,7 +739,7 @@ bool CClient::get_dir_files(const std::string& dir, std::string& out, std::strin
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::require_dir_files(const std::string& param)
 | 
			
		||||
bool CClient::cmd_ls(const std::string& param)
 | 
			
		||||
{
 | 
			
		||||
    auto tvec = COfStr::split(param, " ");
 | 
			
		||||
    if (tvec.size() < 2) {
 | 
			
		||||
@ -695,7 +769,7 @@ bool CClient::require_dir_files(const std::string& param)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::down_req_list(const std::string& param)
 | 
			
		||||
bool CClient::cmd_down_list(const std::string& param)
 | 
			
		||||
{
 | 
			
		||||
    auto tvec = COfStr::split(param, " ");
 | 
			
		||||
    if (tvec.size() < 2) {
 | 
			
		||||
@ -796,6 +870,45 @@ std::vector<std::string> CClient::load_line_his()
 | 
			
		||||
    return history;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CClient::variable_and_reverse_files(const std::string& source)
 | 
			
		||||
{
 | 
			
		||||
    auto vec = COfStr::split(source, "\n");
 | 
			
		||||
    std::string result;
 | 
			
		||||
    bool valid = true;
 | 
			
		||||
    for (const auto& item : vec) {
 | 
			
		||||
        auto rl = COfStr::trim(item);
 | 
			
		||||
        if (rl.empty()) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        auto vi = COfStr::split(rl, "|");
 | 
			
		||||
        if (vi.size() != 2) {
 | 
			
		||||
            TLOGE("Size not 2 {}", item);
 | 
			
		||||
            valid = false;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        auto pr = variable_handle("", vi[1], false);
 | 
			
		||||
        if (!fs::exists(pr)) {
 | 
			
		||||
            valid = false;
 | 
			
		||||
            TLOGE("Not exist {}", pr);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        TLOGI("---> check pass {}", pr);
 | 
			
		||||
 | 
			
		||||
        fs::path ppr(pr);
 | 
			
		||||
        fs::path pvi(vi[0]);
 | 
			
		||||
 | 
			
		||||
        auto lp = ppr.append(pvi.filename().string()).string();
 | 
			
		||||
        auto rp = pvi.parent_path().string();
 | 
			
		||||
 | 
			
		||||
        auto line = lp + "|" + rp;
 | 
			
		||||
        result = result + line + "\n";
 | 
			
		||||
    }
 | 
			
		||||
    if (!valid) {
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CClient::save_uuid()
 | 
			
		||||
{
 | 
			
		||||
    fs::path uuid_path(uuid_path_);
 | 
			
		||||
@ -1058,6 +1171,23 @@ void CClient::handle_frame(CFrameBuffer* buf)
 | 
			
		||||
        report_trans_ret(TRANS_FAILED);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    case TYPE_REQUEST_DOWN_UPDATE_LIST: {
 | 
			
		||||
        CMessageInfo msg_info;
 | 
			
		||||
        if (!deserialize(buf->data_, buf->len_, msg_info)) {
 | 
			
		||||
            TLOGE("{} GetList deserialize failed.", __LINE__);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        delete[] buf->data_;
 | 
			
		||||
        msg_info.str = variable_and_reverse_files(msg_info.str);
 | 
			
		||||
        serialize(msg_info, &buf->data_, buf->len_);
 | 
			
		||||
        std::swap(buf->tid_, buf->fid_);
 | 
			
		||||
        buf->type_ = TYPE_REQUEST_UPDATE_LIST;
 | 
			
		||||
        if (!send_frame(buf)) {
 | 
			
		||||
            TLOGE("Send Failed {}.", __LINE__);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    };
 | 
			
		||||
    case TYPE_REQUEST_UPDATE_LIST: {
 | 
			
		||||
        std::map<std::string, std::string> files;
 | 
			
		||||
        if (down_ && down_->trans_state_ == TRANS_REDAY) {
 | 
			
		||||
@ -1069,7 +1199,7 @@ void CClient::handle_frame(CFrameBuffer* buf)
 | 
			
		||||
                TLOGE("{} GetList deserialize failed.", __LINE__);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (check_update_list(msg_info.str, files)) {
 | 
			
		||||
            if (variable_and_parse_files(msg_info.str, files)) {
 | 
			
		||||
                buf->type_ = TYPE_CONFIRM_UPDATE_LIST;
 | 
			
		||||
            } else {
 | 
			
		||||
                buf->type_ = TYPE_UNCONFIRM_UPDATE_LIST;
 | 
			
		||||
@ -1240,16 +1370,19 @@ void CClient::judget_down_active()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CClient::variable_handle(const std::string& task_list_path, const std::string& source,
 | 
			
		||||
                                     bool is_send)
 | 
			
		||||
                                     bool is_local)
 | 
			
		||||
{
 | 
			
		||||
    std::string result(source);
 | 
			
		||||
    // 支持的变量如下:
 | 
			
		||||
    //      ${HOME} 用户目录(发送端接收端均支持)
 | 
			
		||||
    //      ${CURRENT} 任务文件所在目录(该变量仅支持发送端,因为接收端没有任务文件所在路径)
 | 
			
		||||
    if (source.find("${HOME}") != std::string::npos) {
 | 
			
		||||
    if (is_local && source.find("${HOME}") != std::string::npos) {
 | 
			
		||||
        result = COfStr::replace(result, "${HOME}", COfPath::get_home());
 | 
			
		||||
    }
 | 
			
		||||
    if (is_send && source.find("${CURRENT}") != std::string::npos) {
 | 
			
		||||
    if (is_local && source.find("${CURRENT}") != std::string::npos) {
 | 
			
		||||
        if (task_list_path.empty()) {
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
        fs::path p(task_list_path);
 | 
			
		||||
        std::string list_dir = p.parent_path().string();
 | 
			
		||||
        result = COfStr::replace(result, "${CURRENT}", list_dir);
 | 
			
		||||
@ -1257,14 +1390,25 @@ std::string CClient::variable_handle(const std::string& task_list_path, const st
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CClient::handle_user_select(const std::unordered_map<int, std::string>& source)
 | 
			
		||||
std::string CClient::handle_user_select(const std::unordered_map<int, std::string>& source, bool is_send)
 | 
			
		||||
{
 | 
			
		||||
    std::string handled_content{};
 | 
			
		||||
    std::string input{};
 | 
			
		||||
 | 
			
		||||
    if (!is_send) {
 | 
			
		||||
        handled_content.clear();   // 清空之前的内容
 | 
			
		||||
        for (const auto& pair : source) {
 | 
			
		||||
            handled_content.append(pair.second + "\n");
 | 
			
		||||
        }
 | 
			
		||||
        return handled_content;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        TLOGI("numbers by space, or '0' use all, 'end' to quit: ");
 | 
			
		||||
        std::getline(std::cin, input);
 | 
			
		||||
        // std::getline(std::cin, input);
 | 
			
		||||
        char* readline = fc_readline();
 | 
			
		||||
        input = std::string(readline);
 | 
			
		||||
        fc_free(readline);
 | 
			
		||||
 | 
			
		||||
        if (input == "end") {
 | 
			
		||||
            handled_content.clear();
 | 
			
		||||
 | 
			
		||||
@ -48,35 +48,37 @@ public:
 | 
			
		||||
    void run(const std::string& ip, const std::string& port, const std::string& config_dir);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    bool get_task_list();
 | 
			
		||||
    bool down_task(const std::string& param);
 | 
			
		||||
    bool up_task(const std::string& param);
 | 
			
		||||
    bool cancel_task();
 | 
			
		||||
    bool send_files(const std::string& param);
 | 
			
		||||
    bool get_clients();
 | 
			
		||||
    bool cmd_fetch_files(const std::string& param);
 | 
			
		||||
    bool cmd_sub_list(const std::string& param);
 | 
			
		||||
    bool cmd_clear_submited();
 | 
			
		||||
    bool cmd_upload_files(const std::string& param);
 | 
			
		||||
    bool down_one_file(int remote_id, const std::string& file, const std::string& local_dir = "");
 | 
			
		||||
    void report_trans_ret(TransState state, const std::string& key = "");
 | 
			
		||||
    bool request_update_list(const std::string& param);
 | 
			
		||||
    bool check_update_list(const std::string& content, std::map<std::string, std::string>& files);
 | 
			
		||||
    bool cmd_sub_task(const std::string& param, bool is_send);
 | 
			
		||||
    bool variable_and_parse_files(const std::string& content, std::map<std::string, std::string>& files);
 | 
			
		||||
    bool down_update_file(const std::map<std::string, std::string>& files);
 | 
			
		||||
    bool get_dir_files(const std::string& dir, std::string& out, std::string& error);
 | 
			
		||||
    bool require_dir_files(const std::string& param);
 | 
			
		||||
    bool down_req_list(const std::string& param);
 | 
			
		||||
    bool cmd_ls(const std::string& param);
 | 
			
		||||
    bool cmd_down_list(const std::string& param);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
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_send);
 | 
			
		||||
    std::string handle_user_select(const std::unordered_map<int, std::string>& source);
 | 
			
		||||
    std::string variable_handle(const std::string& task_list_path, const std::string& source, bool is_local);
 | 
			
		||||
    std::string handle_user_select(const std::unordered_map<int, std::string>& source, bool is_send);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::mutex mutex_;
 | 
			
		||||
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
Subproject commit c477dad67e171aecf9fb5b8ce361119bf459ff8f
 | 
			
		||||
Subproject commit 4b6612cc63f21b4d092a0b5731ceb7f817f20d23
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								img/func1.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/func1.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 796 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								img/func2.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/func2.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 1.1 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								img/tsc_use.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/tsc_use.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 578 KiB  | 
@ -29,6 +29,7 @@ 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,
 | 
			
		||||
@ -106,7 +107,7 @@ 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), "[%Y-%m-%d %H:%M:%S") << "." << std::setfill('0')
 | 
			
		||||
    timestamp << std::put_time(std::localtime(&time_t_now), "[%m-%d %H:%M:%S") << "." << std::setfill('0')
 | 
			
		||||
              << std::setw(3) << milliseconds.count() << "] ";
 | 
			
		||||
 | 
			
		||||
    return timestamp.str();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user