1. 程式人生 > >ftp客戶端socket實現

ftp客戶端socket實現

#ifndef CLIENT_H_  
#define CLIENT_H_  
  
//#include <sys/socket.h>  
#include<winsock.h> 
#include<assert.h> 
//#include <unistd.h>  
#include <fcntl.h>  
#include <stdio.h>  


//#include <stdlib.h>  
//#include <sys/types.h>  
//#include <sys/ioctl.h>  
//#include <netinet/in.h>  
//#include <sys/stat.h>  
//#include <netdb.h>  
//#include <errno.h>  
//#include <arpa/inet.h>  
//#include <sys/stat.h>  


#include <iostream>
#include <string>  
#include <list>  
#include<fstream>  


#define INVALID_SOCKET              -1  
#define FTP_API                     int  
#define MAX_PATH                    260  
#define trace                       printf  
  
#define FTP_PARAM_BASE  
#define FTP_DEFAULT_PORT            "21"                            //FTP預設埠號  
#define FTP_DEFAULT_BUFFER          1024*4                          //FTP下載緩衝預設大小  
#define FTP_DEFAULT_PATH            "/mnt/dvs/"                     //FTP預設儲存路徑  
      
#define FTP_COMMAND_BASE            1000  
#define FTP_COMMAND_END             FTP_COMMAND_BASE + 30  
#define FTP_COMMAND_USERNAME        FTP_COMMAND_BASE + 1            //使用者名稱  
#define FTP_COMMAND_PASSWORD        FTP_COMMAND_BASE + 2            //密碼  
#define FTP_COMMAND_QUIT            FTP_COMMAND_BASE + 3            //退出  
#define FTP_COMMAND_CURRENT_PATH    FTP_COMMAND_BASE + 4            // 獲取檔案路徑  
#define FTP_COMMAND_TYPE_MODE       FTP_COMMAND_BASE + 5            // 改變傳輸模式  
#define FTP_COMMAND_PSAV_MODE       FTP_COMMAND_BASE + 6            // 被動埠模式  
#define FTP_COMMAND_DIR             FTP_COMMAND_BASE + 7            // 獲取檔案列表  
#define FTP_COMMAND_CHANGE_DIRECTORY FTP_COMMAND_BASE + 8           // 改變路徑  
#define FTP_COMMAND_DELETE_FILE     FTP_COMMAND_BASE + 9            // 刪除檔案  
#define FTP_COMMAND_DELETE_DIRECTORY FTP_COMMAND_BASE + 10          // 刪除目錄/資料夾  
#define FTP_COMMAND_CREATE_DIRECTORY FTP_COMMAND_BASE + 11          // 建立目錄/資料夾  
#define FTP_COMMAND_RENAME_BEGIN    FTP_COMMAND_BASE  +12           // 開始重新命名  
#define FTP_COMMAND_RENAME_END      FTP_COMMAND_BASE + 13           // 重新命名結束  
#define FTP_COMMAND_FILE_SIZE       FTP_COMMAND_BASE + 14           // 獲取檔案大小  
#define FTP_COMMAND_DOWNLOAD_POS    FTP_COMMAND_BASE + 15           // 下載檔案從指定位置開始  
#define FTP_COMMAND_DOWNLOAD_FILE   FTP_COMMAND_BASE + 16           // 下載檔案  
#define FTP_COMMAND_UPLOAD_FILE     FTP_COMMAND_BASE + 17           // 上傳檔案  
#define FTP_COMMAND_APPEND_FILE     FTP_COMMAND_BASE + 18           // 追加上載檔案     
  
/*        登陸步驟 
        login2Server 
            | 
        inputUserName 
            | 
        inputPassWord 
            | 
          具體操作 
            | 
          quit 
*/  
   


class CFTPManager   
{  
public :   
enum cftpType
{
binary = 0x31,
ascii,
};
enum cftpType mode;


    CFTPManager(void);  
  
    virtual ~CFTPManager(void);  
      
