c/c++实现ftp客户端源码(超全超详细)

教程简介:

     本教程是我在学习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;
}


实现效果预览:

image.png


image.png


源码地址:

github    https://github.com/sindsun/easy-ftp


存在问题的地方烦请朋友们邮件提醒一下,共同上进!

版权声明: 此文为本站源创文章[或由本站编辑从网络整理改编],
转载请备注出处:
[狂码一生] https://www.sindsun.com/articles/16/137
[若此文确切存在侵权,请联系本站管理员进行删除!]


--THE END--