ftpserver-ftp服务端c++

该代码实现了一个基于C++的FTP服务器,使用VS2017开发,利用TCP多线程处理客户端连接。服务器支持包括USER、PASS、QUIT、HELP、TYPE、SIZE、MDTM、PASV、RETR、MKD、ALLO、STOR等FTP命令。当客户端发送数据时,服务器可以接收并处理相应的请求,如传输文件、创建目录等。

基于c++、vs2017实现的ftp服务端,以tcp多线程的方式,用抓包工具的话,需要看tcp而非ftp,怎么将tcp封成ftp格式,目前没空,就没折腾了

#ifndef COMMONMETHOD_H
#define COMMONMETHOD_H

#include <string>
#include <vector>

/** str字符串中是否存在fmt字符串(不区分大小写) */
bool StringFind(const char *str, const char *fmt);
/** 获取文件最近修改时间:path-文件路径,fmt-时间格式 */
std::string GetFileMtime(const char *path, const char *fmt);
/** 字符串分割:ss-源字符串,split-分割字符串:返回分割后字符串集合 */
std::vector<std::string> StringSplit(const std::string &ss, const std::string &split);
/** 创建目录:menu-目录:成功返回0 */
int CreateMenu(const char *menu);
/** 获取目录所在磁盘剩余可用空间:menu-目录,FreeSpace-剩余可用空间:成功返回0 */
int GetMenuDiskFreeSpace(const std::string &menu, unsigned long long &FreeSpace);

#endif


#include "CommonMethod.h"
#include <algorithm>
#include <time.h>
#include <regex>
#include <io.h>
#include <direct.h>
#include <Windows.h>

#pragma warning(disable:4996)

bool StringFind(const char *str, const char *fmt)
{
    std::string ss_str = str;
    transform(ss_str.begin(), ss_str.end(), ss_str.begin(), ::tolower);//将ss_str字符串转换为小写
    std::string ss_fmt = fmt;
    transform(ss_fmt.begin(), ss_fmt.end(), ss_fmt.begin(), ::tolower);//将ss_fmt字符串转换为小写
    return (ss_str.find(ss_fmt) != std::string::npos);
}

std::string GetFileMtime(const char *path, const char *fmt)
{
    struct _stat st = { 0 };
    if (_stat(path, &st) == 0)
    {
        char ct[128] = { 0 };
        strftime(ct, sizeof(ct), fmt, localtime(&st.st_mtime));
        return ct;
    }
    return "";
}

std::vector<std::string> StringSplit(const std::string &ss, const std::string &split)
{
    std::vector<std::string> svss;
    if (ss != "")
    {
        std::regex reg(split);
        std::sregex_token_iterator pos(ss.begin(), ss.end(), reg, -1);
        decltype(pos) end;
        for (; pos != end; ++pos)
        {
            svss.push_back(pos->str());
        }
    }
    return svss;
}

int CreateMenu(const char *menu)
{
    std::vector<std::string> svss = StringSplit(menu, "/");
    std::string ss = "";
    for (unsigned int i = 0; i < svss.size(); i++)
    {
        ss = ss + svss.at(i) + "/";
        if (_access(ss.c_str(), 0) == 0)
        {
            continue;
        }

        if (_mkdir(ss.c_str()) != 0)
        {
            return -1;
        }
    }
    return 0;
}

int GetMenuDiskFreeSpace(const std::string &menu, unsigned long long &FreeSpace)
{
    std::string ssDisk = "";
    if (menu.find_first_of(":") == std::string::npos)
    {
        char buf[MAX_PATH] = { 0 };
        if (getcwd(buf, sizeof(buf)) == nullptr)
        {
            return -1;
        }

        ssDisk = buf;
        ssDisk = ssDisk.substr(0, ssDisk.find_first_of(":")) + ":";
    }
    else
        ssDisk = menu.substr(0, menu.find_first_of(":")) + ":";
    WCHAR wc[8] = { 0 };
    swprintf(wc, L"%S", ssDisk.c_str());
    LPCWSTR ls = wc;
    DWORD64 lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes;
    if (GetDiskFreeSpaceEx(ls, (PULARGE_INTEGER)&lpFreeBytesAvailableToCaller, (PULARGE_INTEGER)&lpTotalNumberOfBytes, (PULARGE_INTEGER)&FreeSpace) == false)
    {
        return -2;
    }

    return 0;
}


