FTP文件传输优化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010872301/article/details/82459811

       FTP传输模式分为PORT(主动模式)和PASV(被动模式),被动模式的工作原理:FTP客户端连接到FTP服务器的21端口,发送用户名和密码登录,登录成功后要list列表或者读取数据时,发送PASV命令到FTP服务器, 服务器在本地随机开放一个端口(1024以上),然后把开放的端口告诉客户端, 客户端再连接到服务器开放的端口进行数据传输。如下图所示:

本文目标优化FTP长连接传输,提高ftp传输速度:

1、每次连接ftp服务器时push只发送图片数据,如果链表内有图片数据,不断开连接quitServer(),继续发送数据;

2、当FTP服务器异常断开时,delete ftp对象调用已分配的对象的析构函数,释放资源;

3、当重新连接ftp服务器时,在创建客服端FTP对象,执行构造函数CFTPManager()时实现连接ftp服务器,用户名、密码、传输方式任务!

4、编译ftp静态库生成libftp.a

源码

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include <sys/socket.h>  
#include <netinet/tcp.h> 
#include <signal.h>
#include <sys/types.h>          /* See NOTES */
#include <net/if.h>

#include "FtpClientLib.h"

using namespace std;


static int SplitString( std::string strSrc, std::list<std::string> &strArray , std::string strFlag){
	int pos = 1; 

	while((pos = (int)strSrc.find_first_of(strFlag.c_str())) > 0) {
		strArray.push_back(strSrc.substr(0 , pos));
		strSrc = strSrc.substr(pos + 1, strSrc.length() - pos - 1); 
	}

	if(!strSrc.empty()){
		strArray.push_back(strSrc);
	}

	return FTP_SUCC; 
}

std::string CompositString(std::string ip, int port){
	std::string dst;
	int pos = 1;
	
	while((pos = (int)ip.find_first_of(".")) > 0) {
		dst += ip.substr(0 , pos);
		dst += ",";
		ip = ip.substr(pos + 1, ip.length() - pos - 1); 
	}

	dst += ip;
	dst += ",";
	dst += std::to_string(port/256);
	dst += ",";
	dst += std::to_string(port%256);

	return dst;
}

int ParseString(std::list<std::string> strArray, unsigned long & nPort ,std::string & strServerIp){
	if (strArray.size() < 6 )
		return FTP_FAIL ;

	std::list<std::string>::iterator citor;
	citor = strArray.begin();
	strServerIp = *citor;
	strServerIp += ".";
	citor ++;
	strServerIp += *citor;
	strServerIp += ".";
	citor ++ ;
	strServerIp += *citor;
	strServerIp += ".";
	citor ++ ;
	strServerIp += *citor;
	citor = strArray.end();
	citor--;
	nPort = atol( (*citor).c_str());
	citor--;
	nPort += atol( (*(citor)).c_str()) * 256 ;

	return FTP_SUCC;
}

std::string GetLocalIp(){

	int sock_get_ip;
	char ipaddr[50];

	struct   sockaddr_in *sin;
	struct   ifreq ifr_ip;

	if ((sock_get_ip=socket(AF_INET, SOCK_STREAM, 0)) == -1){
		warn("socket create failse...GetLocalIp!/n");
		return "";
	}

	memset(&ifr_ip, 0, sizeof(ifr_ip));
	strncpy(ifr_ip.ifr_name, "eth0", sizeof(ifr_ip.ifr_name) - 1);
	if(ioctl(sock_get_ip, SIOCGIFADDR, &ifr_ip) < 0 )
	{
		return "";
	}
	sin = (struct sockaddr_in *)&ifr_ip.ifr_addr;
	strcpy(ipaddr,inet_ntoa(sin->sin_addr));
	close(sock_get_ip);

	return ipaddr;
}

CFTPManager::CFTPManager(const FTPParams &ftp_params){
	m_cmdSocket = 0;
	m_strResponse = "";
	m_commandStr = "";
	m_nCurrentCommand = 0;
	m_bLogin = false;
	m_strPath = "";
	cycle_cout = 0;

	signal(SIGPIPE, SIG_IGN);
	
	struct hostent *addr_info = NULL;
	addr_info = gethostbyname(ftp_params.ip.c_str());
	if(addr_info == NULL){
		warn("!!! can not fetch serverip from host!\n");
		m_strServerIP = "";
	}else{
		char str[32];
		inet_ntop(addr_info->h_addrtype, addr_info->h_addr, str, sizeof(str));
		m_strServerIP = str;
		printf("Got ServerIP %s\n", str);
	}

	m_nServerPort = ftp_params.port;
	m_strUserName = ftp_params.username;
	m_strPassWord = ftp_params.password;
	m_strPath = ftp_params.path;	

#if 1
		int ret = FTP_SUCC;
	
		printf("===ftp connect to server===\n");
	
		ret = connect2Server();
		if(ret < 0){ 
			warn("!!! ftp connect to server fail!\n");///
			//return FTP_FAIL;
		}
	
		ret = inputUserName();
		if(ret < 0){
			warn("!!! ftp cmd input username fail!\n");
			quitServer();
			//return FTP_FAIL;
		}
	
		ret = inputPassWord();
		if( ret < 0){
			warn("!!! ftp cmd input password fail!\n");
			quitServer();
			//return FTP_FAIL;
		}
	
		ret = setTransferMode(binary);
		if(ret < 0){
			warn("!!! ftp cmd set to binary mode fail!\n");
			quitServer();
			//return FTP_FAIL;
		}
#endif


}

