1.简介
FTP(File Transfer Protocol)是一种用于文件传输的标准协议,主要作用是在服务器和客户端之间实现文件的传输和共享。FTP 协议运行在 TCP 连接上,保证了文件传输的可靠性。FTP 通常使用两个端口,分别是 20 和 21。其中,21 号端口用于控制连接,客户端通过该端口向服务器发送命令和接收响应;20 号端口用于数据传输,客户端和服务器通过该端口进行文件的上传和下载。
FTP 协议支持多种模式,包括主动模式(Port 模式)和被动模式(Passive 模式)。在主动模式下,客户端主动打开一个临时端口,服务器接收连接并分配一个临时端口进行数据传输。在被动模式下,服务器主动打开一个临时端口,客户端接收连接并分配一个临时端口进行数据传输。
使用 FTP 协议传输文件需要先连接到 FTP 服务器,然后使用 put 或 get 命令来上传或下载文件。例如,使用 ftp 命令连接到 FTP 服务器后,可以使用 put 命令将本地文件上传到服务器:ftp> put localfile remotefile。同样地,使用 get 命令可以将服务器上的文件下载到本地:ftp> get remotefile localfile。这样就完成了文件传输的过程。
2.TCP协议
TCP/IP协议是一个网络通信协议,它负责在不同的计算机之间进行数据传输。TCP/IP协议是Transmission Control Protocol(TCP)和 Internet Protocol(IP)的组合。其中,TCP 负责发现传输的问题,一旦有问题就会发出重传信号,直到所有数据安全正确的传输到目的地,它提供了面向连接的可靠数据传输服务。而 IP 协议工作在网络层,负责对数据包进行编址和路由,它不负责数据的可靠性。TCP/IP 分层模型中,TCP 和 UDP 是传输层协议,IP 协议是网络层协议。TCP/IP 协议是互联网中最基本的通信协议。
3.FTP协议中的指令
连接 FTP 服务器:ftp [主机名]
显示 FTP 服务器上的目录内容:ls
进入 FTP 服务器上的目录:cd [目录名]
获取服务器上的文件:get [文件名]
下载文件:retr [文件名]
上传文件:put [文件名]
删除服务器上的文件:delete [文件名]
重命名服务器上的文件:rename [原文件名] [新文件名]
创建新目录:md [目录名]
删除服务器上的目录:rmd [目录名]
退出 FTP:quit
隐藏 FTP 服务器上的文件:mask [文件名]
显示 FTP 服务器上的文件属性:attr [文件名]
设置 FTP 服务器的时间和日期:set [时间日期]
4.一些接口的实现
1.连接ftp服务器,ftp默认端口号21
int ftp::ftp_connect(const char* ip)
{
int ftp_port = 21;
if (WSAStartup(MAKEWORD(2, 2), &Ws)!=0){
cout<<"create socket failed ::"<<GetLastError()<<endl;
return 0;
}
m_ctrl_socket = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(ftp_port);
memset(server_addr.sin_zero, 0x00, 8);
if (connect(m_ctrl_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
cout << "connect error" << endl;
}
else {
cout << "connect success" << endl;
}
//登录ftp
memset(cmd_send, 0x00, sizeof(cmd_send));
return 0;
}
2.登录ftp服务器
int ftp::ftp_login(char* user, char* password)
{
memset(cmd_send,0x00,sizeof(cmd_send));
sprintf(cmd_send,"USER %s\r\n",user);
ftp_sendcmd(cmd_send, sizeof(cmd_send));
memset(cmd_send, 0x00, sizeof(cmd_send));
sprintf(cmd_send, "PASS %s\r\n", password);
ftp_sendcmd(cmd_send, sizeof(cmd_send));
return 0;
}
3.发送命令
int ftp::ftp_sendcmd(char* cmd,int cmd_length)
{
int ret = send(m_ctrl_socket,cmd_send,cmd_length,0);
if (ret == -1) {
cout << "send cmd fail " << cmd << endl;
return -1;
}
else {
cout << "send cmd success " << cmd << endl;
return 0;
}
}
4.显示当前目录
int ftp::ftp_directory(char* buff)
{
memset(cmd_send, 0x00, sizeof(cmd_send));
strcpy(cmd_send,"PWD\r\n");
ftp_sendcmd(cmd_send,sizeof(cmd_send));
return 0;
}
5.列出目录的所有文件
int ftp::ftp_list(char* dir)
{
//设置被动模式
memset(cmd_send, 0x00, sizeof(cmd_send));
memset(cmd_recv, 0x00, sizeof(cmd_send));
strcpy(cmd_send, "PASV\r\n");
ftp_sendcmd(cmd_send, sizeof(cmd_send));
recv(m_ctrl_socket, cmd_recv, sizeof(cmd_recv), 0);
Sleep(100);
if (strncmp(cmd_recv, "220", 3) != 0) {
cout << "PASV fail" << endl;
return -1;
}
else {
cout << "PASV success" << endl;
}
memset(cmd_send, 0x00, sizeof(cmd_send));
sprintf(cmd_send, "NLST %s\r\n", dir);
if (ftp_sendcmd(cmd_send, sizeof(cmd_send))==-1) {
return -1;
}
char rec_buf[256];
int len = 0;
while (len = recv(m_data_socket, rec_buf, 256, 0)) {
rec_buf[len] = '\0';
cout << "read len:" << len << "data:" << rec_buf << endl;
}
vector<std::string> list;
return 0;
}
6.ftp上传文件
int ftp::ftp_upload(char* localfile, char* path, char*filename)
{
//设置被动模式
memset(cmd_send, 0x00, sizeof(cmd_send));
memset(cmd_recv, 0x00, sizeof(cmd_send));
strcpy(cmd_send, "PASV\r\n");
ftp_sendcmd(cmd_send,sizeof(cmd_send));
clock_t pasv_start_time, pasv_end_time;
pasv_start_time = clock();
//获取返回的端口号
char* getport = nullptr;
while (getport == nullptr) {
recv(m_ctrl_socket, cmd_recv, sizeof(cmd_recv), 0);
getport = strchr(cmd_recv, '(');
pasv_end_time = clock();
if (strncmp(cmd_recv, "220", 3) != 0) {
cout << "PASV fail" << endl;
if (pasv_end_time - pasv_start_time > 100) {
cout << "接受超时,程序退出!!!" << endl;
return -1;
}
}
else {
cout << "PASV success" << endl;
}
}
int pa, pb, data_port;
if (sscanf(getport, "(172,20,10,200,%d,%d", &pa, &pb) == -1) {
cout << "sscanf " << pa << " " << pb << "error" << endl;
}
data_port = pa * 256 + pb;
m_data_socket = socket(AF_INET,SOCK_STREAM,0);
//建立传数据通道连接
struct sockaddr_in send_addr;
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = inet_addr("172.20.10.200");
send_addr.sin_port = htons(data_port);//30312
if (connect(m_data_socket, (struct sockaddr*)&send_addr, sizeof(send_addr)) == SOCKET_ERROR) {
cout << "connect error" << endl;
closesocket(m_data_socket);
return -1;
}
else {
cout << "connect success" << endl;
}
memset(cmd_recv, 0x00, sizeof(cmd_send));
memset(cmd_send, 0x00, sizeof(cmd_send));
sprintf(cmd_send, "STOR %s%s\r\n", path,filename);
ftp_sendcmd(cmd_send, sizeof(cmd_send));
recv(m_ctrl_socket, cmd_recv, sizeof(cmd_recv), 0);
Sleep(100);
if (strncmp(cmd_recv, "150", 3) != 0) {
cout << "STOR fail" << endl;
return -1;
}
else {
cout << "STOR success" << endl;
}
ifstream file;
char sendbuf[16384] = { '\0' };
file.open(localfile, ios::binary);
if (!file.is_open()) {
cout << "fail open file" << endl;
return 0;
}
else {
file.seekg(0, file.end);//设置输入文件流的文件流指针位置
int size = file.tellg();
file.seekg(0, file.beg);//设置输入文件流的文件流指针位置
file.read(sendbuf, size);
if (send(m_data_socket, sendbuf, size, 0) < 0) {
cout << "send file fail " << sendbuf << endl;
return -1;
}else {
cout << "send file success " << sendbuf << endl;
}
}
file.close();
closesocket(m_data_socket);
return 0;
}
7.ftp下载文件
int ftp::ftp_download(char* remotefile, char* localfile)
{
memset(cmd_send, 0x00, sizeof(cmd_send));
strcpy(cmd_send,"PASV\r\n");
if (ftp_sendcmd(cmd_send,sizeof(cmd_send)) == -1)
return -1;
clock_t pasv_start_time, pasv_end_time;
pasv_start_time = clock();
//获取返回的端口号
int pa, pb, data_port;
char* getport = nullptr;
while (getport == nullptr) {
recv(m_ctrl_socket, cmd_recv, sizeof(cmd_recv), 0);
getport = strchr(cmd_recv, '(');
pasv_end_time = clock();
if (strncmp(cmd_recv, "220", 3) != 0) {
cout << "PASV fail" << endl;
if (pasv_end_time - pasv_start_time > 100) {
cout << "接受超时,程序退出!!!" << endl;
return -1;
}
}
else {
cout << "PASV success" << endl;
}
}
if (sscanf(getport, "(172,20,10,200,%d,%d", &pa, &pb) < 0) {
cout << "sscanf " << pa << " " << pb << "error" << endl;
}
data_port = pa * 256 + pb;
m_data_socket = socket(AF_INET, SOCK_STREAM, 0);
//建立传数据通道连接
struct sockaddr_in send_addr;
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = inet_addr("172.20.10.200");
send_addr.sin_port = htons(data_port);//30312
if (connect(m_data_socket, (struct sockaddr*)&send_addr, sizeof(send_addr)) == SOCKET_ERROR) {
cout << "connect error" << endl;
closesocket(m_data_socket);
return -1;
}
else {
cout << "connect success" << endl;
}
memset(cmd_send, 0x00, sizeof(cmd_send));
sprintf(cmd_send, "RETR %s\r\n",remotefile);
if (ftp_sendcmd(cmd_send, sizeof(cmd_send)) == -1)
return -1;
/**************************读取文件**************************************/
char rec_buf[1024] = { '\0' };
if (recv(m_data_socket, rec_buf, sizeof(rec_buf), 0) < 0) {
cout << "receive file fail " << rec_buf << endl;
return -1;
}
else {
cout << "receive file success " << rec_buf << endl;
fstream outfile;
outfile.open(localfile);
outfile << rec_buf << endl;
outfile.close();
}
closesocket(m_data_socket);
return 0;
}
8.关闭连接
int ftp::ftp_close()
{
memset(cmd_send, 0x00, sizeof(cmd_send));
strcpy(cmd_send, "QUIT\r\n");
if (ftp_sendcmd(cmd_send, sizeof(cmd_send)) == -1)
return -1;
closesocket(m_ctrl_socket);
closesocket(m_data_socket);
return 0;
}