    // ! 登陸伺服器  
    FTP_API login2Server(const std::string &serverIP);  
  
    // !輸入使用者名稱  
    FTP_API inputUserName(const std::string &userName);  
  
    // !輸入密碼  
    FTP_API inputPassWord(const std::string &password);  
  
    // !退出FTP  
    FTP_API quitServer(void);  
  
    // !命令: PWD  
    const std::string PWD();  
  
    // !設定傳輸格式 2進位制  還是ascii方式傳輸  
FTP_API setTransferMode(cftpType mode);
  
    // !設定為被動模式  
    const std::string Pasv();  
  
    // ! 命令: DIR  
    const std::string Dir(const std::string &path);  
  
    // !命令 : CD  
    FTP_API CD(const std::string &path);  
  
    // !刪除檔案  
    FTP_API DeleteFile(const std::string &strRemoteFile);  
  
    // ! 刪除資料夾/目錄  
    FTP_API DeleteDirectory(const std::string &strRemoteDir);  
  
    // ! 建立目錄/資料夾  
    FTP_API CreateDirectory(const std::string &strRemoteDir);  
  
    // !重新命名  
    FTP_API Rename(const std::string &strRemoteFile, const std::string &strNewFile);  
  
    // !獲取檔案大小  
    long long getFileLength(const std::string &strRemoteFile);  
  
    // !關閉連線  
    void Close(int sock);  
void closeClient();
  
    // 下載檔案  
    FTP_API Get(const std::string &strRemoteFile, const std::string &strLocalFile);  
  
    // 上載檔案  支援斷電傳送方式  
    FTP_API Put(const std::string &strRemoteFile, const std::string &strLocalFile);  


FTP_API Put_2(const std::string &strRemoteFile, const std::string &strLocalFile);
  
  
private:  
    // !合成傳送到伺服器的命令  
    const std::string parseCommand(const unsigned int command, const std::string &strParam);  
  
    // ! 建立連線  
    FTP_API Connect(int socketfd, const std::string &serverIP, unsigned int nPort);  
  
    // ! 返回伺服器資訊  
    const std::string serverResponse(int sockfd);  
  
    // !獲取伺服器資料  
    FTP_API getData(int fd, char *strBuf, unsigned long length);  
  
    // !傳送命令  
    FTP_API Send(int fd, const std::string &cmd);  
  
    // !傳送命令  
    FTP_API Send(int fd, const char *cmd, const size_t len);  
  
    // !建立資料連線  
    FTP_API createDataLink(int data_fd);  
  
    // !解析PASV模式返回的字串獲取FTP埠號和FTP伺服器IP  
    FTP_API ParseString(std::list<std::string> strArray, unsigned long & nPort ,std::string & strServerIp);  
  
    // 開啟本地檔案  
    FILE *createLocalFile(const std::string &strLocalFile);  
  
    // 下載檔案   
    FTP_API downLoad(const std::string &strRemoteFile, const std::string &strLocalFile, const int pos = 0, const unsigned int length = 0);  
  
    // 解析返回ftp命令的值  
    FTP_API parseResponse(const std::string &str);  
  
private:  
    //!控制連線套接字  
    int     m_cmdSocket;  
      
    // !當前使用者名稱  
    std::string m_strUserName;  
  
    // !當前使用者密碼  
    std::string m_strPassWord;  
  
    // !伺服器的IP  
    std::string m_strServerIP;  
  
    // !伺服器Port  
    unsigned int m_nServerPort;  
  
    // !伺服器迴應資訊快取  
    std::string m_strResponse;  
  
    // !儲存命令引數  
    std::string m_commandStr;  
  
    // !當前使用的命令引數  
    unsigned int m_nCurrentCommand;  
  
    // !是否登陸標誌。  
    bool    m_bLogin;  
};  
  
  

#endif  