CFTPManager::~CFTPManager(void){
	if(m_cmdSocket > 0){
		Close(m_cmdSocket);
		m_bLogin = false;
	}
}
// ! 连接服务器

FTP_API CFTPManager::connect2Server(){

	int m_retry_times = 0;
	int ret = FTP_SUCC;
	
M_RETRY:
	m_cmdSocket = socket(AF_INET, SOCK_STREAM, 0);
	if(m_cmdSocket <= 0){
		m_retry_times++;
		if(m_retry_times > 3){
			warn("!!! ftp cmd socket create retry limited!\n");
			return FTP_FAIL;
		}
		
		goto M_RETRY;
	}

	//set port reused
	int addruse = 1;
	setsockopt(m_cmdSocket, SOL_SOCKET, SO_REUSEADDR, &addruse, sizeof(addruse));

	//keep alive sets
	int keepAlive = 1; //open keepalive
	int keepIdle = 5; // timeout to start check.
	int keepInterval = 1; // time between to check
	int keepCount = 3; // check total times.

	setsockopt(m_cmdSocket, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
	setsockopt(m_cmdSocket, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
	setsockopt(m_cmdSocket, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
	setsockopt(m_cmdSocket, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

	if(m_strServerIP.empty()){
		warn("!!! do not get valid ip!\n");
		close(m_cmdSocket);
		m_cmdSocket = INVALID_SOCKET;
		return FTP_FAIL;
	}

	if(m_nServerPort <= 0){
		m_nServerPort = FTP_DEFAULT_PORT;
		warn("!!! connect to server:%s,use default port:%d\n",m_strServerIP.c_str(),m_nServerPort);			
	}

	if (Connect(m_cmdSocket, m_strServerIP, m_nServerPort) < 0){		
		warn("!!! connect to server fail!\n");
		m_cmdSocket = INVALID_SOCKET;
		return FTP_FAIL;
	}
	
	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n", __LINE__, m_strResponse.c_str());

	ret = parseResponse(m_strResponse);
	if(ret != 220){//ready
		warn("!!! connect to server resp fail,rsp:%d\n",ret);
		shutdown(m_cmdSocket, SHUT_RDWR);
		close(m_cmdSocket);
		m_cmdSocket = INVALID_SOCKET;
		return FTP_FAIL;
	}

	return ret;
}

// !输入用户名
FTP_API CFTPManager::inputUserName(){
	int ret = FTP_SUCC;

	if(m_strUserName.empty()){
		warn("!!! do not get valid username!\n");	
		return FTP_FAIL;
	}
	
	std::string strCommandLine = composeCommand(FTP_COMMAND_USERNAME, m_strUserName);
	if (Send(m_cmdSocket, strCommandLine) < 0){
		warn("!!! send user name to server fail!\n");
		return FTP_FAIL;
	}

	m_strResponse = serverResponse(m_cmdSocket);
	ret = parseResponse(m_strResponse);
	if(ret == 220){
		m_strResponse = serverResponse(m_cmdSocket);
		ret = parseResponse(m_strResponse);
	}
	
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	if(ret != 331){//request password
		warn("!!! after input user name,rsp:%d!\n", ret);
		return FTP_FAIL;
	}

	return ret;
}

// !输入密码
FTP_API CFTPManager::inputPassWord(){

	int ret = FTP_SUCC;
	
	if(m_strPassWord.empty()){
		warn("!!! do not get valid password!\n");	
		return FTP_FAIL;
	}
	
	std::string strCmdLine = composeCommand(FTP_COMMAND_PASSWORD, m_strPassWord);
	if (Send(m_cmdSocket, strCmdLine) < 0){
		warn("!!! send password to server fail!line:%d\n", __LINE__);
		return FTP_FAIL;
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());

	ret = parseResponse(m_strResponse);
	if(ret != 230){//login success
		warn("!!! after input password,rsp:%d!\n", ret);
		return FTP_FAIL;
	}

	m_bLogin = true;

	return ret;
}
// !退出FTP

FTP_API CFTPManager::quitServer(void){
	
	std::string strCmdLine = composeCommand(FTP_COMMAND_QUIT, "");
	if (Send(m_cmdSocket, strCmdLine) < 0){
		warn("!!! send quit command to server fail!\n");
		Close(m_cmdSocket);
		m_bLogin = false;
		
		return FTP_FAIL;
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	Close(m_cmdSocket);
	m_bLogin = false;
	return FTP_SUCC;
}

// !设置传输格式 2进制
FTP_API CFTPManager::setTransferMode(type mode){
	std::string strCmdLine;
	int ret = FTP_SUCC;
	
	switch (mode)
	{
		case binary:
			strCmdLine = composeCommand(FTP_COMMAND_TYPE_MODE, "I");
			break;
		case ascii:
			strCmdLine = composeCommand(FTP_COMMAND_TYPE_MODE, "A");
			break;
		default:
			return FTP_FAIL;
	}

	if (Send(m_cmdSocket, strCmdLine.c_str()) < 0){
		warn("!!! send type mode cmd to server fail!\n");
		return FTP_FAIL;
	}
	
	m_strResponse  = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());

	ret = parseResponse(m_strResponse);
	if(ret != 200){//success
		return FTP_FAIL;
	}

	return ret;
}
// !设置为被动模式

const std::string CFTPManager::Pasv(){
	std::string strCmdLine = composeCommand(FTP_COMMAND_PSAV_MODE, "");
	
	if (Send(m_cmdSocket, strCmdLine.c_str()) < 0){
		warn("!!! send passv cmd to server fail!\n");
		return "";
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	return m_strResponse;
}

const std::string CFTPManager::Port(std::string local_ip, int data_port){

	std::string buff = CompositString(local_ip, data_port);
	std::string strCmdLine = composeCommand(FTP_COMMAND_PORT_MODE, buff);
	if (Send(m_cmdSocket, strCmdLine.c_str()) < 0){
		warn("!!! send port cmd to server fail!\n");
		return "";
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	return m_strResponse;
}

FTP_API CFTPManager::CD(const std::string &path){
	std::string strCmdLine = composeCommand(FTP_COMMAND_CHANGE_DIRECTORY, path);

	if (Send(m_cmdSocket, strCmdLine) < 0){
		warn("!!! send cd cmd to server fail!\n");
		Close(m_cmdSocket);
		return FTP_FAIL;
	}
		
	m_strResponse = serverResponse(m_cmdSocket);
	
	trace("@@@@line: %d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	return parseResponse(m_strResponse);
}

// ! 创建目录/文件夹
FTP_API CFTPManager::CreateDirectory(const std::string &strRemoteDir){
	
	std::string strCmdLine = composeCommand(FTP_COMMAND_CREATE_DIRECTORY, strRemoteDir);

	if (Send(m_cmdSocket, strCmdLine) < 0){
		warn("!!! send mkdir cmd to server fail!\n");
		return FTP_FAIL;
	}
	
	m_strResponse = serverResponse(m_cmdSocket);

	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	return parseResponse(m_strResponse);
}
// !重命名

FTP_API CFTPManager::Rename(const std::string &strRemoteFile, const std::string &strNewFile){
	int ret = FTP_SUCC;
	std::string strCmdLine = composeCommand(FTP_COMMAND_RENAME_BEGIN, strRemoteFile);
	if((ret = Send(m_cmdSocket, strCmdLine)) < 0){
		warn("!!! send rename from cmd fail!\n");
		return FTP_FAIL;
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());	
	ret = parseResponse(m_strResponse);
	if(ret != 350){
		if(m_strResponse.find("350") == m_strResponse.npos){
			m_strResponse = serverResponse(m_cmdSocket);
			if(m_strResponse.find("350") == m_strResponse.npos){
				warn("!!! rename from cmd fail,rsp:%d!\n",ret);
				return FTP_FAIL;
			}
		}
	}

	if((ret = Send(m_cmdSocket, composeCommand(FTP_COMMAND_RENAME_END, strNewFile))) < 0){
		warn("!!! send rename name to cmd fail!\n");
		return FTP_FAIL;		
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line: %d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	ret = parseResponse(m_strResponse);
	if(ret != 250){
		warn("!!! rename to cmd fail,rsp:%d!\n",ret);
		return FTP_FAIL;
	}

	return FTP_SUCC;
}

	const std::string CFTPManager::Nlist(const std::string &path, int cmd_type){

	int ret = FTP_SUCC;
	int data_fd = INVALID_SOCKET;

	data_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(data_fd < 0){
		warn("!!! list cmd try create data socket fail!\n");
		return "";
	}

	//set port reused
	int addruse = 1;
	setsockopt(data_fd, SOL_SOCKET, SO_REUSEADDR, &addruse, sizeof(addruse));

	if (createDataLink(data_fd) < 0){
		warn("!!! list CreateDataLink fail!\n");
		Close(data_fd);
		return "";
	}
	
	std::string strCmdLine = composeCommand(cmd_type, path);
	if (Send(m_cmdSocket, strCmdLine) < 0){
		Close(data_fd);
		warn("!!! send list cmd to server fail!\n");
		return "";
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line: %d, Response: %s\n",  __LINE__, m_strResponse.c_str());
	ret = parseResponse(m_strResponse);
	if((ret != 150) && (ret != 125)){
		Close(data_fd);
		warn("!!! lis command fail,rsp:%d!\n",ret);
		return "";
	}

	char data[16*1024] = {0};
	ret = getData(data_fd, data, 16*1024);
	if(ret > 0){
		Close(data_fd);

		m_strResponse = serverResponse(m_cmdSocket);
		trace("@@@@line: %d, Response: %s\n",  __LINE__, m_strResponse.c_str());
		
		std::string list_rsp = data;
		return list_rsp;
	}

	warn("!!! list cmd get data fail!\n");
	Close(data_fd);

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line: %d, Response: %s\n",  __LINE__, m_strResponse.c_str());

	return "";
}

FTP_API CFTPManager::DelTemps(std::string path){
#if USE_LIST
	string list_rsp  = Nlist(path, FTP_COMMAND_DIR);
	if(list_rsp.empty()){
		return FTP_SUCC;
	}
	
	while(1)  {  
		int nIdx = list_rsp.find("\r\n");  
		if(nIdx == -1){
			break;
		}

		string strListDataTemp = list_rsp.substr(0 , nIdx);
		if(strListDataTemp.at(0) == 'd'){//dir
			list_rsp = list_rsp.substr(nIdx + 2, list_rsp.length() - nIdx - 2);
			continue;  
		}  

		int i = 0;  
		while(1)  {
			int npos = strListDataTemp.find(" ");  
			strListDataTemp = strListDataTemp.substr(npos + 1, strListDataTemp.length() - npos - 1);
			strListDataTemp.erase(0,strListDataTemp.find_first_not_of(' ')); 
			strListDataTemp.erase(0,strListDataTemp.find_first_not_of('\t')); 

			if(++i == 8)
			break;
		}  

		int tpos = strListDataTemp.find(".tmp"); 
		if(tpos > 0){
			DelFile(strListDataTemp);
		}

		list_rsp = list_rsp.substr(nIdx + 2, list_rsp.length() - nIdx - 2);
	} 
#else
	string list_rsp  = Nlist(path, FTP_COMMAND_NLIST);
		if(list_rsp.empty()){
		return FTP_SUCC;
	}

	while(1) {
		int nIdx = list_rsp.find("\r\n");
		if(nIdx == -1){
			break;
		}

		string strListDataTemp = list_rsp.substr(0 , nIdx);
		int tpos = strListDataTemp.find(".tmp");
		if(tpos > 0){
			DelFile(strListDataTemp);
		}

		list_rsp = list_rsp.substr(nIdx + 2, list_rsp.length() - nIdx - 2);
	}
#endif
	return FTP_SUCC;
}

FTP_API CFTPManager::DelFile(const std::string &strRemoteFile){
	int ret = FTP_SUCC;
	printf("start delete tmp file:%s\n", strRemoteFile.c_str());
	std::string strCmdLine = composeCommand(FTP_COMMAND_DELETE_FILE, strRemoteFile);
	if((ret = Send(m_cmdSocket, strCmdLine)) < 0){
		warn("!!! send dele cmd fail!\n");
		return FTP_FAIL;
	}

	m_strResponse = serverResponse(m_cmdSocket);
	trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());

	ret = parseResponse(m_strResponse);
	if(ret != 250){
		warn("!!! dele command fail,rsp:%d!\n",ret);
		return FTP_FAIL;
	}

	return ret;
}

long CFTPManager::GetFileLen(const std::string &strRemoteFile){

	int ret = FTP_SUCC;
	std::string strCmdLine = composeCommand(FTP_COMMAND_FILE_SIZE, strRemoteFile);  

	if (Send(m_cmdSocket, strCmdLine) < 0)  {
		warn("!!! send size cmd fail!\n");
		return FTP_FAIL;
	}  
  
	m_strResponse = serverResponse(m_cmdSocket);  
	trace("@@@@Response: %s\n", m_strResponse.c_str());  

	ret = parseResponse(m_strResponse);
	if (ret == 213) {  
		std::string strData = m_strResponse.substr(4);  
		trace("@@@file len return string: %s\n", strData.c_str());  
		unsigned long len = atol(strData.c_str());  

		return len;  
	}  

	return FTP_FAIL;  
} 
// !关闭连接

void CFTPManager::Close(int &sock){
	shutdown(sock, SHUT_RDWR);
	close(sock);
	sock = INVALID_SOCKET;
}

FTP_API CFTPManager::ConnectServer(void){
	
	int ret = FTP_SUCC;
	
	printf("===ftp connect to server===\n");
	
	ret = connect2Server();
	if(ret < 0){ 
		warn("!!! ftp connect to server fail!\n");///
		return FTP_FAIL;
	}
	
	ret = inputUserName();
	if(ret < 0){
		warn("!!! ftp cmd input username fail!\n");
		quitServer();
		return FTP_FAIL;
	}
	
	ret = inputPassWord();
	if( ret < 0){
		warn("!!! ftp cmd input password fail!\n");
		quitServer();
		return FTP_FAIL;
	}
	
	ret = setTransferMode(binary);
	if(ret < 0){
		warn("!!! ftp cmd set to binary mode fail!\n");
		quitServer();
		return FTP_FAIL;
	}

}


FTP_API CFTPManager::Push(std::string FilePrefix, char *data, int size){

	std::string strCmdLine;
	int ret = FTP_SUCC;

	if(data == NULL || size <= 0){
		return FTP_FAIL;
	}

	trace("@@@ push file:%s\n", FilePrefix.c_str());
	printf("===push file to fail\n");


#if 0
	ret = connect2Server();
	if(ret < 0){ 
		warn("!!! ftp connect to server fail!\n");
		return FTP_FAIL;
	}

	ret = inputUserName();
	if(ret < 0){
		warn("!!! ftp cmd input username fail!\n");
		quitServer();
		return FTP_FAIL;
	}

	ret = inputPassWord();
	if( ret < 0){
		warn("!!! ftp cmd input password fail!\n");
		quitServer();
		return FTP_FAIL;
	}

	ret = setTransferMode(binary);
	if(ret < 0){
		warn("!!! ftp cmd set to binary mode fail!\n");
		quitServer();
		return FTP_FAIL;
	}
#endif

	if(!m_strPath.empty()){
		printf("@@@start create dst dir:%s\n",m_strPath.c_str());
		std::list<std::string> strArray ;
		std::list<std::string>::iterator ite;

		SplitString(m_strPath , strArray , "/" );
		ite = strArray.begin();
		while(ite != strArray.end()){
			if(((std::string)*ite).empty()){
				break;
			}

			ret = CD(*ite);
			if(ret < 0){
				warn("!!! ftp cmd change dir fail!dir:%s\n", ((std::string)*ite).c_str());
				quitServer();
				return FTP_FAIL;
			}
			
			if(ret == 550){//file can not access
				ret = CreateDirectory(*ite);
				if(ret < 0 || ret != 257){//create success
					warn("!!! ftp cmd create dir fail!dir:%s\n", ((std::string)*ite).c_str());
					quitServer();
					return FTP_FAIL;
				}

				ret = CD(*ite);
				if(ret < 0 || ret != 250){//change success
					warn("!!! ftp change dir fail!dir:%s\n", ((std::string)*ite).c_str());
					quitServer();
					return FTP_FAIL;
				}
			}
			
			ite++;
			}
	}

	if(cycle_cout == 100){
		DelTemps("./"); //delete old tmp files
		cycle_cout = 0;
	}
	cycle_cout++;

	int retry_times = 0;
	int data_fd = INVALID_SOCKET;
D_RETRY:
	data_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(data_fd < 0){
		retry_times++;
		if(retry_times > 3){
			warn("!!! try create data socket too much times, quit!\n");
			quitServer();
			return FTP_FAIL;
		}
		
		goto D_RETRY;
	}
	
	if (createDataLink(data_fd) < 0){
		warn("!!! CreateDataLink fail!\n");
		Close(data_fd);
		
		quitServer();
		return FTP_FAIL;
	}

	string processing_file = FilePrefix + ".tmp";
	strCmdLine = composeCommand(FTP_COMMAND_UPLOAD_FILE, processing_file);
	if (Send(m_cmdSocket, strCmdLine) < 0){
		warn("!!! send upload cmd to server fail!line:%d\n", __LINE__);
		Close(data_fd);
		m_strResponse = serverResponse(m_cmdSocket);
		trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
		
		quitServer();
		return FTP_FAIL;
	}
	trace("@@@@line: %d, Response: %s\n",  __LINE__, serverResponse(m_cmdSocket).c_str());
	ret = parseResponse(m_strResponse);
	if((ret != 150) && (ret != 125)){
		warn("!!! upload cmd rsp fail!rsp:%d\n", ret);
		Close(data_fd);
		m_strResponse = serverResponse(m_cmdSocket);
		trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());

		quitServer();
		return FTP_FAIL;
	}
	
	if (Send(data_fd, (char *)data, size) < 0){
		warn("!!! send data to server fail!line:%d\n", __LINE__);

		Close(data_fd);
		m_strResponse = serverResponse(m_cmdSocket);
		trace("@@@@line:%d, Response: %s\n",  __LINE__, m_strResponse.c_str());
		
		quitServer();
		return FTP_FAIL;
	}
	
	Close(data_fd);
	trace("@@@@line: %d, Response: %s\n",  __LINE__, serverResponse(m_cmdSocket).c_str());
	ret = parseResponse(m_strResponse);
	if(ret != 226){
		warn("!!! close data link rsp fail!rsp:%d\n", ret);
	}

	string final_file = FilePrefix + ".jpg";
	int rename_cnt = 0;
Rename_retry:
	ret = Rename(processing_file, final_file);
	if(ret < 0){
		rename_cnt++;
		if(rename_cnt > 2){
			warn("!!! rename fail retry limited,quit!\n");

			quitServer();
			return FTP_FAIL;
		}

		goto Rename_retry;
	}

	//quitServer();
	return size;
}
// !合成发送到服务器的命令

const std::string CFTPManager::composeCommand(const unsigned int command, const std::string &strParam){
	if (command < FTP_COMMAND_BASE || command > FTP_COMMAND_END){
		return "";
	}

	std::string strCommandLine;

	m_nCurrentCommand = command;
	m_commandStr.clear();

	switch (command)
	{
	case FTP_COMMAND_USERNAME:
		strCommandLine = "USER ";
		break;
	case FTP_COMMAND_PASSWORD:
		strCommandLine = "PASS ";
		break;
	case FTP_COMMAND_QUIT:
		strCommandLine = "QUIT ";
		break;
	case FTP_COMMAND_CURRENT_PATH:
		strCommandLine = "PWD ";
		break;
	case FTP_COMMAND_TYPE_MODE:
		strCommandLine = "TYPE ";
		break;
	case FTP_COMMAND_PSAV_MODE:
		strCommandLine = "PASV ";
		break;
	case FTP_COMMAND_DIR:
		strCommandLine = "LIST ";
		break;
	case FTP_COMMAND_CHANGE_DIRECTORY:
		strCommandLine = "CWD ";
		break;
	case FTP_COMMAND_DELETE_FILE:
		strCommandLine = "DELE ";
		break;
	case FTP_COMMAND_DELETE_DIRECTORY:
		strCommandLine = "RMD ";
		break;
	case FTP_COMMAND_CREATE_DIRECTORY:
		strCommandLine = "MKD ";
		break;
	case FTP_COMMAND_RENAME_BEGIN:
		strCommandLine = "RNFR ";
		break;
	case FTP_COMMAND_RENAME_END:
		strCommandLine = "RNTO ";
		break;
	case FTP_COMMAND_FILE_SIZE:
		strCommandLine = "SIZE ";
		break;
	case FTP_COMMAND_DOWNLOAD_FILE:
		strCommandLine = "RETR ";
		break;
	case FTP_COMMAND_DOWNLOAD_POS:
		strCommandLine = "REST ";
		break;
	case FTP_COMMAND_UPLOAD_FILE:
		strCommandLine = "STOR ";
		break;
	case FTP_COMMAND_APPEND_FILE:
		strCommandLine = "APPE ";
		break;
	case FTP_COMMAND_PORT_MODE:
		strCommandLine = "PORT ";
		break;
	case FTP_COMMAND_NOOP:
		strCommandLine = "NOOP ";
		break;			
	case FTP_COMMAND_NLIST:
		strCommandLine = "NLST ";
		break;

	default :
		break;
	}

	strCommandLine += strParam;
	strCommandLine += "\r\n";

	m_commandStr = strCommandLine;
	trace("@@@composeCommand: %s", m_commandStr.c_str());

	return m_commandStr;
}

//建立连接
FTP_API CFTPManager::Connect(int socketfd, const std::string &serverIP, unsigned int nPort){
	if (socketfd == INVALID_SOCKET){
		return FTP_FAIL;
	}

	unsigned long ul = 1;
	ioctl(socketfd, FIONBIO, &ul); //设置为非阻塞模式,否则ftp地址不存在时,connect会阻塞

	int error = FTP_FAIL;
	int len = sizeof(int);
	struct sockaddr_in  addr;
	bool ret = false;
	timeval stime;
	fd_set  set;

	memset(&addr, 0, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_port	= htons(nPort);
	addr.sin_addr.s_addr = inet_addr(serverIP.c_str());
	bzero(&(addr.sin_zero), 8);

	trace("Address: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
	if (connect(socketfd, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == -1){   //若直接返回 则说明正在进行TCP三次握手
	
		stime.tv_sec = 2; //设置超时时间
		stime.tv_usec = 0;
		FD_ZERO(&set);
		FD_SET(socketfd, &set);

		if (select(socketfd + 1, NULL, &set, NULL, &stime) > 0){   ///在这边等待 阻塞 返回可以读的描述符 或者超时返回0  或者出错返回-1
			getsockopt(socketfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t*)&len);
			if (error == 0){
				ret = true;
			} else {
				warn("!!! socket connect error:%d\n", error);////
				ret = false;
			}
		}
	} else {
		trace("Connect Immediately!!!\n");
		ret = true;
	}

	if (!ret){
		shutdown(socketfd, SHUT_RDWR);
		close(socketfd);
		warn("!!! cannot connect server!!\n");/////
		return FTP_FAIL;
	}

	// use linger, limit the waiting time when close ftp(not neccessory)
	struct linger stLinger = { 1, 2 };
	struct linger {
		int l_onoff; /* 0 denotes that linger is disabled, else means enabled */
		int l_linger; /* linger time, in second. the value must be >= 2 */
	};
	
	/*	* linger :徘徊的意思。SO_LINGER:表示经历time_wait阶段,且时间是stLinger中第二个参数指定的值。	
	* 当套接口关闭时内核将拖延一段时间(由l_linger决定)。如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直到	
	* (a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)或	
	* (b)延迟时间到。	
	* */
	int flags = setsockopt(socketfd, SOL_SOCKET, SO_LINGER, &stLinger, sizeof(struct linger));
	if (flags == FTP_FAIL)
	{
		shutdown(socketfd, SHUT_RDWR);
		close(socketfd);
		return FTP_FAIL;
	}

	return FTP_SUCC;
}

// ! 返回服务器信息

const std::string CFTPManager::serverResponse(int sockfd){
	if (sockfd == INVALID_SOCKET){
		return "";
	}
	
	int nRet = FTP_FAIL;
	char buf[MAX_PATH] = {0};
	m_strResponse.clear();

	if((nRet = getData(sockfd, buf, MAX_PATH)) > 0){
		buf[MAX_PATH - 1] = '\0';
		m_strResponse = buf;
		return m_strResponse;
	}

	warn("!!! socket getData fail!\n");
	return "";
}
// !获取服务器数据

FTP_API CFTPManager::getData(int fd, char *strBuf, unsigned long length){

	timeval stime;
	fd_set	readfd;
	int nLen = 0;
	int ret = FTP_SUCC;
	
	if(strBuf == NULL){
		warn("!!! socket rcv buff is NULL!\n");
		return FTP_FAIL;
	}

	if (fd <= 0){
		warn("!!! socket fd is invalid!\n");
		return FTP_FAIL;
	}
	memset(strBuf, 0, length);

	int retry_cnt = 0;
	stime.tv_sec = 1;
	stime.tv_usec = 0;

Rcv_Retry:
	FD_ZERO( &readfd );
	FD_SET(fd, &readfd );
	ret = select(fd + 1, &readfd, 0, 0, &stime);
	if (ret > 0){
		if ((nLen = recv(fd, strBuf, length, 0)) > 0){
			return nLen;
		} else {
			warn("!!! socket rcv none data or error! ret:%d\n",nLen);
			return FTP_FAIL;
		}
	} else if (ret < 0){
		warn("!!! socket rcv mode select error!\n");
		return FTP_FAIL;
	} else {
		retry_cnt++;
		if(retry_cnt <= 3){
			warn("!!! socket rcv retry!\n");
			goto Rcv_Retry;
		}
	}
	
	return FTP_FAIL;
}
// !发送命令

FTP_API CFTPManager::Send(int fd, const std::string &cmd){

	if (fd <= 0){
		return FTP_FAIL;
	}

	printf("=1==send===\n");
	return Send(fd, cmd.c_str(), cmd.length());
}

FTP_API CFTPManager::Send(int fd, const char *data, const size_t len){

	int ret = FTP_SUCC;
	fd_set  writefd;
	timeval timeout;
	timeout.tv_sec  = 2;
	timeout.tv_usec = 0;

	if((FTP_COMMAND_USERNAME != m_nCurrentCommand) 
		&&(FTP_COMMAND_PASSWORD != m_nCurrentCommand)
		&&(!m_bLogin)){
		warn("!!! has not login,can not send data!\n");
		return FTP_FAIL;
	}
	printf("=2==send===\n");

	trace("@@@ socket need send:%d\n", len);
	size_t nlen = len;
	while(nlen > 0){
		FD_ZERO(&writefd);  
		FD_SET(fd, &writefd);
		ret = select(fd + 1, 0, &writefd , 0 , &timeout);
		if(ret > 0){
			int nSendLen = send(fd, data , (int)nlen , 0);
			trace("@@@ socket send return %d\n", nSendLen);
			if(nSendLen < 0){
				warn("!!! socket send return error! return value:%d\n", nSendLen);
				return FTP_FAIL; 
			}
			
			nlen = nlen - nSendLen;
			data +=  nSendLen;
		} else if(ret < 0){
			warn("!!! socket send select error!");
			return FTP_FAIL;
		} else {
			warn("!!! socket send select timeout!\n");
			continue;
		}
	}

	return FTP_SUCC;
}


FTP_API CFTPManager::createDataLink(int data_fd){
	int ret = FTP_SUCC;
	
	if(data_fd <= 0){
		return FTP_FAIL;
	}

	std::string strData;
	unsigned long nPort = 0 ;
	std::string strServerIp ; 
	std::list<std::string> strArray ;
	printf("=1==createDataLink\n");

	std::string parseStr = Pasv();
	if (parseStr.size() <= 0){
		warn("!!! pasv return null response!\n");
		return FTP_FAIL;
	}

	ret = parseResponse(parseStr);
	if(ret == 421){
		warn("!!! passive cmd return timeout!\n");
		return FTP_FAIL;
	}
	//trace("parseInfo: %s\n", parseStr.c_str());

	size_t nBegin = parseStr.find_first_of("(");
	size_t nEnd	  = parseStr.find_first_of(")");
	strData		  = parseStr.substr(nBegin + 1, nEnd - nBegin - 1);

	//trace("ParseAfter: %s\n", strData.c_str());
	if( SplitString( strData , strArray , "," ) <0)
		return FTP_FAIL;

	if( ParseString( strArray , nPort , strServerIp) < 0)
		return FTP_FAIL;

	//trace("nPort: %ld IP: %s\n", nPort, strServerIp.c_str());
	if (Connect(data_fd, m_strServerIP, nPort) < 0){
		warn("!!! data link connect fail!\n");
		return FTP_FAIL;
	}

	return FTP_SUCC;
}
// !建立数据连接

FTP_API CFTPManager::createDataLink(int data_fd, int accp_fd){
	int ret = FTP_SUCC;
	struct sockaddr_in sckaddr;
	int len = 0; 

	string local_ip;
	int local_port = 0;

	timeval stime;
	fd_set	waitfd;

	if(data_fd <= 0){
		return FTP_FAIL;
	}
	printf("=2==createDataLink\n");
	sckaddr.sin_family = AF_INET;
	sckaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	sckaddr.sin_port = 0;
	len = sizeof(sckaddr);
	ret = ::bind(data_fd, (sockaddr* )&sckaddr, len);
	if(ret < 0){
		warn("!!! port mode socket bind error!\n");
		return FTP_FAIL;
	}

	ret = listen(data_fd, 1);
	if(ret < 0){
		warn("!!! port mode socket linsten error!\n");
		return FTP_FAIL;
	}

	len = sizeof(sckaddr);
	ret = getsockname(data_fd, (sockaddr *)&sckaddr, (socklen_t*)&len); 
	if(ret < 0){
		warn("!!! port mode getsockname error!\n");
		return FTP_FAIL;
	}

	local_ip = GetLocalIp();
	if(local_ip.length() <=0){
		warn("!!! port mode do not get valid addr!\n");
		return FTP_FAIL;
	}
	local_port = htons(sckaddr.sin_port) + 1;

	std::string parseStr = Port(local_ip, local_port);
	ret = parseResponse(parseStr);
	if(ret != 200){
		warn("!!! port cmd return error!\n");
		return FTP_FAIL;
	}
	
	stime.tv_sec = 4;
	stime.tv_usec = 0;
	FD_ZERO( &waitfd );
	FD_SET(data_fd, &waitfd );
	ret = select(data_fd + 1, &waitfd, 0, 0, &stime);
	if (ret > 0){
		accp_fd = accept(data_fd, (struct sockaddr *)NULL, NULL);
		if(accp_fd <= 0){
			warn("!!! passive cmd return timeout!\n");
			return FTP_FAIL;
		}
	} else {
		warn("!!! socket rcv mode select error!\n");
		return FTP_FAIL;
	}

	return FTP_SUCC;
}
// 解析返回ftp命令的值

FTP_API CFTPManager::parseResponse(const std::string &str){
	if(str.empty()){
		return FTP_FAIL;
	}
	
	std::string strData = str.substr(0, 3);
	unsigned int val = atoi(strData.c_str());

	return val;
}

测试代码

/*
编译方法:gaohui@gaohui-virtual-machine:~/FtpLib$ arm-hisiv300-linux-uclibcgnueabi-g++ ftptest.cpp -I/home/gaohui/thirdparty/FtpLib -L/home/gaohui/thirdparty/FtpLib/ -lftp
*/

#include "stdio.h"
#include <memory>
#include "FtpClientLib.h"
#include <string>
#include <sstream>
#include <iostream>

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <string.h>

using namespace std;

int main()
{

	FTPParams ftp_params;
	ftp_params.ip = "192.168.88.88";
    ftp_params.port = 21;
    ftp_params.username = "gaohui";
    ftp_params.password = "123";	
	ftp_params.path = "/home/gaohui/nemo/ftp";
	
	printf("!!!ftp connect\n");
	CFTPManager *ftp = new CFTPManager(ftp_params);
	
    DIR *dir;    
	struct dirent *ptr;
	char base[1000];
	if ((dir=opendir("/media/tf")) == NULL)
	{
		printf("Open dir error...");
	}
	
	while ((ptr=readdir(dir)) != NULL)
	{
        if( ptr->d_type == 8)    ///current dir     
        	{
      			printf("dir name:%s\n",ptr->d_name);
				string filedir = "/media/tf/";
				string filename = filedir + ptr->d_name;
				string a = ptr->d_name;
				string filename1 = a.substr(0, a.rfind("."));//删除图片后缀.jgp			
				//printf("filename1 : %s\n",filename1.c_str());	
				
				memset(base,'\0',sizeof(base));
				strcpy(base,"/media/tf/");
				strcat(base,ptr->d_name);
				//printf("base : %s\n",base);
				
				FILE *rdfile = fopen(base, "rb");
				fseek(rdfile,0,SEEK_END); //定位到文件末
				int nFileLen = ftell(rdfile); //文件长度 
				fseek(rdfile,0,SEEK_SET);
				char *data = (char *)malloc(nFileLen);
				int ret = fread(data, 1, nFileLen, rdfile);
				if(ret != nFileLen){
					printf("read fail!\n");
				}

				int retftp = ftp->Push(filename1, data, ret);
				
				int d = rand() % 500;
				d = 500 + d;
				usleep(d*1000);
				
				if(retftp < 0) {
					printf("ftp push jgp fail! \n");
					delete ftp;
					CFTPManager *ftp = new CFTPManager(ftp_params);
					//break;
				}else{
				    //string deletejpg = "rm -f " + filename;
    				//system(deletejpg.c_str());	
					printf("ftp push jgp success!\n");
					//break;
				}							
				
				free(data);
				data = NULL;
				fclose(rdfile);	
        	}
		
	}

	closedir(dir);
	delete ftp;

	return 0;
}

对长连接后FTP协议分析

1. 打开Wireshark,选择工具栏上的“Capture”->“interfaces选择网关”,选择工具栏上的“Capture”->“optoins”选择过滤器,并在capture filter中输入 tcp port 20 or tcp port 21(表示要抓tcp的包)

2、第一部分抓的包,客户端与主机的TCP三次握手,访问21端口号,开放随机端口41206。

3、发送第一幅图片数据:

3.1、第二部分抓的包,客户端输入用户名;服务器端确认并提示输密码;客户端确认;客户端发送密码;服务器端确认密码正确;客户端确认收到;客户端输入二进制数据传输方式,服务器端确认并提示传输模式。

3.2、第三部分抓的包,服务器端保存的数据路径,使用被动模式进行数据连接。

3.3、第四部分抓的包,客户端向服务器端请求数据连接,发送图片数据并确认收到。

4、发送第二幅图片数据:

4.1、第五部分抓的包,服务器保存数据路径,使用被动模式进行数据连接。

4.2、第六部分抓的包,客户端向服务器端请求数据连接,发送图片数据并确认收到。

5、第六部分抓的包,断开数据连接,客户端与服务器的TCP四次挥手,关闭21端口号和随机端口41206。

下载

https://download.csdn.net/download/u010872301/10649387

猜你喜欢

转载自blog.csdn.net/u010872301/article/details/82459811