#ifndef TCPCOMMON_H
#define TCPCOMMON_H

/** 发送tcp数据(不适用于在结束之前可能遇到'\0'的字符串):TcpSocket-tcp套接字,data-数据,timeout-超时:返回0-成功,-1-select失败,-2-select超时,-3-发送失败 */
int SendTcpData(int TcpSocket, const char *data, long timeout = 1);
/** 发送tcp数据:TcpSocket-tcp套接字,data-数据,DataLen-数据长度,timeout-超时:返回0-成功,-1-select失败,-2-select超时,-3-发送失败 */
int SendTcpDataL(int TcpSocket, const char *data, int DataLen, long timeout = 1);
/** 创建tcp服务端:port-端口,ClientCnt-客户端数量:成功返回套接字,失败返回-1 */
int CreateTcpServer(const int &port, int ClientCnt);
/** 创建自动端口tcp服务端:port-端口(输出):成功返回套接字,失败返回-1 */
int CreateAutoPortTcpServer(unsigned short &port);
/** 接收tcp数据:TcpSocket-套接字,data-数据,DataLen-希望读取长度,RecvLen-实际读取长度,timeout-超时:返回0-成功,1-对端关闭,-1-参数错误,-2-select超时,-3-select失败,-4-接收失败 */
int RecvTcpData(int TcpSocket, char *data, int DataLen, int &RecvLen, long timeout = 1);

#endif


#include "TcpCommon.h"
#include <WinSock2.h>

int SendTcpData(int TcpSocket, const char *data, long timeout)
{
    int DataLen = strlen(data);
    fd_set __writefds;
    timeval tv = { timeout, 0 };
    int ir = 0;
    int SendLen = 0;
    while (true)
    {
        FD_ZERO(&__writefds);
        FD_SET(TcpSocket, &__writefds);
        ir = select(TcpSocket + 1, NULL, &__writefds, NULL, &tv);
        if (ir < 0)
        {
            return -1;
        }
        else if (ir == 0)
        {
            return -2;
        }

        ir = send(TcpSocket, data + SendLen, DataLen - SendLen, 0);
        if (ir == -1)
        {
            return -3;
        }

        SendLen += ir;
        if (SendLen == DataLen)
            break;
    }

    return 0;
}

int SendTcpDataL(int TcpSocket, const char *data, int DataLen, long timeout)
{
    fd_set __writefds;
    timeval tv = { timeout, 0 };
    int ir = 0;
    int SendLen = 0;
    while (true)
    {
        FD_ZERO(&__writefds);
        FD_SET(TcpSocket, &__writefds);
        ir = select(TcpSocket + 1, NULL, &__writefds, NULL, &tv);
        if (ir < 0)
        {
            return -1;
        }
        else if (ir == 0)
        {
            return -2;
        }

        ir = send(TcpSocket, data + SendLen, DataLen - SendLen, 0);
        if (ir == -1)
        {
            return -3;
        }

        SendLen += ir;
        if (SendLen == DataLen)
            break;
    }

    return 0;
}