#include "WS2tcpip.h" //WS2tcpip.h
#include "FTPManager.h"  




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.insert(strArray.end(), strSrc.substr(0, pos));
strSrc = strSrc.substr(pos + 1, strSrc.length() - pos - 1);
}


strArray.insert(strArray.end(), strSrc.substr(0, strSrc.length()));


return 0;
}


CFTPManager::CFTPManager(void) : m_bLogin(false)
{
WORD wVersionRequested;
WSADATA wsaData;
int ret;


//WinSock初始化: 
wVersionRequested = MAKEWORD(2, 2);//希望使用的WinSock DLL的版本 
ret = WSAStartup(wVersionRequested, &wsaData);
if (ret != 0)
{
printf("WSAStartup() failed!\n");
}
//確認WinSock DLL支援版本2.2: 
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
printf("Invalid Winsock version!\n");
}
//isConnect = false;


m_cmdSocket = socket(AF_INET, SOCK_STREAM, 0);
mode = binary;


}


CFTPManager::~CFTPManager(void)
{
std::string strCmdLine = parseCommand(FTP_COMMAND_QUIT, "");


Send(m_cmdSocket, strCmdLine.c_str());
Close(m_cmdSocket);
m_bLogin = false;
}


FTP_API CFTPManager::login2Server(const std::string &serverIP)
{
std::string strPort;
int pos = serverIP.find_first_of(":");


if (pos > 0)
{
strPort = serverIP.substr(pos + 1, serverIP.length() - pos);
}
else
{
pos = serverIP.length();
strPort = FTP_DEFAULT_PORT;
}


m_strServerIP = serverIP.substr(0, pos);
m_nServerPort = atol(strPort.c_str());


trace("IP: %s port: %d\n", m_strServerIP.c_str(), m_nServerPort);


if (Connect(m_cmdSocket, m_strServerIP, m_nServerPort) < 0)
{


return -1;
}


m_strResponse = serverResponse(m_cmdSocket);
printf("@@@@Response: %s", m_strResponse.c_str());


return  parseResponse(m_strResponse);
}


FTP_API CFTPManager::inputUserName(const std::string &userName)
{
std::string strCommandLine = parseCommand(FTP_COMMAND_USERNAME, userName);


m_strUserName = userName;


if (Send(m_cmdSocket, strCommandLine) < 0)
{
return -1;
}


m_strResponse = serverResponse(m_cmdSocket);
printf("Response: %s\n", m_strResponse.c_str());


return parseResponse(m_strResponse);
}


FTP_API CFTPManager::inputPassWord(const std::string &password)
{
std::string strCmdLine = parseCommand(FTP_COMMAND_PASSWORD, password);


m_strPassWord = password;
if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}
else
{
m_bLogin = true;


m_strResponse = serverResponse(m_cmdSocket);
printf("Response: %s\n", m_strResponse.c_str());


return parseResponse(m_strResponse);
}
}


FTP_API CFTPManager::quitServer(void)
{
std::string strCmdLine = parseCommand(FTP_COMMAND_QUIT, "");
if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}
else
{
m_strResponse = serverResponse(m_cmdSocket);
printf("Response: %s\n", m_strResponse.c_str());


return parseResponse(m_strResponse);
}


}


const std::string CFTPManager::PWD()
{
std::string strCmdLine = parseCommand(FTP_COMMAND_CURRENT_PATH, "");


if (Send(m_cmdSocket, strCmdLine.c_str()) < 0)
{
return "";
}
else
{
return serverResponse(m_cmdSocket);
}
}




FTP_API CFTPManager::setTransferMode(cftpType mode)
{
std::string strCmdLine;


switch (mode)
{
case binary:
strCmdLine = parseCommand(FTP_COMMAND_TYPE_MODE, "I");
break;
case ascii:
strCmdLine = parseCommand(FTP_COMMAND_TYPE_MODE, "A");
break;
default:
break;
}


if (Send(m_cmdSocket, strCmdLine.c_str()) < 0)
{
assert(false);
}
else
{
m_strResponse = serverResponse(m_cmdSocket);
printf("@@@@Response: %s", m_strResponse.c_str());


return parseResponse(m_strResponse);
}
}




