debug:统一指令后调试通过。

This commit is contained in:
taynpg 2025-04-08 12:01:35 +08:00
parent f6e7596367
commit 3ded526713
8 changed files with 140 additions and 116 deletions

View File

@ -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`查看所有指令的具体介绍。
![tsc](https://www.sinxmiao.cn/taynpg/transm/raw/branch/main/img/tsc_use.png)
### 2.2 功能一简介
## 3.介绍补充
![func1](https://www.sinxmiao.cn/taynpg/transm/raw/branch/main/img/func1.png)
- 需要传入存储路径的指令,如果未填写,默认`tsc`当前工作目录。
- `任务文件`格式统一为`UTF-8`编码。
- 支持相对路径处理。
- `UpTask``DownTask`指令`任务文件`格式完全一致,均为左文件,右目录,尽管是下载别人文件。
### 2.3 功能二简介
![func2](https://www.sinxmiao.cn/taynpg/transm/raw/branch/main/img/func2.png)
### 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`构建工具。

View File

@ -57,7 +57,7 @@ void CClient::print_help(bool detail)
TLOGW("SupportCmd ==>");
if (!detail) {
TLOGI("Get|Who|Where|Ls|Sub|Fetch|Up|Down|UpTask|DownTask");
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;
@ -152,12 +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(); });
print_help(false);
fc_append('|');
get_id();
if (ip == "127.0.0.1") {
get_clients();
}
print_help(false);
fc_append('|');
while (1) {
char* readline = fc_readline();
@ -210,7 +211,11 @@ void CClient::run(const std::string& ip, const std::string& port, const std::str
param.erase(0, param.find_first_of(" ") + 1);
if (scmd == "UpTask" || scmd == "uptask" || scmd == "ut") {
cmd_uptask(param);
cmd_sub_task(param, true);
continue;
}
if (scmd == "DownTask" || scmd == "downtask" || scmd == "dt") {
cmd_sub_task(param, false);
continue;
}
if (scmd == "Up" || scmd == "up") {
@ -412,7 +417,7 @@ bool CClient::cmd_upload_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;
@ -544,7 +549,7 @@ void CClient::report_trans_ret(TransState state, const std::string& key)
*/
bool CClient::cmd_uptask(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) {
@ -564,20 +569,19 @@ bool CClient::cmd_uptask(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);
@ -589,6 +593,7 @@ bool CClient::cmd_uptask(const std::string& param)
in.close();
#if defined(_WIN32)
// 从文件中读取的内容还是要手动转码一下。
content = CCodec::u8_to_ansi(content);
#endif
@ -607,27 +612,17 @@ bool CClient::cmd_uptask(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;
}
if (is_send) {
TLOGI("--->check pass {}:{}", line, pr);
}
auto sec = v[1];
if (local_trans) {
sec = variable_handle(list_file_full, v[1], true);
sec = variable_handle(list_file_full, sec, local_trans);
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;
}
}
mre[line++] = pr + "|" + sec;
continue;
}
@ -640,7 +635,7 @@ bool CClient::cmd_uptask(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;
@ -648,7 +643,12 @@ bool CClient::cmd_uptask(const std::string& param)
list_file_ = list_file_full;
std::shared_ptr<CFrameBuffer> buf = std::make_shared<CFrameBuffer>();
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_);
@ -661,7 +661,7 @@ bool CClient::cmd_uptask(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;
@ -870,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_);
@ -1132,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) {
@ -1143,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;
@ -1314,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);
@ -1331,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();

View File

@ -55,8 +55,8 @@ public:
bool cmd_upload_files(const std::string& param);
bool down_one_file(int remote_id, const std::string& file, const std::string& local_dir = "");
void report_trans_ret(TransState state, const std::string& key = "");
bool cmd_uptask(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 cmd_ls(const std::string& param);
@ -66,6 +66,7 @@ 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();
@ -76,8 +77,8 @@ private:
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 578 KiB

View File

@ -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,