From 55b530d9f8c026221163590f01929fcef76789d9 Mon Sep 17 00:00:00 2001 From: taynpg Date: Tue, 17 Jun 2025 11:48:13 +0800 Subject: [PATCH] trans: 1.basic trans logic. 2.ico res. --- ClientCore/ClientCore.cpp | 7 +- ClientCore/ClientCore.h | 2 + ClientCore/FileTrans.cpp | 164 +++++++++++++++++++++++++++++++++++++- ClientCore/FileTrans.h | 42 +++++++++- Gui/CMakeLists.txt | 3 +- Gui/main.cpp | 3 +- Protocol/Protocol.h | 11 ++- Res/frelay.qrc | 6 ++ Res/ico.rc | 1 + Res/main.ico | Bin 0 -> 16958 bytes Res/original/a.svg | 1 + Res/original/b.svg | 1 + Res/server.ico | Bin 0 -> 16958 bytes Res/server.rc | 1 + Server/CMakeLists.txt | 4 +- Struct/InfoMsg.h | 7 +- 16 files changed, 236 insertions(+), 17 deletions(-) create mode 100644 Res/frelay.qrc create mode 100644 Res/ico.rc create mode 100644 Res/main.ico create mode 100644 Res/original/a.svg create mode 100644 Res/original/b.svg create mode 100644 Res/server.ico create mode 100644 Res/server.rc diff --git a/ClientCore/ClientCore.cpp b/ClientCore/ClientCore.cpp index a049421..9a1fd16 100644 --- a/ClientCore/ClientCore.cpp +++ b/ClientCore/ClientCore.cpp @@ -122,7 +122,12 @@ bool ClientCore::Send(const char* data, qint64 len) qCritical() << QString("client %1 not connected...").arg(remoteID_); return false; } - qint64 bytesWritten = socket_->write(data, len); + + qint64 bytesWritten = -1; + { + QMutexLocker locker(&sockMut_); + bytesWritten = socket_->write(data, len); + } if (bytesWritten == -1 || !socket_->waitForBytesWritten(5000)) { qCritical() << QString("Send data to server failed. %1").arg(socket_->errorString()); return false; diff --git a/ClientCore/ClientCore.h b/ClientCore/ClientCore.h index a6b068e..0689e85 100644 --- a/ClientCore/ClientCore.h +++ b/ClientCore/ClientCore.h @@ -56,6 +56,8 @@ public: QMutex conMutex_; QString ownID_; QString remoteID_; + + QMutex sockMut_; QTcpSocket* socket_; QByteArray recvBuffer_; diff --git a/ClientCore/FileTrans.cpp b/ClientCore/FileTrans.cpp index 45c7d4b..6b84142 100644 --- a/ClientCore/FileTrans.cpp +++ b/ClientCore/FileTrans.cpp @@ -2,18 +2,180 @@ FileTrans::FileTrans(ClientCore* clientCore) : clientCore_(clientCore) { + RegisterFrameCall(); } void FileTrans::SetTasks(const QVector& tasks) { - tasks_ = tasks; + localTasks_ = tasks; } void FileTrans::RegisterFrameCall() { clientCore_->SetFrameCall(FBT_CLI_REQ_SEND, [this](QSharedPointer frame) { fbtReqSend(frame); }); + clientCore_->SetFrameCall(FBT_CLI_REQ_RECV, [this](QSharedPointer frame) { fbtReqRecv(frame); }); + clientCore_->SetFrameCall(FBT_CLI_TRANS_DONE, [this](QSharedPointer frame) { fbtTransDone(frame); }); + clientCore_->SetFrameCall(FBT_CLI_ANSSEND_SUCCESS, [this](QSharedPointer frame) { fbtAnsSendSuccess(frame); }); + clientCore_->SetFrameCall(FBT_CLI_ANSSEND_FAILED, [this](QSharedPointer frame) { fbtAnsSendFailed(frame); }); + clientCore_->SetFrameCall(FBT_CLI_ANSRECV_FAILED, [this](QSharedPointer frame) { fbtAnsRecvFailed(frame); }); + clientCore_->SetFrameCall(FBT_CLI_ANSRECV_SUCCESS, [this](QSharedPointer frame) { fbtAnsRecvSuccess(frame); }); + clientCore_->SetFrameCall(FBT_CLI_FILETRANS, [this](QSharedPointer frame) { fbtFileTrans(frame); }); + clientCore_->SetFrameCall(FBT_CLI_FILETRANS_FAILED, [this](QSharedPointer frame) { fbtFileTransFailed(frame); }); } void FileTrans::fbtReqSend(QSharedPointer frame) { + // judget is same client's same file. + + // send + InfoMsg info = infoUnpack(frame->data); + auto doTask = QSharedPointer::create(); + doTask->file.setFileName(info.path); + if (!doTask->file.open(QIODevice::ReadOnly)) { + qCritical() << QString(tr("open file failed: %1")).arg(info.path); + return; + } + doTask->task.isUpload = true; + doTask->task.localPath = info.path; + doTask->task.remoteId = frame->fid; + SendFile(doTask); } + +void FileTrans::fbtReqRecv(QSharedPointer frame) +{ + // recv is single thread recv. + + // judge idle + + // reply msg + + // recv + InfoMsg info = infoUnpack(frame->data); + downTask_.file.setFileName(info.path); + if (!downTask_.file.open(QIODevice::WriteOnly)) { + info.msg = QString(tr("open file failed: %1")).arg(info.path); + qCritical() << info.msg; + if (!clientCore_->Send(info, FBT_CLI_ANSRECV_FAILED, frame->fid)) { + qCritical() << QString(tr("open recv file:%2 failed, and reply %2 failed.")).arg(info.msg).arg(frame->fid); + downTask_.file.close(); + return; + } + return; + } + info.msg = QString(tr("open recv file success: %1")).arg(info.path); + if (!clientCore_->Send(info, FBT_CLI_ANSRECV_SUCCESS, frame->fid)) { + qCritical() << QString(tr("open recv file:%2 success, but reply %2 failed.")).arg(info.msg).arg(frame->fid); + downTask_.file.close(); + return; + } + downTask_.state = TaskState::STATE_RUNNING; +} + +void FileTrans::fbtTransDone(QSharedPointer frame) +{ + auto info = infoUnpack(frame->data); + if (downTask_.file.isOpen()) { + downTask_.file.close(); + downTask_.state = TaskState::STATE_FINISH; + qInfo() << QString(tr("recv file:%1 success.")).arg(downTask_.file.fileName()); + clientCore_->Send(info, FBT_CLI_ANSRECV_SUCCESS, frame->fid); + return; + } + qCritical() << QString(tr("recv file:%1 done sigal, but file not opened.")).arg(info.msg); +} + +void FileTrans::fbtAnsRecvSuccess(QSharedPointer frame) +{ + // ready to send + InfoMsg info = infoUnpack(frame->data); + auto doTask = QSharedPointer::create(); + doTask->file.setFileName(info.path); + if (!doTask->file.open(QIODevice::ReadOnly)) { + qCritical() << QString(tr("open file failed: %1")).arg(info.path); + return; + } + doTask->task.isUpload = true; + doTask->task.localPath = info.path; + doTask->task.remoteId = frame->fid; + SendFile(doTask); +} + +void FileTrans::fbtAnsRecvFailed(QSharedPointer frame) +{ + InfoMsg info = infoUnpack(frame->data); + qCritical() << QString(tr("request send file:%1 failed. reason:%2")).arg(info.path).arg(info.msg); +} + +void FileTrans::fbtFileTrans(QSharedPointer frame) +{ + if (downTask_.state != TaskState::STATE_RUNNING) { + return; + } + // For the sake of efficiency, not verify the legality of the file + auto ws = downTask_.file.write(frame->data.constData(), frame->data.size()); + if (ws != frame->data.size()) { + downTask_.state = TaskState::STATE_FAILED; + InfoMsg info; + info.msg = downTask_.file.errorString(); + clientCore_->Send(info, FBT_CLI_FILETRANS_FAILED, frame->fid); + } +} + +void FileTrans::fbtAnsSendFailed(QSharedPointer frame) +{ +} + +void FileTrans::fbtAnsSendSuccess(QSharedPointer frame) +{ +} + +void FileTrans::fbtFileTransFailed(QSharedPointer frame) +{ +} + +void FileTrans::SendFile(const QSharedPointer& task) +{ + auto* sendThread = new SendThread(clientCore_); + sendThread->setTask(task); + QMutexLocker locker(&sthMut_); + sendThreads_.push_back(sendThread); + sendThread->run(); +} + +SendThread::SendThread(ClientCore* clientCore) : cliCore_(clientCore) +{ +} + +void SendThread::run() +{ + // task's file shoule be already opened. + auto frame = QSharedPointer::create(); + frame->tid = task_->task.remoteId; + frame->type = FBT_CLI_FILETRANS; + + bool suc = true; + while (!task_->file.atEnd()) { + frame->data.resize(CHUNK_BUF_SIZE); + auto br = task_->file.read(frame->data.data(), CHUNK_BUF_SIZE); + if (br == -1) { + qCritical() << QString(tr("read file failed: %1")).arg(task_->file.errorString()); + suc = false; + break; + } + frame->data.resize(br); + if (!cliCore_->Send(frame)) { + qCritical() << QString(tr("send to %1 file failed.")).arg(task_->task.remoteId); + suc = false; + break; + } + } + + if (!suc) { + task_->file.close(); + } +} + +void SendThread::setTask(const QSharedPointer& task) +{ + task_ = task; +} \ No newline at end of file diff --git a/ClientCore/FileTrans.h b/ClientCore/FileTrans.h index cd424b5..1b15620 100644 --- a/ClientCore/FileTrans.h +++ b/ClientCore/FileTrans.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "ClientCore.h" @@ -18,7 +19,7 @@ struct TransTask { }; enum class TaskState { - STATE_READY = 0, + STATE_NONE = 0, STATE_RUNNING, STATE_FAILED, STATE_FINISH, @@ -26,10 +27,25 @@ enum class TaskState { struct DoTransTask { QFile file; - TaskState state; + TaskState state = TaskState::STATE_NONE; TransTask task; }; +class SendThread : public QThread +{ + Q_OBJECT +public: + SendThread(ClientCore* clientCore); + +public: + void run() override; + void setTask(const QSharedPointer& task); + +private: + ClientCore* cliCore_; + QSharedPointer task_; +}; + class FileTrans : public QObject { Q_OBJECT @@ -42,12 +58,30 @@ public: private: void fbtReqSend(QSharedPointer frame); + void fbtReqRecv(QSharedPointer frame); + void fbtTransDone(QSharedPointer frame); + void fbtAnsRecvSuccess(QSharedPointer frame); + void fbtAnsRecvFailed(QSharedPointer frame); + void fbtFileTrans(QSharedPointer frame); + void fbtAnsSendFailed(QSharedPointer frame); + void fbtAnsSendSuccess(QSharedPointer frame); + void fbtFileTransFailed(QSharedPointer frame); + +private: + void SendFile(const QSharedPointer& task); private: DoTransTask downTask_; - QVector tasks_; + + QMutex lMut_; + QMutex rMut_; + QVector localTasks_; + QVector remoteTasks_; + ClientCore* clientCore_; - QMap upTasks_; + QMutex sthMut_; + QVector sendThreads_; + QMap upTasks_; }; #endif \ No newline at end of file diff --git a/Gui/CMakeLists.txt b/Gui/CMakeLists.txt index bb35770..39b9fe9 100644 --- a/Gui/CMakeLists.txt +++ b/Gui/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.16) project(frelayGUI VERSION ${PROJECT_VERSION} LANGUAGES CXX) @@ -22,6 +22,7 @@ Control/ConnectControl.h Control/ConnectControl.cpp Control/ConnectControl.ui Control/CompareControl.h Control/CompareControl.cpp Control/CompareControl.ui GuiUtil/Public.h GuiUtil/Public.cpp Control/Transform.h Control/Transform.cpp Control/Transform.ui +../Res/frelay.qrc ../Res/ico.rc ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) diff --git a/Gui/main.cpp b/Gui/main.cpp index 2931e4f..22fcbd5 100644 --- a/Gui/main.cpp +++ b/Gui/main.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "frelayGUI.h" @@ -12,6 +12,7 @@ int main(int argc, char* argv[]) #ifdef _WIN32 QFont font("Microsoft YaHei", 9); a.setFont(font); + a.setWindowIcon(QIcon(":/ico/main.ico")); a.setStyle("Windows"); #endif diff --git a/Protocol/Protocol.h b/Protocol/Protocol.h index cb5ea4c..abcf276 100644 --- a/Protocol/Protocol.h +++ b/Protocol/Protocol.h @@ -1,10 +1,12 @@ -#ifndef PROTOCOL_H +#ifndef PROTOCOL_H #define PROTOCOL_H #include #include #include +constexpr quint32 CHUNK_BUF_SIZE = 1 * 1024 * 1024; + // It is specified here that the first 30 contents (inclusive) are // used for communication with the server. // Contents beyond 30 are only forwarded. @@ -20,13 +22,14 @@ enum FrameBufferType : uint16_t { FBT_CLI_ASK_HOME, FBT_CLI_ANS_HOME, FBT_CLI_REQ_SEND, - FBT_CLI_ANSREQ_SUCCESS, - FBT_CLI_ANSREQ_FAILED, + FBT_CLI_ANSSEND_SUCCESS, + FBT_CLI_ANSSEND_FAILED, FBT_CLI_REQ_RECV, FBT_CLI_ANSRECV_SUCCESS, FBT_CLI_ANSRECV_FAILED, FBT_CLI_FILETRANS, - FBT_CLI_TRANS_DONE + FBT_CLI_TRANS_DONE, + FBT_CLI_FILETRANS_FAILED }; struct FrameBuffer { diff --git a/Res/frelay.qrc b/Res/frelay.qrc new file mode 100644 index 0000000..727aff0 --- /dev/null +++ b/Res/frelay.qrc @@ -0,0 +1,6 @@ + + + main.ico + server.ico + + diff --git a/Res/ico.rc b/Res/ico.rc new file mode 100644 index 0000000..e42514b --- /dev/null +++ b/Res/ico.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "main.ico" diff --git a/Res/main.ico b/Res/main.ico new file mode 100644 index 0000000000000000000000000000000000000000..f470d9a99611cbe9e4729b63355ea0202ecf927d GIT binary patch literal 16958 zcmeHP`BPNc74G~K`8iejK_HA}QbC%fp+yt~MRsr?ilZYtBB)7KCK;;+6BuBqWLyD3 z1Yss4i<((ckS&akjB{&LQP*ECHx($biw;Zld!{rdHL_dEAH=bn4c z1&QPr^dpx`==ZNA7A_LWFC`L*1qq#!93kQJLX%4*zg{~fQzs-6thQAR7)t{x6`)iC zd|UxdIX;d@w{N56!Uc3yRAT(uGhlQSs*w?>#>RljNi)@g3S)hJsQV-X$DBNnWU~Y5 zQcrx9co-wSy#V8X`Ep8Ly)rL85Wff8e!}Irc>LYQ7g1Zv5gIUzyjhZbk48U`H^7<3U*}{=GjQ%ID8#uutm%6BEnY z78EpwjfI6c<>G-q%960tzfX-_#Gl}N43}bJp%@yPgP(nZ;2W2r7DfjLQI(uRHogV> zUC-m)pjX0}Gj36s2Lk@67)+9HP|$bg2B z4vmBMvo6NQ;aYMs9yTu}CD5Py-zVz*EGEWT@E7hkhKNN~x*0r@GD zlmzD!_`o&}r(Cz8z4Dq~OPnMQ-9BH4<8luia!SP?0tOMR!F3U@t|QxlDN6|1?m?$C z6o%}=NPqtv9gL9eRwNxonyn{dH(wxIpU}y7UD%hyIUbw$h^PN>+K%3vH)r90+SZ2R zeG&MG;+cKhZs@i3<$!a(K8E?;N4kSAI%{eK{3?oR?os@jyUiDWwo62qZ_jIOeZKpf z`ey=u#~J+1C1vNxfFPZR%8&wb>0`6J#Xm-wzQD_dU({_gsE zWH@>)XnxaozvXs*%Km-ONwIw2&-ZZJ%^T5fm(1GcHOEMepM0K*V#4o_pG4|<51Q|a z&2GNa#4Wy7f-2M#bw@l8$e_Mc#Hl zig)A4A9R?lJoDprCa? z!?}E%+~9%3vcFNjqyM$`5a#!g@?qa0%KaPD6t}N%NcJ%@?A+0qUjWWYsW@M&gM5RzcCjfL8G&DAmHcxUvvzVysl<{YJwt+G2`btjo2s#5d7eL zIv^|x!qrmr88Tfx@h8U*$tOHf+XLfIbj}cX!uWM@>8+m|7=TJ!cs#i#q1`B7_&Obk zT>vw{ZU!qHDgEIj?WrHL+{HW*@=x193*DF0}gc zHn6jfZ|CHpDkTlA#bv@6@L1>@C+@dV>ad!+nD2Ru<*AVYv$;H$A`wx=#6zj^QTTu=;{BQz8QEOk8y2U2F^?0!QgHSC?eP3L8v7@ zvtNtCpir<+p2GvGJuYcuf62CCAeJ||UPX60nN1<8^zo-X3{uF41TVEi+qBe6qPCI zf<5cfLHGAJkmDr7vt1Um{EB$5<$ml4wnCotw`AM<(RZ(XjtV+z>X0k5#{kW#JfAFg zyxRR|OwV>%qD1x%@+kk&QBzA{@XX$aDf|v*i=XwuIv9E10+pM|9^@{#RZs}t~WKb;M(d#@DyqFpc-ntUvgP2OI&oeraeAB`1xw3 z;QMCqoALZleDB&}iG1lgd_n7{r>$+mxQoB;|8NUuoSney260_2`pV^eWLuw^?`%oDAnygN++>9bT1T65?9Y8$Q2bBValBK(@y>>g`0;#^I^NO7&+qS5R^iMh zHOHOTn0Pv$4%pT?-Yw>Mm-g=R9$w9OR~LT%oc9D@O-@B=%mI{8 z{$No;YmE}JHKWSjNStumgjx^%>wmV__MqwY4&};t@3ujW^q{0#n9jZ9lp|U2dCIv8 zd*2^FhQdfPN%^JHh!~yuALaww?oBT%TJPGSGW8Q$?@~VQ|JgV<%76PoKF1iMc=7~g zk#Rcnf1L05j`JNGH=}`aIf|vodB2G+iBCd)nCqd7)Ps`h)|X-rpo09gI8U*yvp@Kk z!&(#sg`nrg4~Exx-UOGS-{Be(@3UEi9zZ$v8z)ZVEY&`GLo6{${=LlM!o@*lN4-2 z+yYPH65CYLztC b2uFU;=lS#1wyJ?8(17@V09KDHQv?44A5s#` literal 0 HcmV?d00001 diff --git a/Res/original/a.svg b/Res/original/a.svg new file mode 100644 index 0000000..49f4e61 --- /dev/null +++ b/Res/original/a.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Res/original/b.svg b/Res/original/b.svg new file mode 100644 index 0000000..74ea3ad --- /dev/null +++ b/Res/original/b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Res/server.ico b/Res/server.ico new file mode 100644 index 0000000000000000000000000000000000000000..ea2020c552fe1845e7ffe5b5bc56eb1a2d7b919f GIT binary patch literal 16958 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 @@ -10,15 +10,16 @@ struct InfoMsg { qint32 mark{}; QString msg; + QString path; void serialize(QDataStream& data) const { - data << mark << msg; + data << mark << msg << path; } void deserialize(QDataStream& data) { - data >> mark >> msg; + data >> mark >> msg >> path; } };