const std::string CFTPManager::Pasv()
{
std::string strCmdLine = parseCommand(FTP_COMMAND_PSAV_MODE, "");


if (Send(m_cmdSocket, strCmdLine.c_str()) < 0)
{
return "";
}
else
{
m_strResponse = serverResponse(m_cmdSocket);


return m_strResponse;
}
}




const std::string CFTPManager::Dir(const std::string &path)
{
int dataSocket = socket(AF_INET, SOCK_STREAM, 0);


if (createDataLink(dataSocket) < 0)
{
return "";
}
// 資料連線成功  
std::string strCmdLine = parseCommand(FTP_COMMAND_DIR, path);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());
Close(dataSocket);
return "";
}
else
{
trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());
m_strResponse = serverResponse(dataSocket);


trace("@@@@Response: \n%s\n", m_strResponse.c_str());
Close(dataSocket);


return m_strResponse;
}


}




FTP_API CFTPManager::CD(const std::string &path)
{
assert(m_cmdSocket != INVALID_SOCKET);


std::string strCmdLine = parseCommand(FTP_COMMAND_CHANGE_DIRECTORY, path);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}


m_strResponse = serverResponse(m_cmdSocket);


trace("@@@@Response: %s\n", m_strResponse.c_str());
return parseResponse(m_strResponse);
}


FTP_API CFTPManager::DeleteFile(const std::string &strRemoteFile)
{
assert(m_cmdSocket != INVALID_SOCKET);


std::string strCmdLine = parseCommand(FTP_COMMAND_DELETE_FILE, strRemoteFile);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}


m_strResponse = serverResponse(m_cmdSocket);
printf("@@@@Response: %s\n", m_strResponse.c_str());
return parseResponse(m_strResponse);
}


FTP_API CFTPManager::DeleteDirectory(const std::string &strRemoteDir)
{
assert(m_cmdSocket != INVALID_SOCKET);


std::string strCmdLine = parseCommand(FTP_COMMAND_DELETE_DIRECTORY, strRemoteDir);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}


m_strResponse = serverResponse(m_cmdSocket);


trace("@@@@Response: %s\n", m_strResponse.c_str());
return parseResponse(m_strResponse);
}


FTP_API CFTPManager::CreateDirectory(const std::string &strRemoteDir)
{
assert(m_cmdSocket != INVALID_SOCKET);


std::string strCmdLine = parseCommand(FTP_COMMAND_CREATE_DIRECTORY, strRemoteDir);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}


m_strResponse = serverResponse(m_cmdSocket);


trace("@@@@Response: %s\n", m_strResponse.c_str());
return parseResponse(m_strResponse);
}


FTP_API CFTPManager::Rename(const std::string &strRemoteFile, const std::string &strNewFile)
{
assert(m_cmdSocket != INVALID_SOCKET);


std::string strCmdLine = parseCommand(FTP_COMMAND_RENAME_BEGIN, strRemoteFile);
Send(m_cmdSocket, strCmdLine);
trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());


Send(m_cmdSocket, parseCommand(FTP_COMMAND_RENAME_END, strNewFile));


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


long long CFTPManager::getFileLength(const std::string &strRemoteFile)
{
assert(m_cmdSocket != INVALID_SOCKET);


std::string strCmdLine = parseCommand(FTP_COMMAND_FILE_SIZE, strRemoteFile);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}


m_strResponse = serverResponse(m_cmdSocket);


trace("@@@@Response: %s\n", m_strResponse.c_str());


std::string strData = m_strResponse.substr(0, 3);
//unsigned long val = atol(strData.c_str());
long long val = atoll(strData.c_str());


if (val == 213)
{
strData = m_strResponse.substr(4);
trace("strData: %s\n", strData.c_str());
val = atoll(strData.c_str());


return val;
}


