├── TFTP_Client ├── icon.ico ├── images │ ├── clear.png │ ├── icon.png │ ├── save.png │ ├── download.png │ ├── openFile.png │ ├── upload.png │ └── images.qrc ├── main.cpp ├── util.h ├── TFTP_Client.pro ├── mainwindow.h ├── workthread.h ├── def.h ├── util.cpp ├── mainwindow.cpp ├── mainwindow.ui ├── workthread.cpp └── TFTP_Client.pro.user └── Readme.md /TFTP_Client/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/icon.ico -------------------------------------------------------------------------------- /TFTP_Client/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/images/clear.png -------------------------------------------------------------------------------- /TFTP_Client/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/images/icon.png -------------------------------------------------------------------------------- /TFTP_Client/images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/images/save.png -------------------------------------------------------------------------------- /TFTP_Client/images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/images/download.png -------------------------------------------------------------------------------- /TFTP_Client/images/openFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/images/openFile.png -------------------------------------------------------------------------------- /TFTP_Client/images/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BIIIANG/HUST-CSE-ComputerTelecommunicationsAndNetwork-TFTP-Client/HEAD/TFTP_Client/images/upload.png -------------------------------------------------------------------------------- /TFTP_Client/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWindow w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /TFTP_Client/images/images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | clear.png 4 | download.png 5 | openFile.png 6 | save.png 7 | upload.png 8 | icon.png 9 | 10 | 11 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## 项目环境 2 | 3 | - Qt Creator 5.0.0 Based on Qt 5.15.2 (MSVC 2019, 64 bit) 4 | - Kit:QT 6.0.3 MSVC2019 64bit 5 | 6 | ## 支持 7 | 8 | - RFC1350 9 | - RFC2347中的扩展选项(TFTP Option Extension) 10 | - RFC2348中的块大小可选项(TFTP Blocksize Option) 11 | - RFC2349中的超时时间可选项和文件大小可选项(TFTP Timeout Interval and Transfer Size Options) -------------------------------------------------------------------------------- /TFTP_Client/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include "def.h" 5 | 6 | #define LF '\n' 7 | #define CR '\r' 8 | #define NUL '\0' 9 | #define PLATFORM_WINDOWS 1 10 | #define PLATFORM_LINUX 2 11 | 12 | #define FULLSTRLEN(str) (strlen(str) + 1) 13 | #define FULLNUMLEN(num) (numlen(num) + 1) 14 | 15 | int appendMsg(char* buf, rsize_t bufferCount, int* offset, const char* format, ...); 16 | int getMsg(char* buf, rsize_t bufferCount, const char* source, int* offset); 17 | int getFileSize(const char* fileName); 18 | int numlen(int num); 19 | 20 | int encodeNetascii(const char *fileName); 21 | int decodeNetascii(const char *fileName, int platform); 22 | 23 | #endif // UTIL_H 24 | -------------------------------------------------------------------------------- /TFTP_Client/TFTP_Client.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 2 | RC_ICONS = icon.ico 3 | 4 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets \ 5 | network \ 6 | core5compat 7 | 8 | CONFIG += c++11 9 | 10 | # You can make your code fail to compile if it uses deprecated APIs. 11 | # In order to do so, uncomment the following line. 12 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 13 | 14 | SOURCES += \ 15 | main.cpp \ 16 | mainwindow.cpp \ 17 | util.cpp \ 18 | workthread.cpp 19 | 20 | HEADERS += \ 21 | def.h \ 22 | mainwindow.h \ 23 | util.h \ 24 | workthread.h 25 | 26 | FORMS += \ 27 | mainwindow.ui 28 | 29 | # 添加库文件 30 | LIBS += -L"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64" \ 31 | -lws2_32 32 | 33 | # Default rules for deployment. 34 | qnx: target.path = /tmp/$${TARGET}/bin 35 | else: unix:!android: target.path = /opt/$${TARGET}/bin 36 | !isEmpty(target.path): INSTALLS += target 37 | 38 | RESOURCES += \ 39 | images/images.qrc 40 | 41 | DISTFILES += \ 42 | images/icon.png 43 | -------------------------------------------------------------------------------- /TFTP_Client/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "def.h" 12 | #include "util.h" 13 | #include "workthread.h" 14 | 15 | QT_BEGIN_NAMESPACE 16 | namespace Ui { class MainWindow; } 17 | QT_END_NAMESPACE 18 | 19 | class MainWindow : public QMainWindow 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | MainWindow(QWidget *parent = nullptr); 25 | ~MainWindow(); 26 | 27 | signals: 28 | void sendReadFileArgs(SOCKET sock, SOCKADDR_IN servAddr, const char* reFile, const char* loFile, int mode, EXTENDED ext, int oper); 29 | 30 | public slots: 31 | 32 | private: 33 | Ui::MainWindow *ui; 34 | 35 | EXTENDED extended = { 0 }; 36 | 37 | unsigned short blkSize = TFTP_DEFAULT_BLOCK_SIZE; 38 | 39 | int serverPort, mode = MODE_OCTET; 40 | SOCKET socketFd = INVALID_SOCKET; 41 | SOCKADDR_IN serverAddr = { 0 }; 42 | int serverAddrLen = sizeof(serverAddr); 43 | 44 | const char *remoteFile, *localFile, *serverIP; 45 | std::string remoteFileStd, localFileStd, serverIPStd, remoteFileGB, localFileGB; 46 | QByteArray remoteFileByteArray, localFileByteArray; 47 | 48 | WorkThread *workThread = new WorkThread(); 49 | 50 | int checkArgs(int op); 51 | 52 | void setModuleEnabled(bool isEnabled); 53 | 54 | QTimer *timer = new QTimer(); 55 | 56 | }; 57 | #endif // MAINWINDOW_H 58 | -------------------------------------------------------------------------------- /TFTP_Client/workthread.h: -------------------------------------------------------------------------------- 1 | #ifndef WORDTHREAD_H 2 | #define WORDTHREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "def.h" 10 | #include "util.h" 11 | 12 | class WorkThread : public QThread 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit WorkThread(QObject *parent = nullptr); 18 | 19 | signals: 20 | void sendMsg(int code, QString msg); 21 | void sendProcessBarMaximum(int max); 22 | void sendProcessBarValue(int val); 23 | void sendTSize(QString tsize); 24 | void sendUploadSpeed(QString speed); 25 | void sendDownloadSpeed(QString speed); 26 | 27 | protected: 28 | void run() override; 29 | 30 | public slots: 31 | void recvReadFileArgs(SOCKET sock, SOCKADDR_IN servAddr, const char* reFile, const char* loFile, int mode, EXTENDED ext, int oper); 32 | void recvTimeOutSignal(); 33 | 34 | private: 35 | 36 | FILE* fp = NULL; 37 | 38 | // Packet need to use. 需要用到的Packet 39 | PKG_DATA_ERROR_OACK rcvdPkt = { 0 }, dataPkt = { htons(OP_DATA), { 0 } }; 40 | PKG_ACK ackPkt = { htons(OP_ACK), 0 }; 41 | PKG_REQUEST reqPkt = { 0 , { 0 } }; 42 | 43 | // Task arguments. 任务参数 44 | int operation; 45 | SOCKET socketFd = INVALID_SOCKET; 46 | SOCKADDR_IN serverAddr = { 0 }; 47 | const char *remoteFile = NULL, *localFile = NULL; 48 | int serverAddrLen = sizeof(serverAddr), mode = 0; 49 | EXTENDED extended = { 0 }; 50 | 51 | // Extended settings used. 实际使用的扩展参数 52 | int tSize; 53 | TIMEVAL tvTimeOut; 54 | unsigned short blkSize, timeOut; 55 | 56 | // Statistical information. 统计信息 57 | int reqMsgLen; 58 | double percent = 0; 59 | int bytesRecv, bytesSend, lastBytesRecv, lastBytesSend; 60 | int totalRetransmitCount; 61 | int fileSize; 62 | WORD curBlock; 63 | clock_t startTime, endTime; 64 | 65 | int checkArgs(); 66 | int openLocalFile(); 67 | int sendReqest(); 68 | 69 | int recvFile(); 70 | int sendFile(); 71 | 72 | int sendAck(WORD block); 73 | int sendPkt(const char* buf, int len); 74 | int recvPkt(char* buf, int len); 75 | int getOptionFromOAckPkt(int rcvdSize); 76 | 77 | int waitForPkt(int timeOutMs, int &rcvdSize); 78 | int waitForOACK(int timeOutMs, int &rcvdSize); 79 | int waitForSpecificPkt(WORD block, int timeOutMs, int &rcvdSize, int pktType); 80 | 81 | void tftpTerminate(bool isSuccess); 82 | 83 | QTimer *timer; 84 | 85 | }; 86 | 87 | #endif // WORDTHREAD_H 88 | -------------------------------------------------------------------------------- /TFTP_Client/def.h: -------------------------------------------------------------------------------- 1 | #ifndef DEF_H 2 | #define DEF_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #define SERV_PORT 69 13 | 14 | #define OP_READ_REQ 1 15 | #define OP_WRITE_REQ 2 16 | #define OP_DATA 3 17 | #define OP_ACK 4 18 | #define OP_ERROR 5 19 | #define OP_OACK 6 20 | 21 | #define TFTP_DATA_PKT 3 22 | #define TFTP_ACK_PKT 4 23 | #define TFTP_ERROR_PKT 5 24 | #define TFTP_OACK_PKT 6 25 | 26 | #define MODE_NETASCII 1 27 | #define MODE_OCTET 2 28 | 29 | #define OP_SIZE 2 30 | #define MODE_SIZE 2 31 | #define BLOCK_SIZE 2 32 | #define ERRCODE_SIZE 2 33 | #define REQ_SIZE 512 34 | #define ERROR_SIZE 512 35 | #define TFTP_DEFAULT_BLOCK_SIZE 512 36 | #define TFTP_MIN_BLOCK_SIZE 8 37 | //#define TFTP_MAX_BLOCK_SIZE 65464 // RFC 2349 38 | #define TFTP_MAX_BLOCK_SIZE 16384 // Tftpd64 39 | 40 | #define RECV_LOOP_MAX 100000 41 | #define TFTP_MAX_RETRANSMIT 10 42 | #define RECV_TIMEOUT_SEC 0 43 | #define RECV_TIMEOUT_USEC 1000000 44 | #define TFTP_DEFAULT_TIMEOUT_SEC 2 45 | #define TFTP_MIN_TIMEOUT_SEC 1 46 | #define TFTP_MAX_TIMEOUT_SEC 255 47 | 48 | #define TFTP_NO_ERROR 0 49 | #define ERROR_INVALID_ARG -1 50 | #define ERROR_TOO_LONG_REQUEST -11 51 | #define ERROR_UNDEFINED_MODE -12 52 | #define ERROR_OPENFILE_FAIL -13 53 | #define ERROR_SEND_REQ_FAIL -14 54 | #define ERROR_SOCKET_ERROR -15 55 | #define ERROR_CONNECT_CLOSE -16 56 | #define ERROR_WRONG_PKT -17 57 | #define ERROR_SEND_ACK_FAIL -18 58 | #define ERROR_FWRITE_FAIL -19 59 | #define ERROR_UNEXPECTED_PKT -20 60 | #define ERROR_RETRANSMIT_EQU -21 61 | #define ERROR_RETRANSMIT_ACK -22 62 | #define ERROR_RETRANSMIT_TOO_MUCH -23 63 | #define TFTP_ERROR_TIMEOUT -24 64 | #define ERROR_SEND_DATA_FAIL -25 65 | #define ERROR_SELECT_SOCKET_ERROR -41 66 | #define ERROR_SELECT_TIMEOUT -42 67 | #define ERROR_SELECT_CONNECT_CLOSE -43 68 | #define TFTP_ERROR_WRONG_PKT -61 69 | #define TFTP_NOT_SUPPORT_EXT -62 70 | 71 | #define TFTP_REFRESH_INTERVAL 1000 // 吞吐量刷新间隔(ms) 72 | 73 | typedef struct { 74 | WORD op; 75 | char reqMsg[REQ_SIZE]; 76 | }PKG_REQUEST; 77 | 78 | typedef struct { 79 | WORD op; 80 | union { 81 | WORD block; 82 | WORD errCode; 83 | char opMsg[2]; 84 | }; 85 | union { 86 | char data[TFTP_MAX_BLOCK_SIZE]; 87 | char errMsg[ERROR_SIZE]; 88 | }; 89 | }PKG_DATA_ERROR_OACK; 90 | 91 | typedef struct { 92 | WORD op; 93 | WORD block; 94 | }PKG_ACK; 95 | 96 | typedef struct { 97 | bool isExtended; 98 | bool tsizeExist, blksizeExist, timeoutExist; 99 | int tsize, blksize, timeout; 100 | }EXTENDED; 101 | 102 | #endif // DEF_H 103 | 104 | 105 | -------------------------------------------------------------------------------- /TFTP_Client/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | /** 4 | * RECEIVE ARGUMENTS LIKE sprintf TO APPEND MESSAGE TO THE BUF AND ALTER THE OFFSET 5 | * 接收可变参数,将字符串附加在BUF的指定位置并维护当前写到位置的偏移OFFSET 6 | * @buf : pointer to string buffer 字符串缓冲区首指针 7 | * @bufferCount : max length of the buf 字符串缓冲区最大长度 8 | * @offset : current position of the string 当前位置 9 | * @format,... : format like sprintf 类似sprintf函数的可变格式参数 10 | * #return : length of the message 信息长度 11 | */ 12 | int appendMsg(char* buf, rsize_t bufferCount, int* offset, const char* format, ...) 13 | { 14 | va_list args; 15 | va_start(args, format); 16 | int ret = vsprintf_s(buf + *offset, bufferCount - *offset, format, args); 17 | va_end(args); 18 | *offset += ret + 1; 19 | return ret; 20 | } 21 | 22 | /** 23 | * READ MESSAGE FORM SOURCE DATA TO BUF AND ALTER THE OFFSET TO GET THE NEXT MESSAGE 24 | * 从SOURCE数据中读取一条信息到BUF中并维护下一条信息的开始偏移OFFSET 25 | * @buf : pointer to string buffer 字符串缓冲区首指针 26 | * @bufferCount : max length of the buf 字符串缓冲区最大长度 27 | * @source : source data 源数据 28 | * @offset : position of the current message 当前信息的位置 29 | * #return : length of the message 信息长度 30 | */ 31 | int getMsg(char* buf, rsize_t bufferCount, char const* source, int* offset) 32 | { 33 | int ret = sprintf_s(buf, bufferCount, "%s", source + *offset); 34 | *offset += ret + 1; 35 | return ret; 36 | } 37 | 38 | /** 39 | * GET THE BYTE SIZE OF A FILE 获得一个文件的字节长度 40 | * @fileName : name of the file 文件名 41 | * #return : the byte size of the file 该文件的字节长度 42 | */ 43 | int getFileSize(const char* fileName) 44 | { 45 | struct stat statBuf; 46 | if (stat(fileName, &statBuf) != 0) { return -1; } 47 | return statBuf.st_size; 48 | } 49 | 50 | /** 51 | * GET THE LENGTH OF A DECIMAL NUMBER 获得一个十进制数的长度 52 | * @num : the decimal number 十进制数 53 | * #return : the length 长度 54 | */ 55 | int numlen(int num) 56 | { 57 | if (num == 0) { return 1; } 58 | int count = 0; 59 | for (; num != 0; num /= 10) { count++; } 60 | return count; 61 | } 62 | 63 | /** 64 | * ENCODE FILE TO NETASCII 将本地编码的文件转换为NETASCII编码文件 65 | * LF(0x0A) => CRLF(0x0D,0x0A) 66 | * CR(0x0D) => CRNUL(0x0D,0x00) 67 | * EXAMPLE.FILE => EXAMPLE.FILE.netascii 68 | * @fileName : name of the file to be encoded 需要进行编码的文件名 69 | * #return : status code (-1 for fail, 0 for succeed) 状态码 70 | */ 71 | int encodeNetascii(const char *fileName) { 72 | FILE *fp = NULL, *tfp = NULL; 73 | char tempFileName[1024]; 74 | strcpy_s(tempFileName, 1023, fileName); 75 | strcat_s(tempFileName, 1023, ".netascii"); 76 | fopen_s(&fp, fileName, "rb"); 77 | fopen_s(&tfp, tempFileName, "wb"); 78 | if (fp == NULL || tfp == NULL) { 79 | if (fp) { fclose(fp); } 80 | if (fp) { fclose(tfp); } 81 | return -1; 82 | } 83 | 84 | int ch = fgetc(fp), lch = NUL; 85 | while (true) { 86 | if (ch == LF && lch != CR) { fputc(CR, tfp); } // LF => CRLF 87 | else if (lch == CR && ch != LF && ch != NUL) { fputc(NUL, tfp); } // CR => CRNUL 88 | else if (ch == EOF) { break; } 89 | fputc(ch, tfp); 90 | lch = ch; 91 | ch = fgetc(fp); 92 | } 93 | 94 | fclose(fp); 95 | fclose(tfp); 96 | return 0; 97 | } 98 | 99 | /** 100 | * DECODE NETASCII TO LOCAL FORMAT 将NETASCII编码的文件转换为本地编码 101 | * ON WINDOWS : CRLF => CRLF CRNUL => CR 102 | * ON LINUX : CRLF => LF CRNUL => CR 103 | * EXAMPLE.FILE => EXAMPLE.FILE.local 104 | * @fileName : name of the file to be decoded 需要进行解码的文件名 105 | * #return : status code (-1 for fail, 0 for succeed) 状态码 106 | */ 107 | int decodeNetascii(const char *fileName, int platform) { 108 | FILE *fp = NULL, *tfp = NULL; 109 | char tempFileName[1024]; 110 | strcpy_s(tempFileName, 1023, fileName); 111 | strcat_s(tempFileName, 1023, ".local"); 112 | fopen_s(&fp, fileName, "rb"); 113 | fopen_s(&tfp, tempFileName, "wb"); 114 | if (fp == NULL || tfp == NULL) { 115 | if (fp) { fclose(fp); } 116 | if (fp) { fclose(tfp); } 117 | return -1; 118 | } 119 | 120 | int ch = 0, nch = 0; 121 | while (true) { 122 | ch = fgetc(fp); 123 | if (ch == CR) { 124 | nch = fgetc(fp); 125 | if ( platform != PLATFORM_WINDOWS && nch == LF) { ch = LF; } 126 | else if (nch == NUL) { ch = CR; } 127 | else { fseek(fp, -1L, SEEK_CUR); } 128 | } else if (ch == EOF) { break; } 129 | fputc(ch, tfp); 130 | } 131 | 132 | fclose(fp); 133 | fclose(tfp); 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /TFTP_Client/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | MainWindow::MainWindow(QWidget *parent) 5 | : QMainWindow(parent) 6 | , ui(new Ui::MainWindow) 7 | { 8 | ui->setupUi(this); 9 | 10 | // init modules 初始化UI组件 11 | ui->ProgressBar->setRange(0,1); 12 | ui->ProgressBar->setValue(0); 13 | ui->ModeComboBox->addItem("OCTET"); 14 | ui->ModeComboBox->addItem("NETASCII"); 15 | ui->BreakBtn->setEnabled(false); // waiting to be done 待完成 16 | 17 | // for debug 测试用 18 | // ui->LocalFileLineEdit->setText("H:\\测试用例1.jpg"); 19 | // ui->RemoteFileLineEdit->setText("测试用例1.jpg"); 20 | 21 | #ifdef WIN32 22 | // init WinSock 初始化WinSock 23 | WSADATA wsaData; 24 | int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 25 | if (iResult != 0) { QMessageBox::warning(this, "Init WinSock Fail.", "Init socket fail."); exit(-1); } 26 | if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); exit(-1); } 27 | #endif 28 | 29 | // select local file 选择本地文件 30 | connect(ui->dirBtn,&QPushButton::clicked, this, [=]() { 31 | QString desktopPath = QProcessEnvironment::systemEnvironment().value("USERPROFILE") + "\\desktop"; 32 | QString localFilePath = QFileDialog::getSaveFileName(this, "请选择本地文件", desktopPath); 33 | if(!localFilePath.isEmpty()) { ui->LocalFileLineEdit->setText(localFilePath); } 34 | }); 35 | // receive file 接收文件 36 | connect(ui->GetBtn, &QPushButton::clicked, this, [=]() { 37 | if (checkArgs(OP_READ_REQ) == TFTP_NO_ERROR) { 38 | timer->start(TFTP_REFRESH_INTERVAL); 39 | setModuleEnabled(false); 40 | serverAddr.sin_family = AF_INET; 41 | serverAddr.sin_port = htons(SERV_PORT); 42 | InetPtonA(AF_INET, serverIP, (void*)&serverAddr.sin_addr); 43 | socketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 44 | if (socketFd == INVALID_SOCKET) { QMessageBox::warning(this, "Init Socket Fail.", "Init socket fail."); exit(-1); } 45 | emit sendReadFileArgs(socketFd, serverAddr, remoteFile, localFile, mode, extended, OP_READ_REQ); 46 | workThread->start(); 47 | } 48 | }); 49 | // send file 发送文件 50 | connect(ui->PutBtn, &QPushButton::clicked, this, [=]() { 51 | if (checkArgs(OP_WRITE_REQ) == TFTP_NO_ERROR) { 52 | timer->start(TFTP_REFRESH_INTERVAL); 53 | setModuleEnabled(false); 54 | serverAddr.sin_family = AF_INET; 55 | serverAddr.sin_port = htons(SERV_PORT); 56 | InetPtonA(AF_INET, serverIP, (void*)&serverAddr.sin_addr); 57 | if (mode == MODE_NETASCII) { // encode to netascii 58 | encodeNetascii(localFile); 59 | localFileStd += ".netascii"; 60 | localFile = localFileStd.c_str(); 61 | QTextCodec *code = QTextCodec::codecForName("GB2312"); 62 | localFileByteArray = code->fromUnicode(localFile); 63 | localFileGB = localFileByteArray.data(); 64 | localFile = localFileGB.c_str(); 65 | } 66 | socketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 67 | if (socketFd == INVALID_SOCKET) { QMessageBox::warning(this, "Init Socket Fail.", "Init socket fail."); exit(-1); } 68 | emit sendReadFileArgs(socketFd, serverAddr, remoteFile, localFile, mode, extended, OP_WRITE_REQ); 69 | workThread->start(); 70 | } 71 | }); 72 | connect(this, &MainWindow::sendReadFileArgs, workThread, &WorkThread::recvReadFileArgs); 73 | connect(workThread, &WorkThread::finished, this, [=]() { 74 | setModuleEnabled(true); 75 | timer->stop(); 76 | if(socketFd != INVALID_SOCKET) { closesocket(socketFd); } 77 | }); 78 | 79 | // set UI modules action 设置UI组件动作 80 | connect(workThread, &WorkThread::sendProcessBarMaximum, this, [=](int max) { ui->ProgressBar->setMaximum(max); }); 81 | connect(workThread, &WorkThread::sendProcessBarValue, this, [=](int val) { ui->ProgressBar->setValue(val); }); 82 | connect(workThread, &WorkThread::sendTSize, this, [=](QString tsize) { ui->tsizeLabel->setText(tsize); }); 83 | connect(ui->tsizeCheckBox, &QCheckBox::stateChanged, this, [=](int state) { extended.tsizeExist = state == Qt::Checked ? true : false; }); 84 | connect(ui->blksizeCheckBox, &QCheckBox::stateChanged, this, [=](int state) { extended.blksizeExist = state == Qt::Checked ? true : false; }); 85 | connect(ui->timeoutCheckBox, &QCheckBox::stateChanged, this, [=](int state) { extended.timeoutExist = state == Qt::Checked ? true : false; }); 86 | connect(ui->blksizeSpinBox, &QSpinBox::valueChanged, this, [=](int value) { extended.blksize = value; }); 87 | connect(ui->timeoutSpinBox, &QSpinBox::valueChanged, this, [=](int value) { extended.timeout = value; }); 88 | connect(ui->ModeComboBox, &QComboBox::currentIndexChanged, this, [=](int index) { mode = index == 0 ? MODE_OCTET : MODE_NETASCII; }); 89 | 90 | ui->tsizeCheckBox->setCheckState(Qt::Checked); 91 | ui->blksizeCheckBox->setCheckState(Qt::Checked); 92 | extended.blksize = TFTP_DEFAULT_BLOCK_SIZE; 93 | extended.timeout = TFTP_DEFAULT_TIMEOUT_SEC; 94 | 95 | // real-time speed 实时吞吐量 96 | connect(timer, &QTimer::timeout, workThread, &WorkThread::recvTimeOutSignal); 97 | connect(workThread, &WorkThread::sendUploadSpeed, this, [=](QString speed) { ui->UploadLabel->setText(speed); }); 98 | connect(workThread, &WorkThread::sendDownloadSpeed, this, [=](QString speed) { ui->DownloadLabel->setText(speed); }); 99 | 100 | // log 记录log和保存log 101 | connect(workThread, &WorkThread::sendMsg, this, [=](int code, QString msg) { 102 | QString fullMsg = QDateTime::currentDateTime().toString("[MM/dd hh:mm:ss.zzz]"); 103 | fullMsg += (code == TFTP_NO_ERROR ? " OK " : " ER ") + msg; 104 | ui->LogTextBrowser->append(fullMsg); 105 | }); 106 | connect(ui->ClearBtn, &QPushButton::clicked, this, [=]() { ui->LogTextBrowser->clear(); }); 107 | connect(ui->SaveBtn, &QPushButton::clicked, this, [=]() { 108 | if(!ui->LogTextBrowser->toPlainText().isEmpty()) { 109 | QString logFileName = "TFTP_log_" + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss") + ".txt"; 110 | QFile file("./" + logFileName); 111 | file.open(QIODevice::ReadWrite | QIODevice::Append); 112 | QTextStream out(&file); 113 | out << ui->LogTextBrowser->toPlainText(); 114 | file.close(); 115 | QMessageBox::information(this, "Save Log Success.", "Save log sucess as: \"" + logFileName + "\" in process directory."); 116 | } 117 | else { 118 | QMessageBox::information(this, "Save Log Fail.", "Log is empty."); 119 | } 120 | }); 121 | 122 | } 123 | 124 | MainWindow::~MainWindow() { 125 | if(socketFd != INVALID_SOCKET) { closesocket(socketFd); } 126 | #ifdef WIN32 127 | WSACleanup(); 128 | #endif 129 | delete ui; 130 | } 131 | 132 | /** 133 | * CHECK ARGUMENTS IN INPUT LABELS AND SHOW THE NOTICE MESSAGE 检查输入框内的参数并输出提示信息 134 | * @op : operation 操作 135 | * #return : status code 状态码 136 | */ 137 | int MainWindow::checkArgs(int op) { 138 | if (ui->ServerIPLineEdit->text().isEmpty()) { 139 | QMessageBox::critical(this, "Server IP Error.", "Server IP missing. Please input the Server IP."); 140 | return ERROR_INVALID_ARG; 141 | } 142 | 143 | if (ui->ServerPortLineEdit->text().isEmpty()) { 144 | QMessageBox::critical(this, "Server Port Error.", "Server port missing. Please input the Server port."); 145 | return ERROR_INVALID_ARG; 146 | } 147 | 148 | if (ui->LocalFileLineEdit->text().isEmpty()) { 149 | QMessageBox::critical(this, "Local File Error.", "Local file missing. Please input the local file."); 150 | return ERROR_INVALID_ARG; 151 | } 152 | 153 | if (ui->RemoteFileLineEdit->text().isEmpty()) { 154 | QMessageBox::critical(this, "Remote File Error.", "Remote file missing. Please input the Remote file."); 155 | return ERROR_INVALID_ARG; 156 | } 157 | 158 | QHostAddress *ip = new QHostAddress(ui->ServerIPLineEdit->text()); 159 | if (ip->isNull()) { 160 | QMessageBox::critical(this, "Server IP Error.", "Wrong Server IP. Please input the Server IP again."); 161 | return ERROR_INVALID_ARG; 162 | } 163 | serverIPStd = ui->ServerIPLineEdit->text().toStdString(); 164 | serverIP = serverIPStd.c_str(); 165 | 166 | QTextCodec *code = QTextCodec::codecForName("GB2312"); 167 | 168 | localFileStd = ui->LocalFileLineEdit->text().toStdString(); 169 | localFile = localFileStd.c_str(); 170 | localFileByteArray = code->fromUnicode(localFile); 171 | localFileGB = localFileByteArray.data(); 172 | localFile = localFileGB.c_str(); 173 | if (op == OP_READ_REQ && _access(localFile, 0) != -1) { 174 | QMessageBox::StandardButton res = QMessageBox::question(this, "Local File Warning.", "File exist. Do you want to overlay it?"); 175 | if (res == QMessageBox::No) { return ERROR_INVALID_ARG; } 176 | } 177 | 178 | remoteFileStd = ui->RemoteFileLineEdit->text().toStdString(); 179 | remoteFile = remoteFileStd.c_str(); 180 | remoteFileByteArray = code->fromUnicode(remoteFile); 181 | remoteFileGB = remoteFileByteArray.data(); 182 | remoteFile = remoteFileGB.c_str(); 183 | 184 | extended.isExtended = extended.tsizeExist || extended.blksizeExist || extended.timeoutExist; 185 | return TFTP_NO_ERROR; 186 | } 187 | 188 | /** 189 | * SET UI MODULE STATUS 设置UI组件状态 190 | * @isEnabled : enabled or disabled 启用或禁用 191 | */ 192 | void MainWindow::setModuleEnabled(bool isEnabled) { 193 | ui->GetBtn->setEnabled(isEnabled); 194 | ui->PutBtn->setEnabled(isEnabled); 195 | //ui->BreakBtn->setEnabled(!isEnabled); 196 | ui->tsizeCheckBox->setEnabled(isEnabled); 197 | ui->blksizeCheckBox->setEnabled(isEnabled); 198 | ui->timeoutCheckBox->setEnabled(isEnabled); 199 | ui->blksizeSpinBox->setEnabled(isEnabled); 200 | ui->timeoutSpinBox->setEnabled(isEnabled); 201 | if (isEnabled) { ui->UploadLabel->setText("0.00"); ui->DownloadLabel->setText("0.00"); } 202 | return; 203 | } 204 | 205 | -------------------------------------------------------------------------------- /TFTP_Client/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | 15 | 新宋体 16 | 11 17 | 18 | 19 | 20 | TFTP CLIENT by XBA 21 | 22 | 23 | 24 | :/icon.png:/icon.png 25 | 26 | 27 | 28 | 29 | 30 | 31 | Basic Settings 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | QFrame::NoFrame 42 | 43 | 44 | QFrame::Raised 45 | 46 | 47 | Server IP: 48 | 49 | 50 | 51 | 52 | 53 | 54 | 127.0.0.1 55 | 56 | 57 | 512 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | QFrame::NoFrame 69 | 70 | 71 | QFrame::Raised 72 | 73 | 74 | Server Port: 75 | 76 | 77 | 78 | 79 | 80 | 81 | Qt::StrongFocus 82 | 83 | 84 | 69 85 | 86 | 87 | 128 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Mode: 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | QFrame::NoFrame 117 | 118 | 119 | QFrame::Raised 120 | 121 | 122 | Local File: 123 | 124 | 125 | 126 | 127 | 128 | 129 | QFrame::NoFrame 130 | 131 | 132 | QFrame::Raised 133 | 134 | 135 | Remote File: 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 512 149 | 150 | 151 | 152 | 153 | 154 | 155 | ... 156 | 157 | 158 | 159 | :/openFile.png:/openFile.png 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 512 169 | 170 | 171 | true 172 | 173 | 174 | false 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | Extended Settings 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | false 199 | 200 | 201 | tsize 202 | 203 | 204 | 205 | 206 | 207 | 208 | blksize 209 | 210 | 211 | 212 | 213 | 214 | 215 | timeout 216 | 217 | 218 | 219 | 220 | 221 | 222 | Qt::LeftToRight 223 | 224 | 225 | Unknown 226 | 227 | 228 | 229 | 230 | 231 | 232 | true 233 | 234 | 235 | 16384 236 | 237 | 238 | 512 239 | 240 | 241 | 242 | 243 | 244 | 245 | 255 246 | 247 | 248 | 2 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | Operate 261 | 262 | 263 | 264 | 265 | 266 | 267 | 30 268 | 0 269 | 270 | 271 | 272 | Get 273 | 274 | 275 | 276 | :/download.png:/download.png 277 | 278 | 279 | 280 | 281 | 282 | 283 | Put 284 | 285 | 286 | 287 | :/upload.png:/upload.png 288 | 289 | 290 | 291 | 292 | 293 | 294 | Break(Undone) 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | Info 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | ↑: 315 | 316 | 317 | 318 | 319 | 320 | 321 | 0.00 322 | 323 | 324 | 325 | 326 | 327 | 328 | kB/s 329 | 330 | 331 | 332 | 333 | 334 | 335 | ↓: 336 | 337 | 338 | 339 | 340 | 341 | 342 | 0.00 343 | 344 | 345 | 346 | 347 | 348 | 349 | kB/s 350 | 351 | 352 | 353 | 354 | 355 | 356 | 24 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | QFrame::Box 366 | 367 | 368 | QFrame::Raised 369 | 370 | 371 | 1 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | Qt::Horizontal 381 | 382 | 383 | 384 | 40 385 | 20 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | Clear 394 | 395 | 396 | 397 | :/clear.png:/clear.png 398 | 399 | 400 | 401 | 402 | 403 | 404 | Save 405 | 406 | 407 | 408 | :/save.png:/save.png 409 | 410 | 411 | 412 | 413 | 414 | 415 | Qt::Horizontal 416 | 417 | 418 | 419 | 40 420 | 20 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | -------------------------------------------------------------------------------- /TFTP_Client/workthread.cpp: -------------------------------------------------------------------------------- 1 | #include "workthread.h" 2 | 3 | WorkThread::WorkThread(QObject *parent) : QThread(parent) {} 4 | 5 | void WorkThread::run() { 6 | // Show task msg. 展示任务信息 7 | if (operation == OP_READ_REQ) { emit sendMsg(NO_ERROR, QString("--------------------- READ START ---------------------")); } 8 | if (operation == OP_WRITE_REQ) { emit sendMsg(NO_ERROR, QString("--------------------- WRITE START --------------------")); } 9 | emit sendMsg(NO_ERROR, QString("┌ LOCAL FILE : %1").arg(QString::fromLocal8Bit(localFile))); 10 | emit sendMsg(NO_ERROR, QString("├ REMOTE FILE : %1").arg(QString::fromLocal8Bit(remoteFile))); 11 | char tempIP[16] = "0"; 12 | InetNtopA(AF_INET, &serverAddr.sin_addr, tempIP, 16); 13 | emit sendMsg(NO_ERROR, QString("├ SERVER IP : %1").arg(tempIP)); 14 | emit sendMsg(NO_ERROR, QString("└ SERVER PORT : %1").arg(ntohs(serverAddr.sin_port))); 15 | 16 | // Check task arguments. 检查任务参数 17 | if (checkArgs() != TFTP_NO_ERROR) { tftpTerminate(false); return; } 18 | 19 | // Open local file. 打开本地文件 20 | if (openLocalFile() != TFTP_NO_ERROR) { tftpTerminate(false); return; } 21 | 22 | // Send request. 发送请求报文 23 | if (operation == OP_WRITE_REQ) { tSize = getFileSize(localFile); } 24 | if ((reqMsgLen = sendReqest()) < TFTP_NO_ERROR) { tftpTerminate(false); return; } 25 | 26 | // Set process bar and tsize label. 设置进度条和tsize 27 | emit sendProcessBarMaximum(operation == OP_WRITE_REQ ? (tSize <= 0 ? 0 : tSize) : 0); 28 | emit sendTSize(operation == OP_WRITE_REQ ? QString::number(tSize <= 0 ? 0 : tSize) : "Unknown"); 29 | 30 | // Execute the task. 执行任务 31 | startTime = clock(); 32 | if (operation == OP_READ_REQ) { recvFile(); } 33 | if (operation == OP_WRITE_REQ) { sendFile(); } 34 | } 35 | 36 | /** 37 | * RECEIVE TASK ARGUMENTS FROM MAIN PROCESS 从主进程获得任务参数 38 | */ 39 | void WorkThread::recvReadFileArgs(SOCKET sock, SOCKADDR_IN servAddr, const char* reFile, const char* loFile, int mode, EXTENDED ext, int oper) { 40 | // Get args. 41 | //this->socketFd = sock; 42 | this->serverAddr = servAddr; 43 | this->remoteFile = reFile; 44 | this->localFile = loFile; 45 | this->mode = mode; 46 | this->extended = ext; 47 | this->operation = oper; 48 | 49 | // Init args. 50 | if (fp) { fclose(fp); fp = NULL; } 51 | tSize = 0; 52 | blkSize = TFTP_DEFAULT_BLOCK_SIZE; 53 | timeOut = TFTP_DEFAULT_TIMEOUT_SEC; 54 | tvTimeOut = { timeOut, 0 }; 55 | lastBytesRecv = lastBytesSend = bytesRecv = bytesSend = 0; 56 | totalRetransmitCount = fileSize = 0; 57 | percent = 0; 58 | 59 | this->socketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 60 | } 61 | 62 | /** 63 | * CHECK ARGUMENTS OF CURRENT READ/WRITE REQUEST 检查当前读/写请求的参数 64 | * #return : stauts code 状态码 65 | */ 66 | int WorkThread::checkArgs() { 67 | // Check reqest size. 检查请求报文长度 68 | size_t reqMsgLenCheck = FULLSTRLEN(remoteFile) + FULLSTRLEN(mode == MODE_NETASCII ? "netascii" : "octet"); 69 | if (extended.isExtended) { 70 | int tempTSize = operation == OP_WRITE_REQ ? getFileSize(localFile) : 0; 71 | reqMsgLenCheck += extended.tsizeExist ? FULLSTRLEN("tsize") + FULLNUMLEN(0) : FULLNUMLEN(tempTSize <= 0 ? 0 : tempTSize); 72 | reqMsgLenCheck += extended.blksizeExist ? FULLSTRLEN("blksize") + FULLNUMLEN(extended.blksize) : 0; 73 | reqMsgLenCheck += extended.timeoutExist ? FULLSTRLEN("timeout") + FULLNUMLEN(extended.timeout) : 0; 74 | } 75 | if (reqMsgLenCheck > REQ_SIZE) { 76 | emit sendMsg(ERROR_TOO_LONG_REQUEST, QString("Args Check : Too long request : <%1>.").arg(reqMsgLenCheck + OP_SIZE)); 77 | return ERROR_TOO_LONG_REQUEST; 78 | } 79 | 80 | // Check mode. 检查模式 81 | if (mode != MODE_NETASCII && mode != MODE_OCTET) { 82 | emit sendMsg(ERROR_UNDEFINED_MODE, QString("Args Check : Undefined mode : <%1>.").arg(mode)); 83 | return ERROR_UNDEFINED_MODE; 84 | } 85 | 86 | emit sendMsg(TFTP_NO_ERROR, "Args Check : Valid."); 87 | return TFTP_NO_ERROR; 88 | } 89 | 90 | /** 91 | * OPEN LOCAL FILE ACCORDING TO THE OPERATION 根据当前操作用不同模式打开文件 92 | * #return : stauts code 状态码 93 | */ 94 | int WorkThread::openLocalFile() { 95 | // on windows, if "w", there will be a '\r' before '\n' while output 96 | if (fopen_s(&fp, localFile, operation == OP_READ_REQ ? "wb" : "rb") == 0) { 97 | emit sendMsg(TFTP_NO_ERROR, QString("Open file : Success : <%1>.").arg(QString::fromLocal8Bit(localFile))); 98 | return TFTP_NO_ERROR; 99 | } else { 100 | emit sendMsg(ERROR_OPENFILE_FAIL, QString("Open file : Fail : <%1>.").arg(QString::fromLocal8Bit(localFile))); 101 | return ERROR_OPENFILE_FAIL; 102 | } 103 | } 104 | 105 | /** 106 | * MAKE AND SEND REQUEST PACKET 组装并发送请求报文 107 | * #return : stauts code 状态码 108 | */ 109 | int WorkThread::sendReqest() { 110 | // Make packet. 组装请求报文 111 | int reqMsgLen = 0; 112 | reqPkt.op = htons(operation); 113 | appendMsg(reqPkt.reqMsg, REQ_SIZE, &reqMsgLen, "%s", remoteFile); 114 | appendMsg(reqPkt.reqMsg, REQ_SIZE, &reqMsgLen, mode == MODE_NETASCII ? "netascii" : "octet"); 115 | if (extended.isExtended) { 116 | if (extended.tsizeExist) { appendMsg(reqPkt.reqMsg, REQ_SIZE, &reqMsgLen, "%s%c%d", "tsize", 0, tSize <= 0 ? 0 : tSize); } 117 | if (extended.blksizeExist) { appendMsg(reqPkt.reqMsg, REQ_SIZE, &reqMsgLen, "%s%c%d", "blksize", 0, extended.blksize); } 118 | if (extended.timeoutExist) { appendMsg(reqPkt.reqMsg, REQ_SIZE, &reqMsgLen, "%s%c%d", "timeout", 0, extended.timeout); } 119 | } 120 | 121 | // Send packet. 发送请求报文 122 | int res = sendPkt((char*)&reqPkt, OP_SIZE + reqMsgLen); 123 | if (res < 0) { 124 | emit sendMsg(ERROR_SEND_REQ_FAIL, QString("REQUEST : Send request packet fail with code : <%1>.").arg((WSAGetLastError()))); 125 | return ERROR_SEND_REQ_FAIL; 126 | } else if (res < OP_SIZE + reqMsgLen) { 127 | emit sendMsg(TFTP_NO_ERROR, QString("REQUEST : Send request packet partically with length : <%1>.").arg(res)); 128 | } else { 129 | emit sendMsg(TFTP_NO_ERROR, QString("REQUEST : Send request packet success with length : <%1>.").arg(res)); 130 | if (extended.isExtended) { 131 | if (extended.tsizeExist) { emit sendMsg(TFTP_NO_ERROR, QString("└ Option tsize : <%1>.").arg(tSize <= 0 ? 0 : tSize)); } 132 | if (extended.blksizeExist) { emit sendMsg(TFTP_NO_ERROR, QString("└ Option blksize : <%1>.").arg(extended.blksize)); } 133 | if (extended.timeoutExist) { emit sendMsg(TFTP_NO_ERROR, QString("└ Option timeout : <%1>.").arg(extended.timeout)); } 134 | } 135 | } 136 | return reqMsgLen; 137 | } 138 | 139 | /** 140 | * RECEIVE FILE FROM TFTP SERVER 从TFTP服务器接收文件 141 | * #return : stauts code 状态码 142 | */ 143 | int WorkThread::recvFile() { 144 | bool notSupportExtended = false; 145 | int rcvdSize, retransmitCount = 0, ret, waitForDataRet = 0; 146 | curBlock = extended.isExtended ? 0 : 1; 147 | 148 | // 1. If extended, get OACK packet and set args. 如果使用扩展,接收OACK报文并且从中获得可选设置 149 | if (extended.isExtended) { 150 | while (retransmitCount <= TFTP_MAX_RETRANSMIT) { 151 | int waitForOACKRet = waitForOACK(timeOut * 1000, rcvdSize); 152 | 153 | if (waitForOACKRet == TFTP_OACK_PKT) { // Get the expected OACK packet. 得到了预期的OACK报文 154 | emit sendMsg(TFTP_NO_ERROR, "OACK : Support extended options."); 155 | getOptionFromOAckPkt(rcvdSize); 156 | if (sendAck(curBlock++) < 0) { 157 | tftpTerminate(false); 158 | return ERROR_SEND_ACK_FAIL; 159 | } 160 | break; 161 | } else if (waitForOACKRet == TFTP_DATA_PKT) { // Get the expected DATA<1> packet. 服务器端不支持扩展选项,获得第一个DATA报文 162 | emit sendMsg(TFTP_NOT_SUPPORT_EXT, QString("DATA <1> : Not support extended options. Use default.")); 163 | curBlock = 1; 164 | notSupportExtended = true; 165 | break; 166 | } else if (waitForOACKRet == TFTP_ERROR_PKT) { // Get the expected error packet. 接收到ERROR报文 167 | if (rcvdSize < OP_SIZE + ERRCODE_SIZE) { 168 | emit sendMsg(ERROR_WRONG_PKT, "ERRO : Wrong error packet."); 169 | } else { 170 | emit sendMsg(ntohs(rcvdPkt.errCode), QString(rcvdPkt.errMsg)); 171 | } 172 | tftpTerminate(false); 173 | return ntohs(rcvdPkt.errCode); 174 | } else { // Get a unexpected packet. 接收到其他报文 175 | retransmitCount++; 176 | totalRetransmitCount++; 177 | emit sendMsg(ERROR_RETRANSMIT_EQU, "Retransmit request packet."); 178 | if (sendPkt((char*)&reqPkt, OP_SIZE + reqMsgLen) < 0) { 179 | emit sendMsg(ERROR_SEND_REQ_FAIL, "REQUEST : Send request packet fail."); 180 | tftpTerminate(false); 181 | return ERROR_SEND_REQ_FAIL; 182 | } 183 | } 184 | } 185 | 186 | if (retransmitCount > TFTP_MAX_RETRANSMIT) { 187 | emit sendMsg(ERROR_RETRANSMIT_TOO_MUCH, "Retransmission times too much."); 188 | tftpTerminate(false); 189 | return ERROR_RETRANSMIT_TOO_MUCH; 190 | } 191 | } 192 | 193 | // 2. Get DATA packet and write to local file. 接收DATA报文并将数据写入本地文件 194 | retransmitCount = 0; 195 | while (retransmitCount <= TFTP_MAX_RETRANSMIT) { 196 | // try to get data in timeOut(ms) 197 | // if get the expected packet => send file and write file or error 198 | // else => retransmit last ack and try another time 199 | if (notSupportExtended && curBlock == 1) { 200 | waitForDataRet = TFTP_DATA_PKT; 201 | } else { 202 | waitForDataRet = waitForSpecificPkt(curBlock, timeOut * 1000, rcvdSize, OP_DATA); 203 | } 204 | 205 | if (waitForDataRet == TFTP_DATA_PKT) { // get the expected data packet 得到DATA报文 206 | // Write file. 写入文件 207 | ret = (int)fwrite(rcvdPkt.data, 1, rcvdSize - OP_SIZE - BLOCK_SIZE, fp); 208 | if (ret < (size_t)rcvdSize - OP_SIZE - BLOCK_SIZE) { 209 | emit sendMsg(ERROR_FWRITE_FAIL, "FWRITE : Fail."); 210 | tftpTerminate(false); 211 | return ERROR_FWRITE_FAIL; 212 | } 213 | // Send ACK. 发送ACK报文 214 | if (sendAck(curBlock) < 0) { 215 | tftpTerminate(false); 216 | return ERROR_SEND_ACK_FAIL; 217 | } 218 | // Update data. 更新统计数据 219 | curBlock++; 220 | retransmitCount = 0; 221 | fileSize += rcvdSize - OP_SIZE - BLOCK_SIZE; 222 | // Check if end. 检测是否结束 223 | if (rcvdSize == blkSize + OP_SIZE + BLOCK_SIZE) { // not end => update the process bar 没有结束 => 更新进度条 224 | if (extended.tsizeExist && (double)fileSize / tSize > percent) { 225 | percent = (double)fileSize / tSize + 0.01; 226 | emit sendProcessBarValue(fileSize); 227 | } 228 | continue; 229 | } else { // end => show message and set the process bar 结束了 => 显示统计信息并更新进度条 230 | tftpTerminate(true); 231 | break; 232 | } 233 | } else if (waitForDataRet == TFTP_ERROR_PKT) { // Get a expected error packet. 接收到ERROR报文 234 | if (rcvdSize < OP_SIZE + ERRCODE_SIZE) { 235 | emit sendMsg(ERROR_WRONG_PKT, "ERRO : Wrong error packet."); 236 | } else { 237 | emit sendMsg(ntohs(rcvdPkt.errCode), QString(rcvdPkt.errMsg)); 238 | } 239 | tftpTerminate(false); 240 | return ERROR_WRONG_PKT; 241 | } else { // get a unexpected packet 接收到其他报文 242 | retransmitCount++; 243 | totalRetransmitCount++; 244 | if (!extended.isExtended && curBlock == 1) { 245 | emit sendMsg(ERROR_RETRANSMIT_EQU, "Retransmit request packet."); 246 | if (sendPkt((char*)&reqPkt, OP_SIZE + reqMsgLen) < 0) { 247 | emit sendMsg(ERROR_SEND_REQ_FAIL, "REQUEST : Send request packet fail."); 248 | tftpTerminate(false); 249 | return ERROR_SEND_REQ_FAIL; 250 | } 251 | } else { 252 | emit sendMsg(ERROR_RETRANSMIT_ACK, QString("Retransmit ACK packet <%1>.").arg(curBlock - 1)); 253 | if (sendAck(curBlock - 1) < 0) { 254 | tftpTerminate(false); 255 | return ERROR_SEND_ACK_FAIL; 256 | } 257 | } 258 | } 259 | } 260 | 261 | if (retransmitCount > TFTP_MAX_RETRANSMIT) { 262 | emit sendMsg(ERROR_RETRANSMIT_TOO_MUCH, "Retransmission times too much."); 263 | tftpTerminate(false); 264 | return ERROR_RETRANSMIT_TOO_MUCH; 265 | } 266 | 267 | if (mode == MODE_NETASCII) { decodeNetascii(localFile, PLATFORM_WINDOWS); } 268 | 269 | return TFTP_NO_ERROR; 270 | } 271 | 272 | /** 273 | * SEND FILE TO TFTP SERVER 向TFTP服务器发送文件 274 | * #return : stauts code 状态码 275 | */ 276 | int WorkThread::sendFile() { 277 | bool haveGetFirstAck = false, isFinished = false; 278 | int rcvdSize, dataSize = 0, retransmitCount = 0, waitForDataRet = 0; 279 | curBlock = 0; 280 | 281 | // 1. If extended, get OACK packet and set args. 如果使用扩展,接收OACK报文并且从中获得可选设置 282 | if (extended.isExtended) { 283 | while (retransmitCount <= TFTP_MAX_RETRANSMIT) { 284 | int waitForOACKRet = waitForOACK(timeOut * 1000, rcvdSize); 285 | 286 | if (waitForOACKRet == TFTP_OACK_PKT) { // Get the expected OACK packet. 接收到预期的OACK报文 287 | emit sendMsg(TFTP_NO_ERROR, "OACK : Support extended options."); 288 | getOptionFromOAckPkt(rcvdSize); 289 | haveGetFirstAck = true; 290 | break; 291 | } else if (waitForOACKRet == TFTP_ACK_PKT) { // Get the expected ACK<0> packet. 服务器端不支持扩展选项,获得第一个ACK报文 292 | emit sendMsg(TFTP_NOT_SUPPORT_EXT, QString("ACK <0> : Not support extended options. Use default.")); 293 | haveGetFirstAck = true; 294 | break; 295 | } else if (waitForOACKRet == TFTP_ERROR_PKT) { // Get the expected error packet. 接收到ERROR报文 296 | if (rcvdSize < OP_SIZE + ERRCODE_SIZE) { 297 | emit sendMsg(ERROR_WRONG_PKT, "ERRO : Wrong error packet."); 298 | } else { 299 | emit sendMsg(ntohs(rcvdPkt.errCode), QString(rcvdPkt.errMsg)); 300 | } 301 | tftpTerminate(false); 302 | return ntohs(rcvdPkt.errCode); 303 | } else { // Get a unexpected packet. 接收到其他报文 304 | retransmitCount++; 305 | totalRetransmitCount++; 306 | emit sendMsg(ERROR_RETRANSMIT_EQU, "Retransmit request packet."); 307 | if (sendPkt((char*)&reqPkt, OP_SIZE + reqMsgLen) < 0) { 308 | emit sendMsg(ERROR_SEND_REQ_FAIL, "REQUEST : Send request packet fail."); 309 | tftpTerminate(false); 310 | return ERROR_SEND_REQ_FAIL; 311 | } 312 | } 313 | } 314 | 315 | if (retransmitCount > TFTP_MAX_RETRANSMIT) { 316 | emit sendMsg(ERROR_RETRANSMIT_TOO_MUCH, "Retransmission times too much."); 317 | tftpTerminate(false); 318 | return ERROR_RETRANSMIT_TOO_MUCH; 319 | } 320 | } 321 | 322 | // 2. Read loacl file and sent DATA packet. 读取本地文件并且发送DATA报文 323 | retransmitCount = 0; 324 | while (retransmitCount <= TFTP_MAX_RETRANSMIT) { 325 | // try to get ack in timeOut(ms) 326 | // if get the expected packet => read file and send data or error 327 | // else => retransmit last data and try another time 328 | if (curBlock == 0 && haveGetFirstAck) { 329 | waitForDataRet = TFTP_ACK_PKT; 330 | } else { 331 | waitForDataRet = waitForSpecificPkt(curBlock, timeOut * 1000, rcvdSize, OP_ACK); 332 | } 333 | 334 | if (waitForDataRet == TFTP_ACK_PKT) { // Get the expected ack packet. 接收到ACK报文 335 | if (!isFinished) { 336 | // Read file. 读取本地文件 337 | dataSize = (int)fread(dataPkt.data, 1, blkSize, fp); 338 | if (dataSize < blkSize) { isFinished = true; } 339 | // Send data. 发送DATA报文 340 | dataPkt.block = htons(++curBlock); 341 | if (sendPkt((char*)&dataPkt, dataSize + OP_SIZE + BLOCK_SIZE) < 0) { 342 | emit sendMsg(ERROR_SEND_DATA_FAIL, QString("DATA <%1> : Send DATA packet fail.").arg(curBlock - 1)); 343 | tftpTerminate(false); 344 | return ERROR_SEND_DATA_FAIL; 345 | } 346 | // Update data. 更新统计数据和进度条 347 | retransmitCount = 0; 348 | fileSize += dataSize; 349 | if ((double)fileSize / tSize > percent) { 350 | percent = (double)fileSize / tSize + 0.01; 351 | emit sendProcessBarValue(fileSize); 352 | } 353 | } else { // Show message and set the process bar. 显示统计信息并更新进度条 354 | tftpTerminate(true); 355 | break; 356 | } 357 | } else if (waitForDataRet == TFTP_ERROR_PKT) { // Get a expected error packet. 接收到ERROR报文 358 | if (rcvdSize < OP_SIZE + ERRCODE_SIZE) { 359 | emit sendMsg(ERROR_WRONG_PKT, "ERRO : Wrong error packet."); 360 | } else { 361 | emit sendMsg(ntohs(rcvdPkt.errCode), QString(rcvdPkt.errMsg)); 362 | } 363 | tftpTerminate(false); 364 | return ntohs(rcvdPkt.errCode); 365 | } 366 | else { // Get a unexpected packet. 接收到其他报文 367 | retransmitCount++; 368 | totalRetransmitCount++; 369 | if (!extended.isExtended && curBlock == 0) { 370 | emit sendMsg(ERROR_RETRANSMIT_EQU, "Retransmit request packet."); 371 | if (sendPkt((char*)&reqPkt, OP_SIZE + reqMsgLen) < 0) { 372 | emit sendMsg(ERROR_SEND_REQ_FAIL, "REQUEST : Send request packet fail."); 373 | tftpTerminate(false); 374 | return ERROR_SEND_REQ_FAIL; 375 | } 376 | } else { 377 | emit sendMsg(ERROR_RETRANSMIT_ACK, QString("Retransmit DATA packet <%1>.").arg(curBlock)); 378 | if (sendPkt((char*)&dataPkt, dataSize + OP_SIZE + BLOCK_SIZE) < 0) { 379 | emit sendMsg(ERROR_SEND_DATA_FAIL, QString("DATA <%1> : Send DATA packet fail.").arg(curBlock)); 380 | tftpTerminate(false); 381 | return ERROR_SEND_DATA_FAIL; 382 | } 383 | } 384 | } 385 | } 386 | 387 | if (retransmitCount > TFTP_MAX_RETRANSMIT) { 388 | emit sendMsg(ERROR_RETRANSMIT_TOO_MUCH, "Retransmission times too much."); 389 | tftpTerminate(false); 390 | return TFTP_MAX_RETRANSMIT; 391 | } 392 | 393 | return TFTP_NO_ERROR; 394 | } 395 | 396 | /** 397 | * MAKE AND SEND ACK PACKET 组装并发送ACK报文 398 | * @block : block num that ACK reply to 当前ACK对应的块号 399 | * #return : the byte length sent 发送数据的字节长度 400 | */ 401 | int WorkThread::sendAck(WORD block) { 402 | ackPkt.block = htons(block); 403 | int ret = sendPkt((char*)&ackPkt, sizeof(PKG_ACK)); 404 | if (ret < 0) { 405 | emit sendMsg(ERROR_SEND_ACK_FAIL, QString("ACK <%1> : Send ACK packet fail.").arg(block)); 406 | } else { 407 | // emit sendMsg(TFTP_NO_ERROR, QString("ACK <%1> : Send ACK packet success.").arg(block)); 408 | } 409 | return ret; 410 | } 411 | 412 | /** 413 | * SEND PACKET 发送报文 414 | * @buf : data to be sent 需要发送的数据 415 | * @len : length of the data 需要发送数据的长度 416 | * #return : the byte length sent 发送数据的字节长度 417 | */ 418 | int WorkThread::sendPkt(const char* buf, int len) { 419 | int ret = sendto(socketFd, buf, len, 0, (SOCKADDR*)&serverAddr, sizeof(serverAddr)); 420 | if (ret > 0) { bytesSend += ret; } 421 | return ret; 422 | } 423 | 424 | /** 425 | * RECEIVE PACKET 接收报文 426 | * @buf : buffer to get data 缓冲区 427 | * @len : length of the buffer 缓冲区长度 428 | * #return : the byte length received 接收数据的字节长度 429 | */ 430 | int WorkThread::recvPkt(char* buf, int len) { 431 | int ret = recvfrom(socketFd, buf, len, 0, (SOCKADDR*)&serverAddr, &serverAddrLen); 432 | if (ret > 0) { bytesRecv += ret; } 433 | return ret; 434 | } 435 | 436 | /** 437 | * GET OPTION SETTINGS FROM OACK PACKET 从OACK报文中接收可选设置 438 | * @rcvdSize : the length of the OACK packet OACK报文的长度 439 | * #return : the num of optional settings 可选数据的个数 440 | */ 441 | int WorkThread::getOptionFromOAckPkt(int rcvdSize) { 442 | int count = 0; 443 | char opt[ERROR_SIZE] = { 0 }, val[ERROR_SIZE] = { 0 }; 444 | for (int offset = 0; offset < rcvdSize - 2;) { 445 | getMsg(opt, ERROR_SIZE, rcvdPkt.opMsg, &offset); 446 | getMsg(val, ERROR_SIZE, rcvdPkt.opMsg, &offset); 447 | if (extended.tsizeExist && strcmp(opt, "tsize") == 0) { 448 | emit sendMsg(TFTP_NO_ERROR, QString("└ Option tsize : <%1>.").arg(val)); 449 | emit sendTSize(QString::number(tSize = atoi(val))); 450 | count++; 451 | } 452 | if (extended.blksizeExist && strcmp(opt, "blksize") == 0) { 453 | emit sendMsg(TFTP_NO_ERROR, QString("└ Option blksize : <%1>.").arg(val)); 454 | blkSize = atoi(val); 455 | count++; 456 | } 457 | if (extended.timeoutExist && strcmp(opt, "timeout") == 0) { 458 | emit sendMsg(TFTP_NO_ERROR, QString("└ Option timeout : <%1>.").arg(val)); 459 | tvTimeOut.tv_sec = timeOut = atoi(val) + 1; 460 | count++; 461 | } 462 | } 463 | if (extended.tsizeExist) { emit sendProcessBarMaximum(tSize); } 464 | return count; 465 | } 466 | 467 | /** 468 | * TRY TO GET A PACKET IN TIMEOUTMS 尝试在TIMEOUTMS时间内接收一个报文 469 | * @timeOutMs : time out interval (ms) 超时时间(毫秒) 470 | * @rcvdSize : used to return the size of packet received 用来返回接收报文的大小 471 | * #return : stauts code 状态码 472 | */ 473 | int WorkThread:: waitForPkt(int timeOutMs, int &rcvdSize) { 474 | FD_SET readFds; 475 | TIMEVAL tv = {timeOutMs / 1000, timeOutMs % 1000 * 1000}; 476 | FD_ZERO(&readFds); 477 | FD_SET(socketFd, &readFds); 478 | int selectRet = select(socketFd + 1, &readFds, NULL, NULL, &tv); 479 | 480 | if (selectRet == SOCKET_ERROR) { 481 | return ERROR_SOCKET_ERROR; 482 | } else if (selectRet == 0) { 483 | return TFTP_ERROR_TIMEOUT; 484 | } else { 485 | rcvdSize = recvPkt((char*)&rcvdPkt, sizeof(PKG_DATA_ERROR_OACK)); 486 | if (rcvdSize == SOCKET_ERROR) { 487 | emit sendMsg(ERROR_SOCKET_ERROR, "RECV : SOCKET_ERROR."); 488 | return ERROR_SOCKET_ERROR; 489 | } else if (rcvdSize == 0) { 490 | emit sendMsg(ERROR_CONNECT_CLOSE, "RECV : The connection has been closed."); 491 | return ERROR_CONNECT_CLOSE; 492 | } 493 | } 494 | if (ntohs(rcvdPkt.op) == OP_DATA || ntohs(rcvdPkt.op) == OP_ACK || ntohs(rcvdPkt.op) == OP_ERROR) { 495 | if (rcvdSize < OP_SIZE + MODE_SIZE) { return TFTP_ERROR_WRONG_PKT; } 496 | } else if (ntohs(rcvdPkt.op) == OP_OACK) { 497 | if (rcvdSize < OP_SIZE) { return TFTP_ERROR_WRONG_PKT; } 498 | } 499 | return TFTP_NO_ERROR; 500 | } 501 | 502 | /** 503 | * TRY TO GET A OACK PACKET IN TIMEOUTMS 尝试在TIMEOUTMS时间内接收一个OACK报文 504 | * if operation == OP_READ_REQ and the server don't support extended options, there will be data<1> 505 | * if operation == OP_WRITE_REQ and the server don't support extended options, there will be ack<0> 506 | * @timeOutMs : time out interval (ms) 超时时间(毫秒) 507 | * @rcvdSize : used to return the size of packet received 用来返回接收报文的大小 508 | * #return : type of the packet received 接收到报文的类型 509 | */ 510 | int WorkThread::waitForOACK(int timeOutMs, int &rcvdSize) { 511 | int restTime = timeOutMs, waitForPktRet, startTime; 512 | 513 | do { 514 | startTime = clock(); 515 | waitForPktRet = waitForPkt(restTime, rcvdSize); 516 | restTime -= (clock() - startTime); 517 | } while (waitForPktRet == TFTP_NO_ERROR && (operation == OP_READ_REQ ? (ntohs(rcvdPkt.op) == OP_DATA && ntohs(rcvdPkt.block) != 1) : 518 | (ntohs(rcvdPkt.op) == OP_ACK && ntohs(rcvdPkt.block) != 0))); 519 | 520 | if (restTime <= 0) { emit sendMsg(TFTP_ERROR_TIMEOUT, QString("OACK : Timeout.")); } 521 | 522 | return waitForPktRet != TFTP_NO_ERROR ? waitForPktRet : ntohs(rcvdPkt.op); 523 | } 524 | 525 | /** 526 | * TRY TO GET A SPECIFIC PACKET(IDENTIFIED BY PKTTYPE) IN TIMEOUTMS 尝试在TIMEOUTMS时间内接收一个由PKTTYPE指定类型的报文 527 | * @block : block num of the packet 期待的报文的块号 528 | * @timeOutMs : time out interval (ms) 超时时间(毫秒) 529 | * @rcvdSize : used to return the size of packet received 用来返回接收报文的大小 530 | * @pktType : packet type want to get 期望的报文类型 531 | * #return : type of the packet received 接收到报文的类型 532 | */ 533 | int WorkThread::waitForSpecificPkt(WORD block, int timeOutMs, int &rcvdSize, int pktType) { 534 | int restTime = timeOutMs, waitForPktRet, startTime; 535 | 536 | do { 537 | startTime = clock(); 538 | waitForPktRet = waitForPkt(restTime, rcvdSize); 539 | restTime -= (clock() - startTime); 540 | } while (waitForPktRet == TFTP_NO_ERROR && (ntohs(rcvdPkt.op) != pktType || ntohs(rcvdPkt.block) != block)); 541 | 542 | if (restTime <= 0) { 543 | emit sendMsg(TFTP_ERROR_TIMEOUT, (pktType == TFTP_ACK_PKT ? "ACK " : "DATA") + QString(" <%1> : Timeout.").arg(block)); 544 | } 545 | 546 | return waitForPktRet != TFTP_NO_ERROR ? waitForPktRet : ntohs(rcvdPkt.op); 547 | } 548 | 549 | /** 550 | * TERMINATE CURRENT TASK AND OUTPUT THE STATISTICAL INFORMATION 结束当前任务并输出统计信息 551 | * @isSuccess : if the task is done successfully 当前任务是否成功结束 552 | */ 553 | void WorkThread::tftpTerminate(bool isSuccess) { 554 | emit sendProcessBarMaximum(1); 555 | emit sendProcessBarValue(isSuccess ? 1 : 0); 556 | if (isSuccess) { 557 | endTime = clock(); 558 | double timeSec = (double)(endTime - startTime) / CLOCKS_PER_SEC; 559 | if (operation == OP_READ_REQ) { 560 | emit sendMsg(TFTP_NO_ERROR, QString("Read Success : <%1> ==> <%2>").arg(QString::fromLocal8Bit(remoteFile), QString::fromLocal8Bit(localFile))); 561 | } else { 562 | emit sendMsg(TFTP_NO_ERROR, QString("WRITE Success : <%1> ==> <%2>").arg(QString::fromLocal8Bit(localFile), QString::fromLocal8Bit(remoteFile))); 563 | } 564 | emit sendMsg(TFTP_NO_ERROR, QString("├ Block : <%1 blks | %2 B/blk> ").arg(curBlock - (operation == OP_READ_REQ ? 1 : 0)).arg(blkSize)); 565 | emit sendMsg(TFTP_NO_ERROR, QString("├ Time : <%1 s> ").arg(timeSec)); 566 | emit sendMsg(TFTP_NO_ERROR, QString("├ File Size : <%1 kB> ").arg((double)fileSize / 1024)); 567 | emit sendMsg(TFTP_NO_ERROR, QString("├ Down : <%1 kB> ").arg((double)bytesRecv / 1024)); 568 | emit sendMsg(TFTP_NO_ERROR, QString("├ Down Speed : <%1 kB/s> ").arg(((double)bytesRecv / 1024 / timeSec))); 569 | emit sendMsg(TFTP_NO_ERROR, QString("├ Up : <%1 kB> ").arg((double)bytesSend / 1024)); 570 | emit sendMsg(TFTP_NO_ERROR, QString("├ Up Speed : <%1 kB/s> ").arg(((double)bytesSend / 1024 / timeSec))); 571 | emit sendMsg(TFTP_NO_ERROR, QString("└ Retransmit : <%1 times> ").arg(totalRetransmitCount)); 572 | emit sendMsg(TFTP_NO_ERROR, QString("---------------------- END SUCC ----------------------")); 573 | } else { 574 | emit sendMsg(TFTP_NO_ERROR, QString("---------------------- END FAIL ----------------------")); 575 | } 576 | if (fp) { fclose(fp); } 577 | } 578 | 579 | /** 580 | * UPDATE THE REAL-TIME SPEED INFORMATION 更新实时吞吐量 581 | */ 582 | void WorkThread::recvTimeOutSignal() { 583 | emit sendUploadSpeed(QString::number(((double)bytesSend - lastBytesSend) / TFTP_REFRESH_INTERVAL * 1000 / 1024)); 584 | emit sendDownloadSpeed(QString::number(((double)bytesRecv - lastBytesRecv) / TFTP_REFRESH_INTERVAL * 1000 / 1024)); 585 | lastBytesRecv = bytesRecv; 586 | lastBytesSend = bytesSend; 587 | } 588 | -------------------------------------------------------------------------------- /TFTP_Client/TFTP_Client.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {4771c435-8ff7-4330-bbca-e01a1e213b52} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | false 41 | true 42 | false 43 | 0 44 | true 45 | true 46 | 0 47 | 8 48 | true 49 | false 50 | 1 51 | true 52 | true 53 | true 54 | *.md, *.MD, Makefile 55 | false 56 | true 57 | 58 | 59 | 60 | ProjectExplorer.Project.PluginSettings 61 | 62 | 63 | true 64 | false 65 | true 66 | true 67 | true 68 | true 69 | 70 | 71 | 0 72 | true 73 | 74 | -fno-delayed-template-parsing 75 | 76 | true 77 | Builtin.BuildSystem 78 | 79 | true 80 | true 81 | Builtin.DefaultTidyAndClazy 82 | 4 83 | 84 | 85 | 86 | true 87 | 88 | 89 | 90 | 91 | ProjectExplorer.Project.Target.0 92 | 93 | Desktop 94 | Desktop Qt 6.0.4 MSVC2019 64bit 95 | Desktop Qt 6.0.4 MSVC2019 64bit 96 | qt.qt6.604.win64_msvc2019_64_kit 97 | 1 98 | 0 99 | 0 100 | 101 | 0 102 | C:\Users\86131\Desktop\TFTP_Client\build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Debug 103 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Debug 104 | 105 | 106 | true 107 | QtProjectManager.QMakeBuildStep 108 | false 109 | 110 | 111 | 112 | true 113 | Qt4ProjectManager.MakeStep 114 | 115 | 2 116 | Build 117 | Build 118 | ProjectExplorer.BuildSteps.Build 119 | 120 | 121 | 122 | true 123 | Qt4ProjectManager.MakeStep 124 | clean 125 | 126 | 1 127 | Clean 128 | Clean 129 | ProjectExplorer.BuildSteps.Clean 130 | 131 | 2 132 | false 133 | 134 | 135 | Debug 136 | Qt4ProjectManager.Qt4BuildConfiguration 137 | 2 138 | 139 | 140 | C:\Users\86131\Desktop\TFTP_Client\build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Release 141 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Release 142 | 143 | 144 | true 145 | QtProjectManager.QMakeBuildStep 146 | false 147 | 148 | 149 | 150 | true 151 | Qt4ProjectManager.MakeStep 152 | 153 | 2 154 | Build 155 | Build 156 | ProjectExplorer.BuildSteps.Build 157 | 158 | 159 | 160 | true 161 | Qt4ProjectManager.MakeStep 162 | clean 163 | 164 | 1 165 | Clean 166 | Clean 167 | ProjectExplorer.BuildSteps.Clean 168 | 169 | 2 170 | false 171 | 172 | 173 | Release 174 | Qt4ProjectManager.Qt4BuildConfiguration 175 | 0 176 | 0 177 | 178 | 179 | 0 180 | C:\Users\86131\Desktop\TFTP_Client\build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Profile 181 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Profile 182 | 183 | 184 | true 185 | QtProjectManager.QMakeBuildStep 186 | false 187 | 188 | 189 | 190 | true 191 | Qt4ProjectManager.MakeStep 192 | 193 | 2 194 | Build 195 | Build 196 | ProjectExplorer.BuildSteps.Build 197 | 198 | 199 | 200 | true 201 | Qt4ProjectManager.MakeStep 202 | clean 203 | 204 | 1 205 | Clean 206 | Clean 207 | ProjectExplorer.BuildSteps.Clean 208 | 209 | 2 210 | false 211 | 212 | 213 | Profile 214 | Qt4ProjectManager.Qt4BuildConfiguration 215 | 0 216 | 0 217 | 0 218 | 219 | 3 220 | 221 | 222 | 0 223 | Deploy 224 | Deploy 225 | ProjectExplorer.BuildSteps.Deploy 226 | 227 | 1 228 | 229 | false 230 | ProjectExplorer.DefaultDeployConfiguration 231 | 232 | 1 233 | 234 | true 235 | true 236 | true 237 | 238 | 2 239 | 240 | Qt4ProjectManager.Qt4RunConfiguration:C:/Users/86131/Desktop/TFTP_Client/TFTP_Client/TFTP_Client.pro 241 | C:/Users/86131/Desktop/TFTP_Client/TFTP_Client/TFTP_Client.pro 242 | false 243 | true 244 | true 245 | false 246 | true 247 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MSVC2019_64bit-Release 248 | 249 | 1 250 | 251 | 252 | 253 | ProjectExplorer.Project.Target.1 254 | 255 | Desktop 256 | Desktop Qt 6.0.4 MinGW 64-bit 257 | Desktop Qt 6.0.4 MinGW 64-bit 258 | qt.qt6.604.win64_mingw81_kit 259 | 0 260 | 0 261 | 0 262 | 263 | 0 264 | C:\Users\86131\Desktop\TFTP_Client\build-TFTP_Client-Desktop_Qt_6_0_4_MinGW_64_bit-Debug 265 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MinGW_64_bit-Debug 266 | 267 | 268 | true 269 | QtProjectManager.QMakeBuildStep 270 | false 271 | 272 | 273 | 274 | true 275 | Qt4ProjectManager.MakeStep 276 | 277 | 2 278 | Build 279 | Build 280 | ProjectExplorer.BuildSteps.Build 281 | 282 | 283 | 284 | true 285 | Qt4ProjectManager.MakeStep 286 | clean 287 | 288 | 1 289 | Clean 290 | Clean 291 | ProjectExplorer.BuildSteps.Clean 292 | 293 | 2 294 | false 295 | 296 | 297 | Debug 298 | Qt4ProjectManager.Qt4BuildConfiguration 299 | 2 300 | 301 | 302 | C:\Users\86131\Desktop\TFTP_Client\build-TFTP_Client-Desktop_Qt_6_0_4_MinGW_64_bit-Release 303 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MinGW_64_bit-Release 304 | 305 | 306 | true 307 | QtProjectManager.QMakeBuildStep 308 | false 309 | 310 | 311 | 312 | true 313 | Qt4ProjectManager.MakeStep 314 | 315 | 2 316 | Build 317 | Build 318 | ProjectExplorer.BuildSteps.Build 319 | 320 | 321 | 322 | true 323 | Qt4ProjectManager.MakeStep 324 | clean 325 | 326 | 1 327 | Clean 328 | Clean 329 | ProjectExplorer.BuildSteps.Clean 330 | 331 | 2 332 | false 333 | 334 | 335 | Release 336 | Qt4ProjectManager.Qt4BuildConfiguration 337 | 0 338 | 0 339 | 340 | 341 | 0 342 | C:\Users\86131\Desktop\TFTP_Client\build-TFTP_Client-Desktop_Qt_6_0_4_MinGW_64_bit-Profile 343 | C:/Users/86131/Desktop/TFTP_Client/build-TFTP_Client-Desktop_Qt_6_0_4_MinGW_64_bit-Profile 344 | 345 | 346 | true 347 | QtProjectManager.QMakeBuildStep 348 | false 349 | 350 | 351 | 352 | true 353 | Qt4ProjectManager.MakeStep 354 | 355 | 2 356 | Build 357 | Build 358 | ProjectExplorer.BuildSteps.Build 359 | 360 | 361 | 362 | true 363 | Qt4ProjectManager.MakeStep 364 | clean 365 | 366 | 1 367 | Clean 368 | Clean 369 | ProjectExplorer.BuildSteps.Clean 370 | 371 | 2 372 | false 373 | 374 | 375 | Profile 376 | Qt4ProjectManager.Qt4BuildConfiguration 377 | 0 378 | 0 379 | 0 380 | 381 | 3 382 | 383 | 384 | 0 385 | Deploy 386 | Deploy 387 | ProjectExplorer.BuildSteps.Deploy 388 | 389 | 1 390 | 391 | false 392 | ProjectExplorer.DefaultDeployConfiguration 393 | 394 | 1 395 | 396 | true 397 | true 398 | true 399 | 400 | 2 401 | 402 | Qt4ProjectManager.Qt4RunConfiguration:C:/Users/86131/Desktop/TFTP_Client/TFTP_Client/TFTP_Client.pro 403 | C:/Users/86131/Desktop/TFTP_Client/TFTP_Client/TFTP_Client.pro 404 | false 405 | true 406 | true 407 | false 408 | true 409 | 410 | 1 411 | 412 | 413 | 414 | ProjectExplorer.Project.TargetCount 415 | 2 416 | 417 | 418 | ProjectExplorer.Project.Updater.FileVersion 419 | 22 420 | 421 | 422 | Version 423 | 22 424 | 425 | 426 | --------------------------------------------------------------------------------