教程简介:
本教程是我在学习c/c++时的一个学习项目,在学习ftp协议时写了这个控制台版本的ftp客户端,整过程序代码并不多,查了许多资料完成的,里面涉及到的学习点比较全面,毕竟是学习项目,不保证里面会有许多bug,希望我共享的学习源码可以帮助更多有需要的朋友。
FTP协议简介:
FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。在开发网站的时候,通常利用FTP协议把网页或程序传到Web服务器上。此外,由于FTP传输效率非常高,在网络上传输大的文件时,一般也采用该协议。
默认情况下FTP协议使用TCP端口中的 20和21这两个端口,其中20用于传输数据,21用于传输控制信息。但是,是否使用20作为传输数据的端口与FTP使用的传输模式有关,如果采用主动模式,那么数据传输端口就是20;如果采用被动模式,则具体最终使用哪个端口要服务器端和客户端协商决定。
FTP命令:
FTP 控制帧即远程交换信息,包含控制命令和选项。大多数 FTP 控制帧是简单的ASCII文本,用户通过用户PI向服务器PI发出FTP命令,服务器PI执行用户PI的FTP命令,并将执行的结果返回给用户。常用的FTP命令如下所示:
命令 描述
ABOR 中断数据连接程序
ACCT <account> 系统特权帐号
ALLO <bytes> 为服务器上的文件存储器分配字节
APPE <filename> 添加文件到服务器同名文件
CDUP <dir path> 改变服务器上的父目录
CWD <dir path> 改变服务器上的工作目录
DELE <filename> 删除服务器上的指定文件
HELP <command> 返回指定命令信息
LIST <name> 如果是文件名列出文件信息,如果是目录则列出文件列表
MODE <mode> 传输模式(S=流模式,B=块模式,C=压缩模式)
MKD <directory> 在服务器上建立指定目录
NLST <directory> 列出指定目录内容
NOOP 无动作,除了来自服务器上的承认
PASS <password> 系统登录密码
PASV 请求服务器等待数据连接
PORT <address> IP 地址和两字节的端口 ID
PWD 显示当前工作目录
QUIT 从 FTP 服务器上退出登录
REIN 重新初始化登录状态连接
REST <offset> 由特定偏移量重启文件传递
RETR <filename> 从服务器上找回(复制)文件
RMD <directory> 在服务器上删除指定目录
RNFR <old path> 对旧路径重命名
RNTO <new path> 对新路径重命名
SITE <params> 由服务器提供的站点特殊参数
SMNT <pathname> 挂载指定文件结构
STAT <directory> 在当前程序或目录上返回信息
STOR <filename> 储存(复制)文件到服务器上
STOU <filename> 储存文件到服务器名称上
STRU <type> 数据结构(F=文件,R=记录,P=页面)
SYST 返回服务器使用的操作系统
TYPE <data type> 数据类型(A=ASCII,E=EBCDIC,I=binary)
USER <username>> 系统登录的用户名
FTP响应码:
110重新启动标记答复。
120服务已就绪,在nnn分钟后开始。
125数据连接已打开,正在开始传输。
150文件状态正常,准备打开数据连接。
2xx-肯定的完成答复一项操作已经成功完成。客户端可以执行新命令。
200命令确定。
202未执行命令,站点上的命令过多。
211系统状态,或系统帮助答复。
212目录状态。
213文件状态。
214帮助消息。
215NAME系统类型,其中,NAME是AssignedNumbers文档中所列的正式系统名称。
220服务就绪,可以执行新用户的请求。
221服务关闭控制连接。如果适当,请注销。
225数据连接打开,没有进行中的传输。
226关闭数据连接。请求的文件操作已成功(例如,传输文件或放弃文件)。
227进入被动模式(h1,h2,h3,h4,p1,p2)。
230用户已登录,继续进行。
250请求的文件操作正确,已完成。
257已创建“PATHNAME”。
3xx-肯定的中间答复该命令已成功,但服务器需要更多来自客户端的信息以完成对请求的处理。
331用户名正确,需要密码。
332需要登录帐户。
350请求的文件操作正在等待进一步的信息。
4xx-瞬态否定的完成答复该命令不成功,但错误是暂时的。如果客户端重试命令,可能会执行成功。
421服务不可用,正在关闭控制连接。如果服务确定它必须关闭,将向任何命令发送这一应答。
425无法打开数据连接。 426Connectionclosed;transferaborted.
450未执行请求的文件操作。文件不可用(例如,文件繁忙)。
451请求的操作异常终止:正在处理本地错误。
452未执行请求的操作。系统存储空间不够。
5xx-永久性否定的完成答复该命令不成功,错误是永久性的。如果客户端重试命令,将再次出现同样的错误。
500语法错误,命令无法识别。这可能包括诸如命令行太长之类的错误。
501在参数中有语法错误。
502未执行命令。
503错误的命令序列。
504未执行该参数的命令。
530未登录。
532存储文件需要帐户。
550未执行请求的操作。文件不可用(例如,未找到文件,没有访问权限)。
551请求的操作异常终止:未知的页面类型。
552请求的文件操作异常终止:超出存储分配(对于当前目录或数据集)。
553未执行请求的操作。不允许的文件名。
源码:
开发平台: windows 7
开发工具: QtCreator
EasyFtp.pro 配置文件
TEMPLATE = app CONFIG += console c++11 CONFIG -= app_bundle CONFIG -= qt LIBS += -lpthread libwsock32 libws2_32 SOURCES += \ common.cpp \ index.cpp \ main.cpp HEADERS += \ common.h \ index.h
index.h 头文件
/**************************** * title: ftp主程序类 * description: 学习ftp协议与c++通信篇的一个学习项目,大致流程已经以及程序已经探索完毕,这个项目共享给学习需要的朋友,希望能帮助到一部分人 * datetime: 2020年6月15日16:11:19 * author: Sindsun * from: http://www.sindsun.com ****************************/ #ifndef INDEX_H #define INDEX_H #include <iostream> #include <string> #include <winsock2.h> #include <fstream> #include <windows.h> #include <iomanip> #include <locale> #include "common.h" using namespace std; #define MAX_MSG_SIZE 1024 class Index{ public: Index(); ~Index(); //程序开始 void appStart(); private: Common *pCommon; enum TransMode{ AUTO, PORT, PASV }; //主机名 string hostName = ""; //端口号 int portNum = 0; //用户名 string userName = ""; //密码 string userPwd = ""; //操作命令socket SOCKET ctrlSock; //数据连接socket SOCKET dataConnSock; //数据传输socket SOCKET dataTransSock; //命令字符串 string requestString = ""; //接收的响应字符串 string responseString = ""; //响应码 unsigned int replyCode = 0; //主动模式端口号计数 int portDataConnCount = 0; //错误消息 string errorMsg = ""; //选择的连接模式 TransMode transModeVal = TransMode::AUTO; //网络组件初始化 bool initSocketLib(); //登录 bool login(); //退出登录 bool logout(); //菜单列表 void menuList(); //本地命令输入处理 void localInput(); //远程命令发送 bool sendToRemote(bool returnReplyCode=false); //接收远程响应 bool recvFromRemote(); //处理远程响应,返回响应码 bool getReplyCode(); //写日志文件 bool writeLogs(string msg); //显示错误 void error(string errorStr); //显示成功 void success(string sucStr); //按值选择数据传输模式 bool transModelSelect(); //PORT主动传输模式 bool portMode(); //允许远程连接本机 bool allowAccept(); //PASV连接模式 bool pasvMode(); //ftp参数初始化设置 bool initFtpParam(); //获取文件列表 bool getList(string dirPath); //编辑名称 bool setRename(string fileName); //上传文件 bool uploadFile(string filePath); //下载文件 bool downloadFile(string fileName); //删除文件 bool deleteFile(string fileName); }; #endif // INDEX_H
index.cpp 源文件
#include "index.h" //构造函数 Index::Index() { this->pCommon = new Common; if(!this->initSocketLib()){ this->error(this->errorMsg); exit(-1); } } //析构函数 Index::~Index() { closesocket(this->ctrlSock); closesocket(this->dataConnSock); closesocket(this->dataTransSock); WSACleanup(); } //程序开始 void Index::appStart() { cout << endl << "欢迎使用sindftp! (本软件出自www.sindsun.com)" <<endl << endl; if(!this->login()){ this->error(this->errorMsg); cout << "error code is " << replyCode << endl; Sleep(3000); system("cls"); this->appStart(); } this->menuList(); } //登录 bool Index::login() { cout << endl << setw(12) << "主机地址: " ; cin >> this->hostName; cout << endl << setw(12) << "端口号: "; cin >> this->portNum; cout << endl << setw(12) << "账号: "; cin >> this->userName; cout << endl << setw(12) << "密码: "; cin >> this->userPwd; cout << endl << endl; this->ctrlSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(this->ctrlSock == static_cast<unsigned>(SOCKET_ERROR)){ this->error("创建socket失败"); exit(-1); } sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(static_cast<unsigned short>(this->portNum)); addr.sin_addr.s_addr = inet_addr(this->hostName.c_str()); int connStatus = connect(this->ctrlSock, reinterpret_cast<sockaddr *>(&addr), sizeof (addr)); if(connStatus == -1){ this->errorMsg = "连接服务器失败"; return false; } if(!this->recvFromRemote() || this->replyCode != 220){ this->errorMsg = "连接服务器错误"; return false; } //进入登录 this->requestString = "USER " + this->userName; if(!sendToRemote(true) || this->replyCode != 331){ this->errorMsg = "用户名错误"; return false; } this->requestString = "PASS " + this->userPwd; if(!sendToRemote(true) || this->replyCode != 230){ this->errorMsg = "用户名或密码错误"; return false; } return true; } //菜单列表 void Index::menuList() { cout << "----------- sindftp ------------" << endl << endl; cout << "--------------------------------" << endl << endl; cout << "显示菜单\t menu" << endl << endl; cout << "切换目录\t cwd dirname" << endl << endl; cout << "文件列表\t list dirname" << endl << endl; //cout << "文件重命名\t rename filename" << endl << endl; cout << "上传文件\t upload filename" << endl << endl; cout << "下载文件\t download filename" << endl << endl; cout << "删除文件\t del filename" << endl << endl; cout << "帮助信息\t help" << endl << endl; cout << "清除屏幕\t clear" << endl << endl; cout << "退出平台\t quit" << endl << endl; cout << "--------------------------------" << endl; cout << endl; localInput(); } //本地命令输入处理 void Index::localInput() { requestString = ""; responseString = ""; errorMsg = ""; string userInputCmd = ""; cout << endl; cout << "input cmd ### " ; cin.sync(); getline(cin, userInputCmd); if(userInputCmd.length() < 1){ errorMsg = "请输入正确的命令!"; localInput(); return; } //分割命令与参数 string cmd = ""; string param = ""; bool cmdOver = false; for(char ch:userInputCmd){ if(ch == ' '){ cmdOver = true; continue; } if(cmdOver){ param += ch; }else{ cmd += ch; } } //处理命令 if(cmd == "help"){ //帮助 requestString = "HELP"; sendToRemote(true); }else if(cmd == "menu"){ //菜单 menuList(); }else if(cmd == "cwd"){ //改变目录 //改变数据目录 requestString = "CWD "+param; sendToRemote(true); }else if(cmd == "list"){ //列表 getList(param); }else if(cmd == "rename"){ // 重命名 }else if(cmd == "upload"){ //上传 uploadFile(param); }else if(cmd == "download"){ //下载 downloadFile(param); }else if(cmd == "del"){ //删除 deleteFile(param); }else if(cmd == "clear"){ //清除屏幕 system("cls"); menuList(); }else if(cmd == "quit"){ //退出 cout << "Bye bye!" <<endl; exit(-1); }else{ //errorMsg = "请输入正确的命令!"; //error(errorMsg); requestString = userInputCmd; sendToRemote(true); } localInput(); } //远程命令发送 bool Index::sendToRemote(bool returnReplyCode) { if(this->requestString.length() < 1){ this->errorMsg = "ftp命令不能为空"; return false; } cout << endl << setw(10) << "发送命令: " << setw(10) << requestString << endl; requestString += "\r\n"; requestString = this->pCommon->gbkToUtf8(requestString.c_str()); unsigned int cmdLength = requestString.length(); char *tmpCmdStr = new char[cmdLength]; memset(tmpCmdStr, 0, cmdLength); memcpy(tmpCmdStr, requestString.c_str(), cmdLength); int sendStatus = send(this->ctrlSock, tmpCmdStr, static_cast<int>(cmdLength), 0); delete [] tmpCmdStr; if(sendStatus == -1){ this->errorMsg = "请求失败"; return false; } if(returnReplyCode){ return recvFromRemote(); } return true; } //接收远程响应 bool Index::recvFromRemote() { if(this->ctrlSock == INVALID_SOCKET){ this->errorMsg = "服务已断开 "; return false; } char buf[MAX_MSG_SIZE]; int recvStatus = -1; responseString = ""; while(true){ memset(buf, 0, MAX_MSG_SIZE); Sleep(500); recvStatus = recv(this->ctrlSock, buf, MAX_MSG_SIZE, MSG_PARTIAL); //cout << "######### " << buf << endl; if(recvStatus > 0){ responseString = buf; break; } } if(responseString.length() == 0){ this->errorMsg = "接收数据失败"; return false; } this->success(responseString); return this->getReplyCode(); } //处理远程响应,返回响应码 bool Index::getReplyCode() { string tmpMsg = ""; tmpMsg = responseString.substr(0, 3); char charCode[3] = {}; memcpy(charCode, tmpMsg.c_str(), tmpMsg.length()); this->replyCode = static_cast<unsigned int>(atoi(charCode)); if( !(this->replyCode >= 1 && this->replyCode <= 1000)){ this->errorMsg = "无法获取到响应码"; return false; } return true; } //写日志文件 bool Index::writeLogs(string msg) { if(msg.length() == 0){ return false; } SYSTEMTIME sTime; GetLocalTime(&sTime); char fullTime[20]; memset(fullTime, 0, 20); sprintf(fullTime, "%4d-%02d-%02d %02d:%02d:%02d", sTime.wYear, sTime.wMonth, sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond); msg = "\r\n" + msg; msg = fullTime + msg; fstream fs; fs.open("d:/test/datasock_recv_logs.log", ios::app); fs << msg << endl; fs.close(); return true; } //显示错误 void Index::error(string errorStr) { if(errorStr.length() > 0){ cout << endl; cout << "Error: " << errorStr; cout << endl; } } //显示成功 void Index::success(string sucStr) { if(sucStr.length() > 0){ cout << endl; cout << "服务响应:" << endl; cout << sucStr; cout << endl; } } //按值选择数据传输模式 bool Index::transModelSelect() { if(transModeVal == TransMode::PORT){ return portMode(); }else if(transModeVal == TransMode::PASV){ return pasvMode(); }else{ return (pasvMode() || portMode()); } } //PORT主动传输模式 bool Index::portMode() { dataConnSock = socket(AF_INET, SOCK_STREAM, 0); if(dataConnSock == static_cast<unsigned>(SOCKET_ERROR)){ errorMsg = "初始化数据socket失败"; return false; } portDataConnCount += 1; sockaddr_in addr; int addrLength = sizeof (addr); int getsocknameStatus = getsockname(ctrlSock, reinterpret_cast<sockaddr *>(&addr), &addrLength); if(getsocknameStatus == -1){ errorMsg = "获取socket信息失败"; return false; } int localPort = ntohs(addr.sin_port) + portDataConnCount; //获取机地随机端口+1 addr.sin_family = AF_INET; addr.sin_port = htons(static_cast<u_short>(localPort)); //addr.sin_addr.s_addr = htonl(INADDR_ANY); int countBind = 0; int bindStatus = -1; while(bindStatus != 0){ bindStatus = bind(dataConnSock, reinterpret_cast<sockaddr *>(&addr), sizeof (addr)); countBind ++; if(countBind >= 10){ break; } } if(bindStatus == -1){ errorMsg = "绑定失败"; return false; } //主动模式下的监听一下要放到发送了请求数据命令之后 int listenStatus = listen(dataConnSock, 64); if(listenStatus == -1){ errorMsg = "监听失败"; return false; } char *ipAddr = new char[32]; memset(ipAddr, 0, 32); ipAddr = inet_ntoa(addr.sin_addr); //计算端口号 char charPortNum1[5]; char *portNum1 = itoa(localPort/256, charPortNum1, 10); char charPortNum2[5]; char *portNum2 = itoa(localPort%256, charPortNum2, 10); string portModelStr = ipAddr; portModelStr += ","; portModelStr += portNum1; portModelStr += ","; portModelStr += portNum2; //替换里面的特殊字符 for(char &c:portModelStr){ if(c == '.'){ c = ','; } } requestString = "PORT " + portModelStr; if(!sendToRemote(true)){ errorMsg = "发送port命令失败"; return false; } transModeVal = TransMode::PORT; success("PORT模式开启成功"); return true; } //允许远程连接本机 bool Index::allowAccept() { if(transModeVal != TransMode::PORT){ return true; } sockaddr_in acceptAddr; int acceptAddrLength = sizeof (acceptAddr); dataTransSock = accept(dataConnSock, reinterpret_cast<sockaddr *>(&acceptAddr), &acceptAddrLength); if(dataTransSock == static_cast<u_short>(INVALID_SOCKET)){ errorMsg = "接受请求失败"; return false; } return true; } //PASV连接模式 bool Index::pasvMode() { dataTransSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(dataTransSock == static_cast<u_short>(SOCKET_ERROR)){ errorMsg = "PASV socket初始化失败"; return false; } requestString = "PASV"; if(!sendToRemote(true) || replyCode != 227){ errorMsg = "PASV设置失败"; return false; } //处理字符串 char *newChar = strrchr(responseString.c_str(), '(') + 1; string tmpStr = newChar + 1; int spCount = 0; string p1 = ""; string p2 = ""; for(char ch:tmpStr){ if(ch == ')'){ break; } if(ch == ','){ spCount ++; continue; } if(spCount > 3){ if(spCount == 4){ p1 += ch; }else{ p2 += ch; } } } int dataPort = atoi(p1.c_str()) * 256 + atoi(p2.c_str()); cout << "p1 " << p1 << endl; cout << "p2 " << p2 << endl; cout << "port " << dataPort << endl; sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(static_cast<u_short>(dataPort)); addr.sin_addr.s_addr = inet_addr(hostName.c_str()); int ret = connect(dataTransSock, reinterpret_cast<sockaddr *>(&addr), sizeof (addr)); if(ret == -1){ errorMsg = "PASV连接服务器失败"; error(errorMsg); closesocket(dataTransSock); return false; } transModeVal = TransMode::PASV; success("PASV模式开启成功"); return true; } //获取文件列表 bool Index::getList(string dirPath) { if(!transModelSelect()){ error(errorMsg); return false; } requestString = "LIST "+dirPath; if(!sendToRemote(true) || replyCode != 150 ){ error(errorMsg); return false; } if(!allowAccept()){ error(errorMsg); return false; } char *recvData = new char[MAX_MSG_SIZE]; memset(recvData, 0 , MAX_MSG_SIZE); //如果是150,需要再接收一次数据发送结束的通知 int recvStatus = recv(dataTransSock, recvData, MAX_MSG_SIZE, 0); if(recvStatus == -1){ error("接收数据失败"); return false; } success(pCommon->utf8ToGbk(recvData)); writeLogs(recvData); success("写入日志文件成功"); delete [] recvData; return true; } //上传文件 bool Index::uploadFile(string filePath) { if(!transModelSelect()){ return false; } char *fileName = strrchr(filePath.c_str(), '/'); requestString = "STOR "; requestString += fileName +1; if(!sendToRemote(true)){ errorMsg = "命令发送失败"; return false; } if(!allowAccept()){ return false; } fstream fs; fs.open(filePath, ios::in); char buf[MAX_MSG_SIZE]; memset(buf, 0, MAX_MSG_SIZE); bool status = true; while(!fs.eof()){ fs.read(buf, MAX_MSG_SIZE); int storStatus = send(dataTransSock, buf, sizeof (buf), 0); if(storStatus == -1){ errorMsg = "上传文件异常"; status = false; break; } } closesocket(dataTransSock); if(!status){ return false; } return true; } //下载文件 bool Index::downloadFile(string fileName) { //先获取文件大小 requestString = "SIZE "; requestString += fileName; if(!sendToRemote(true)){ errorMsg = "命令发送失败"; return false; } int fileSize = atoi(responseString.substr(4).c_str()); if(!transModelSelect()){ return false; } requestString = "RETR " + fileName; if(!sendToRemote(true)){ errorMsg = "命令发送失败"; return false; } if(!allowAccept()){ errorMsg = "连接失败"; return false; } //开始进行下载动作 string localPath = "d:/test/d_" + fileName; fstream fs; fs.open(localPath, ios::app); int countFileSize = 0; char *buf = new char[MAX_MSG_SIZE]; while (countFileSize < fileSize) { memset(buf, 0, MAX_MSG_SIZE); recv(dataTransSock, buf, MAX_MSG_SIZE, 0); if(strlen(buf) < 1){ break; } fs << buf ; countFileSize += strlen(buf); } fs.close(); return true; } //删除文件 bool Index::deleteFile(string fileName) { if(!transModelSelect()){ return false; } requestString = "DELE " + fileName; if(!sendToRemote(true)){ errorMsg = "命令发送失败"; return false; } return true; } //网络组件初始化 bool Index::initSocketLib() { WSADATA wsa; WORD vCode = MAKEWORD(2, 2); int wsaStartupStatus = WSAStartup(vCode, &wsa); if(wsaStartupStatus == -1){ this->errorMsg = "初始化套接字库失败,请重试"; return false; } //检查套接字版本号 if(LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wHighVersion) != 2){ WSACleanup(); this->errorMsg = "套接字版本号不符合,请重新配置"; return false; } return true; }
common.h 头文件
/**************************** * title: 辅助处理的公共函数类 * description: 学习ftp协议与c++通信篇的一个学习项目,大致流程已经以及程序已经探索完毕,这个项目共享给学习需要的朋友,希望能帮助到一部分人 * datetime: 2020年6月15日16:11:19 * author: Sindsun * from: http://www.sindsun.com ****************************/ #ifndef COMMON_H #define COMMON_H #include <iostream> #include <string> #include <windows.h> #include <locale.h> #include <codecvt> #include <locale> using namespace std; class Common{ public: Common(); ~Common(); //将utf8转换为GBK,windows引入windows.h string utf8ToGbk(const char *src_str); //将gbk编码字符转换为utf8 string gbkToUtf8(const char *src_str); }; #endif // COMMON_H
common.cpp 源文件
#include "common.h" Common::Common() { } //将utf8转换为GBK,windows引入windows.h string Common::utf8ToGbk(const char *src_str) { int len = MultiByteToWideChar(CP_UTF8, 0, src_str, -1, nullptr, 0); wchar_t* wszGBK = new wchar_t[len + 1]; memset(wszGBK, 0, static_cast<unsigned int>(len * 2 + 2)); MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wszGBK, len); len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, nullptr, 0, nullptr, nullptr); char* szGBK = new char[len + 1]; memset(szGBK, 0, static_cast<unsigned int>(len + 1)); WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, nullptr, nullptr); string strTemp(szGBK); if (wszGBK) delete[] wszGBK; if (szGBK) delete[] szGBK; return strTemp; } //将gbk编码字符转换为utf8 string Common::gbkToUtf8(const char *src_str) { int len = MultiByteToWideChar(CP_ACP, 0, src_str, -1, nullptr, 0); wchar_t* wstr = new wchar_t[len + 1]; memset(wstr, 0, static_cast<unsigned int>(len + 1)); MultiByteToWideChar(CP_ACP, 0, src_str, -1, wstr, len); len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr); char* str = new char[len + 1]; memset(str, 0, static_cast<unsigned int>(len + 1)); WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, nullptr, nullptr); string strTemp = str; if (wstr) delete[] wstr; if (str) delete[] str; return strTemp; }
main.cpp 主程序文件
/**************************** * title: ftp学习程序 学习成果共享 * description: 学习ftp协议与c++通信篇的一个学习项目,大致流程已经以及程序已经探索完毕,这个项目共享给学习需要的朋友,希望能帮助到一部分人 * datetime: 2020年6月15日16:11:19 * author: Sindsun * from: http://www.sindsun.com ****************************/ #include <iostream> #include "index.h" using namespace std; int main() { system("mode con: cols=120 lines=500"); system("color 1f"); Index *index = new Index; index->appStart(); return 0; }
实现效果预览:
源码地址:
github https://github.com/sindsun/easy-ftp
存在问题的地方烦请朋友们邮件提醒一下,共同上进!
版权声明:
此文为本站源创文章[或由本站编辑从网络整理改编],
转载请备注出处:
[狂码一生]
https://www.sindsun.com/articles/16/137
[若此文确切存在侵权,请联系本站管理员进行删除!]
--THE END--