return -1;
}




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


void CFTPManager::closeClient()
{
closesocket(m_cmdSocket);
m_cmdSocket = INVALID_SOCKET;
}


FTP_API CFTPManager::Get(const std::string &strRemoteFile, const std::string &strLocalFile)
{
return downLoad(strRemoteFile, strLocalFile);
}




FTP_API CFTPManager::Put_2(const std::string &strRemoteFile, const std::string &strLocalFile)
{
std::string strCmdLine;
const unsigned long dataLen = FTP_DEFAULT_BUFFER;
char strBuf[dataLen] = { 0 };
unsigned long nSize = getFileLength(strRemoteFile);
unsigned long nLen = 0;


//struct stat sBuf;
//assert(stat(strLocalFile.c_str(), &sBuf) == 0);
//trace("size: %d\n", sBuf.st_size);


FILE *pFile = fopen(strLocalFile.c_str(), "rb");  // 以只讀方式開啟  且檔案必須存在  
assert(pFile != NULL);


int data_fd = socket(AF_INET, SOCK_STREAM, 0);
assert(data_fd != -1);


if (createDataLink(data_fd) < 0)
{
return -1;
}


if (nSize == -1)
{
strCmdLine = parseCommand(FTP_COMMAND_UPLOAD_FILE, strRemoteFile);
}
else
{
strCmdLine = parseCommand(FTP_COMMAND_APPEND_FILE, strRemoteFile);
}


if (Send(m_cmdSocket, strCmdLine) < 0)
{
Close(data_fd);
return -1;
}


trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());


fseek(pFile, nSize, SEEK_SET);
while (!feof(pFile))
{
nLen = fread(strBuf, 1, dataLen, pFile);
if (nLen < 0)
{
break;
}


if (Send(data_fd, strBuf) < 0)
{
Close(data_fd);
return -1;
}
}


trace("@@@@Response: %s\n", serverResponse(data_fd).c_str());


Close(data_fd);
trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());
fclose(pFile);


return 0;
}




FTP_API CFTPManager::Put(const std::string &strRemoteFile, const std::string &strLocalFile)
{
std::string strCmdLine;
const unsigned long dataLen = FTP_DEFAULT_BUFFER;
char strBuf[dataLen] = { 0 };
long long nSize = getFileLength(strRemoteFile);
long long nLen = 0;


//struct stat sBuf;
//assert(stat(strLocalFile.c_str(), &sBuf) == 0);
//trace("size: %d\n", sBuf.st_size);





//FILE *pFile = fopen(strLocalFile.c_str(), "rb");  // 以只讀方式開啟  且檔案必須存在  
//assert(pFile != NULL);


int data_fd = socket(AF_INET, SOCK_STREAM, 0);
assert(data_fd != -1);


if (createDataLink(data_fd) < 0)
{
return -1;
}


if (nSize == -1)
{
strCmdLine = parseCommand(FTP_COMMAND_UPLOAD_FILE, strRemoteFile);
}
else
{
strCmdLine = parseCommand(FTP_COMMAND_APPEND_FILE, strRemoteFile);
nLen = nSize;
}


if (Send(m_cmdSocket, strCmdLine) < 0)
{
Close(data_fd);
return -1;
}


trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());


//fseek(pFile, nSize, SEEK_SET);


std::ifstream in;
//開啟檔案 
//in.open(name, std::ios::binary);C:\Users\hp\Desktop\vot.txt
//printf("input the file path!\n");
//scanf("%s", filename);
in.open(strLocalFile, std::ios::binary);
if (!in) {
printf("cannot open the file\n");
return false;
}
memset(strBuf, 0, sizeof(strBuf));
//得到檔案的位元組數 
in.seekg(0, std::ios_base::end);
//int sp = in.tellg();// -nLen;
long long  sp = in.tellg();// -nLen;
sp = sp - nLen;
int total_size = 0;
int r;
/*char length[20];
sprintf(length, "%d", sp);*/


