基于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

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

被折叠的 条评论
为什么被折叠?



