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_strR