//傳送位元組 
//r = send(dataSocket, length, sizeof(length), 0);
//if (r == SOCKET_ERROR)
//{
//return false;
//}


while (sp > 0)
{
in.clear();
in.seekg(nLen, std::ios_base::beg);
memset(strBuf, 0, sizeof(strBuf));
//讀取檔案 
in.read(strBuf, sizeof(strBuf));
int size = sp < FTP_DEFAULT_BUFFER ? sp : FTP_DEFAULT_BUFFER;
nLen += size;
//傳送檔案 
r = send(data_fd, strBuf, size, 0);


sp -= size;
if (r == SOCKET_ERROR)
{
in.close();
Close(data_fd);
return -1;
}
}

/*while (!feof(pFile))
{
nLen = fread(strBuf, 1, dataLen, pFile);
if (nLen < 0)
{
break;
}
if (Send(data_fd, strBuf) < 0)
{
Close(data_fd);
return -1;
}
}
fclose(pFile);
*/


//trace("@@@@Response: %s\n", serverResponse(data_fd).c_str());
Close(data_fd);


trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());
in.close();


return 0;
}




const std::string CFTPManager::parseCommand(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;
default:
break;
}


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


m_commandStr = strCommandLine;
trace("parseCommand: %s\n", 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 -1;
}


unsigned int argp = 1;
//int error = -1;


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


//ioctl(socketfd, FIONBIO, &argp);  //設定為非阻塞模式  


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 = 20;  //設定為1秒超時  
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
{
ret = false;
}
}
}
else
{
trace("Connect Immediately!!!\n");
ret = true;
}


argp = 0;
//ioctl(socketfd, FIONBIO, &argp);


if (!ret)
{
Close(socketfd);
fprintf(stderr, "cannot connect server!!\n");
return -1;
}


//fprintf(stdout, "Connect!!!\n");  


return 0;
}




const std::string CFTPManager::serverResponse(int sockfd)
{
if (sockfd == INVALID_SOCKET)
{
return "";
}


int nRet = -1;
char buf[MAX_PATH] = { 0 };


m_strResponse.clear();


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


return m_strResponse;
}


FTP_API CFTPManager::getData(int fd, char *strBuf, unsigned long length)
{
assert(strBuf != NULL);


if (fd == INVALID_SOCKET)
{
return -1;
}


memset(strBuf, 0, length);
timeval stime;
int nLen;


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


fd_set  readfd;
FD_ZERO(&readfd);
FD_SET(fd, &readfd);


if (select(fd + 1, &readfd, 0, 0, &stime) > 0)
{
if ((nLen = recv(fd, strBuf, length, 0)) > 0)
{
return nLen;
}
else
{
return -2;
}
}
return 0;
}


FTP_API CFTPManager::Send(int fd, const std::string &cmd)
{
if (fd == INVALID_SOCKET)
{
return -1;
}


return Send(fd, cmd.c_str(), cmd.length());
}


FTP_API CFTPManager::Send(int fd, const char *cmd, const size_t len)
{
if ((FTP_COMMAND_USERNAME != m_nCurrentCommand)
&& (FTP_COMMAND_PASSWORD != m_nCurrentCommand)
&& (!m_bLogin))
{
return -1;
}


timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;


fd_set  writefd;
FD_ZERO(&writefd);
FD_SET(fd, &writefd);


if (select(fd + 1, 0, &writefd, 0, &timeout) > 0)
{
size_t nlen = len;
int nSendLen = 0;
while (nlen > 0)
{
nSendLen = send(fd, cmd, (int)nlen, 0);


if (nSendLen == -1)
return -2;


nlen = nlen - nSendLen;
cmd += nSendLen;
}
return 0;
}
return -1;
}