int CreateTcpServer(const int &port, int ClientCnt)
{
    int FdServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (FdServer < 0)
    {
        return -1;
    }

    int opt = 1;
    if (setsockopt(FdServer, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) == -1)
    {
        closesocket(FdServer);
        return -1;
    }

    sockaddr_in server_addr = { 0 };
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port);
    if (bind(FdServer, (sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        closesocket(FdServer);
        return -1;
    }

    if (listen(FdServer, ClientCnt) < 0)
    {
        closesocket(FdServer);
        return -1;
    }

    return FdServer;
}

int CreateAutoPortTcpServer(unsigned short &port)
{
    int FdServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (FdServer < 0)
    {
        return -1;
    }

    sockaddr_in server_addr = { 0 };
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = 0;
    if (bind(FdServer, (sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        closesocket(FdServer);
        return -1;
    }

    sockaddr sa = { 0 };
    int sal = sizeof(sockaddr);
    if (getsockname(FdServer, &sa, &sal) != 0)
    {
        closesocket(FdServer);
        return -1;
    }

    port = ntohs(((sockaddr_in *)&sa)->sin_port);
    if (listen(FdServer, 1) < 0)
    {
        closesocket(FdServer);
        return -1;
    }

    return FdServer;
}

int RecvTcpData(int TcpSocket, char *data, int DataLen, int &RecvLen, long timeout)
{
    RecvLen = 0;
    if ((TcpSocket == -1) || (data == nullptr) || (DataLen <= 0))
    {
        return -1;
    }

    fd_set __readfds;
    timeval tv = { timeout, 0 };
    int ir = 0;
    while (true)
    {
        FD_ZERO(&__readfds);
        FD_SET(TcpSocket, &__readfds);
        ir = select(TcpSocket + 1, &__readfds, NULL, NULL, &tv);
        if (ir == 0)
        {
            return -2;
        }
        else if (ir < 0)
        {
            return -3;
        }
        else
        {
            ir = recv(TcpSocket, data + RecvLen, DataLen - RecvLen, 0);
            if (ir < 0)
            {
                return -4;
            }
            else if (ir == 0)
            {
                return 1;
            }

            RecvLen += ir;
            if (RecvLen == DataLen)
                break;
        }
    }

    return 0;
}


#ifndef FtpConnectingClient_H
#define FtpConnectingClient_H

#include <thread>

/** 传输模式 */
enum TransferMode
{
    /** 十进制 */
    ascii = 'A',
    /** 二进制 */
    image = 'I'
};

/** 连接中的客户端 */
class FtpConnectingClient
{
public:
    FtpConnectingClient();
    ~FtpConnectingClient();

    /** 开启:ConnSock-客户端套接字,RootDirectory-根目录,LocalIp-本机ip:返回0-成功,-1-发送220消息失败,-2-创建线程失败 */
    int start(int ClientSock, const std::string &RootDirectory, const std::string &LocalIp);
    /** 获取是否处于可以停止的状态 */
    bool GetCanBeStopped();

private:
    /** 客户端套接字 */
    int _ClientSock;
    /** 线程退出标识 */
    bool _ThreadExit;
    /** 线程 */
    std::thread _thread;
    /** 是否处于可以停止的状态 */
    bool _CanBeStopped;
    /** 用户名 */
    std::string _user;
    /** 密码 */
    std::string _pass;
    /** 传输模式 */
    TransferMode _TransferMode;
    /** 根目录 */
    std::string _RootDirectory;
    /** 本机IP */
    std::string _LocalIp;
    /** 客户端上传文件时候附加的文件大小 */
    long _AlloSize;

    /** 停止 */
    void stop();
    /** 线程函数 */
    void ThreadFunc();
};

#endif


#include "FtpConnectingClient.h"
#include <WinSock2.h>
#include <list>
#include "TcpCommon.h"
#include "CommonMethod.h"
#include <algorithm>
#include <WS2tcpip.h>

#pragma warning(disable:4996)

FtpConnectingClient::FtpConnectingClient()
{
    _ClientSock = -1;
    _ThreadExit = true;
    _CanBeStopped = true;
    _user = "";
    _pass = "";
    _TransferMode = ascii;
    _RootDirectory = "";
    _LocalIp = "";
    _AlloSize = 0;
}

FtpConnectingClient::~FtpConnectingClient()
{
    stop();
}

int FtpConnectingClient::start(int ClientSock, const std::string &RootDirectory, const std::string &LocalIp)
{
    printf("FtpConnectingClient::start:ClientSock=%d,RootDirectory=%s\n", ClientSock, RootDirectory.c_str());
    _ClientSock = ClientSock;
    int ir = SendTcpData(_ClientSock, "220 GTLI FTP Service\r\n");
    if (ir != 0)
    {
        printf("FtpConnectingClient::start:SendTcpData 220 fail:ir=%d\n", ir);
        return -1;
    }

    _RootDirectory = RootDirectory;
    _LocalIp = LocalIp;
    _CanBeStopped = false;
    _ThreadExit = false;
    _thread = std::thread(&FtpConnectingClient::ThreadFunc, this);
    if (_thread.joinable() == false)
    {
        _ThreadExit = true;
        _CanBeStopped = true;
        return -2;
    }

    return 0;
}

void FtpConnectingClient::stop()
{
    _CanBeStopped = true;
    _ThreadExit = true;
    if (_thread.joinable() == true)
    {
        _thread.join();
    }
    closesocket(_ClientSock);
}

bool FtpConnectingClient::GetCanBeStopped()
{
    return _CanBeStopped;
}

/** 从USER请求中解析出用户名 */
static void AnalyzeUserFromUserRequest(const char *UserRequest, std::string &user)
{
    user = "";
    std::string _UserRequest = UserRequest;
    std::string::size_type ssst_space = _UserRequest.find_first_of(' ');
    std::string::size_type ssst_line = _UserRequest.find_first_of("\r\n");
    if ((ssst_space != std::string::npos) && (ssst_line != std::string::npos) && (ssst_space < ssst_line))
    {
        user = _UserRequest.substr(ssst_space + 1, ssst_line - ssst_space - 1);
    }
}

/** 用户登录验证 */
static bool UserCheck(const std::string &user, const std::string &pass)
{
    if (user == "anonymous")
    {
        return true;
    }

    return false;
}

/** 获取文件大小:path-路径,TransferMode-传输模式:返回<=0表示失败 */
static long GetFileSize(const char *path, TransferMode TransferMode)
{
    long lr = -1;
    FILE *fr = fopen(path, TransferMode == ascii ? "r" : "rb");
    if(fr != nullptr)
    {
        if (fseek(fr, 0, SEEK_END) == 0)
        {
            lr = ftell(fr);
        }
        fclose(fr);
    }
    return lr;
}

void FtpConnectingClient::ThreadFunc()
{
    printf("FtpConnectingClient::ThreadFunc:thread %d begin\n", std::this_thread::get_id());
    fd_set fs;
    timeval tv = { 1, 0 };
    char CBuf[2048] = { 0 };
    int PasvFdServer = -1;
    unsigned short PasvPort = 0;
    while (_ThreadExit == false)
    {
        FD_ZERO(&fs);
        FD_SET(_ClientSock, &fs);
        int sr = select(_ClientSock + 1, &fs, NULL, NULL, &tv);
        if (sr < 0)
        {
            printf("FtpConnectingClient::ThreadFunc:select fail:sr=%d\n", sr);
            _CanBeStopped = true;
            break;
        }
        else if (sr > 0)
        {
            memset(CBuf, 0, sizeof(CBuf));
            int rr = recv(_ClientSock, CBuf, sizeof(CBuf), 0);
            if (rr == 0)//客户端关闭了连接
            {
                printf("FtpConnectingClient::ThreadFunc:recv 0 bytes,perhaps the client has shut down itself\n");
                _CanBeStopped = true;
                break;
            }
            else if (rr < 0)
            {
                printf("FtpConnectingClient::ThreadFunc:failed to receive message\n");
                _CanBeStopped = true;
                break;
            }
            else
            {
                printf("FtpConnectingClient::ThreadFunc:recv msg:%s\n", CBuf);
                if (StringFind(CBuf, "USER") == true)
                {
                    AnalyzeUserFromUserRequest(CBuf, _user);
                    int ir = SendTcpData(_ClientSock, "331 Password required\r\n");
                    if (ir != 0)
                    {
                        printf("FtpConnectingClient::ThreadFunc:SendTcpData 331 fail:ir=%d\n", ir);
                        _CanBeStopped = true;
                        break;
                    }
                }
                else if (StringFind(CBuf, "PASS") == true)
                {
                    AnalyzeUserFromUserRequest(CBuf, _pass);
                    if (UserCheck(_user, _pass) == false)
                    {
                        int ir1 = SendTcpData(_ClientSock, "530 user or password error.\r\n");
                        if (ir1 != 0)
                        {
                            printf("FtpConnectingClient::ThreadFunc:SendTcpData 530 fail:ir1=%d\n", ir1);
                            _CanBeStopped = true;
                            break;
                        }
                    }
                    else
                    {
    &nb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值