FTP_API CFTPManager::createDataLink(int data_fd)
{
assert(data_fd != INVALID_SOCKET);


std::string strData;
unsigned long nPort = 0;
std::string strServerIp;
std::list<std::string> strArray;


std::string parseStr = Pasv();


if (parseStr.size() <= 0)
{
return -1;
}


//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 -1;


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


//trace("nPort: %ld IP: %s\n", nPort, strServerIp.c_str());  


if (Connect(data_fd, strServerIp, nPort) < 0)
{
return -1;
}


return 0;


}


FTP_API CFTPManager::ParseString(std::list<std::string> strArray, unsigned long & nPort, std::string & strServerIp)
{
if (strArray.size() < 6)
return -1;


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 0;
}


FILE *CFTPManager::createLocalFile(const std::string &strLocalFile)
{
return fopen(strLocalFile.c_str(), "w+b");
}


FTP_API CFTPManager::downLoad(const std::string &strRemoteFile, const std::string &strLocalFile, const int pos, const unsigned int length)
{
assert(length >= 0);


FILE *file = NULL;
unsigned long nDataLen = FTP_DEFAULT_BUFFER;
char strPos[MAX_PATH] = { 0 };
int data_fd = socket(AF_INET, SOCK_STREAM, 0);


assert(data_fd != -1);


if ((length != 0) && (length < nDataLen))
{
nDataLen = length;
}
char *dataBuf = new char[nDataLen];
assert(dataBuf != NULL);


sprintf(strPos, "%d", pos);


if (createDataLink(data_fd) < 0)
{
trace("@@@@ Create Data Link error!!!\n");
return -1;
}


std::string strCmdLine = parseCommand(FTP_COMMAND_DOWNLOAD_POS, strPos);
if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}
trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());


strCmdLine = parseCommand(FTP_COMMAND_DOWNLOAD_FILE, strRemoteFile);


if (Send(m_cmdSocket, strCmdLine) < 0)
{
return -1;
}
trace("@@@@Response: %s\n", serverResponse(m_cmdSocket).c_str());


file = createLocalFile(std::string(FTP_DEFAULT_PATH + strLocalFile));
assert(file != NULL);


int len = 0;
int nReceiveLen = 0;
while ((len = getData(data_fd, dataBuf, nDataLen)) > 0)
{
nReceiveLen += len;


int num = fwrite(dataBuf, 1, len, file);
memset(dataBuf, 0, sizeof(dataBuf));


//trace("%s", dataBuf);  
trace("Num:%d\n", num);
if (nReceiveLen == length && length != 0)
break;


if ((nReceiveLen + nDataLen) > length  && length != 0)
{
delete[]dataBuf;
nDataLen = length - nReceiveLen;
dataBuf = new char[nDataLen];
}
}


Close(data_fd);
fclose(file);
delete[]dataBuf;


return 0;
}


FTP_API CFTPManager::parseResponse(const std::string &str)
{
assert(!str.empty());


std::string strData = str.substr(0, 3);
unsigned int val = atoi(strData.c_str());


return val;
}

test:

#define _CRT_SECURE_NO_WARNINGS
#include "FTPManager.h" 
#include <string.h>
using namespace std;


/*功能測試:
0、登入、退出     ok
1、上傳檔案ok
2、斷點續傳ok
3、刪除檔案ok
4、建立刪除資料夾 ok(刪除空資料夾)
5、
*/




int main()
{
string hostIP = "192.168.1.84";//
string username = "test";
string password = "test";
string remotefile = "OSGEARTH.rar";
string localfile = "E:\\XXXX.rar";


string newFolder = "stock";


//(enum type)binary;
CFTPManager client;

//登入
client.login2Server(hostIP);
client.inputUserName(username);
client.inputPassWord(password);


//傳送檔案/斷點續傳
client.setTransferMode(client.mode);
client.Put(remotefile, localfile);


//刪除檔案
//client.DeleteFile(remotefile);


//建立資料夾
//client.CreateDirectory(newFolder);


//刪除資料夾
//client.DeleteDirectory(newFolder);


//退出連線
client.quitServer();
return 0;
}