Codejie's C++ Space

Using C++

轮子:FTP接口实现

项目中新增对FTP的支持,需求的变更是开发过程中不可避免的事情,只是变更的时间越靠近项目末期,就越是灾难。还好这个需求可以设计的相对独立,做到尽量不影响已实现部分。
     说的FTP,就想起曾经写的一个FTP接口对象,由于现在FTP的实现库到处都是,因此在写好此对象时,被同事称为:做轮子的人~我的想法是,组装车和制作车应该还是有区别的~
     不扯了,下面是实现的代码,比较啰嗦~

#ifndef __FTPOPERAOBJECT_H__
#define __FTPOPERAOBJECT_H__

#include 
<fstream>
#include 
<string>

#include 
"ace/INET_Addr.h"

#include 
"acex/NB_Tcp_Client.h"
#include 
"acex/NB_Tcp_Server.h"

//OK, support both PORT and PASV mode NOW.


const int REPLY_CODE_541                =    541;
const int REPLY_CODE_542                =    542;
const int REPLY_CODE_543                =    543;
const int REPLY_CODE_544                =    544;
const int REPLY_CODE_545                =    545;
const int REPLY_CODE_546                =    546;


const std::string REPLY_COMMENT_541    =    "(inner)Control connection is break.";
const std::string REPLY_COMMENT_542    =    "(inner)Control connection is timeout for waiting for remote reply.";
const std::string REPLY_COMMENT_543    =    "(inner)Control connection gets a NULL reply.";
const std::string REPLY_COMMENT_544    =    "(inner)Reply result is empty.";
const std::string REPLY_COMMENT_545    =    "(inner)Syntax error, reply unrecognized.";
const std::string REPLY_COMMENT_546    =    "(inner)Syntax error, reply is not expected.";

class CFTPDataTcpObject;

class CFTPCtrlTcpObject
{
public:
    
struct ResultInfo_t
    {
        
int m_iResult;
        std::
string m_strInfo;
    };
    
    typedef std::auto_ptr
<CFTPDataTcpObject> TDataTcpObjectPtr;
public:
    CFTPCtrlTcpObject(
const size_t buf_size = 64 * 1024const ACE_Time_Value& connect_timeout = ACE_Time_Value(2), const ACE_Time_Value& select_timeout = ACE_Time_Value(2));
    
virtual ~CFTPCtrlTcpObject();
public:
    
int Open(const ACE_INET_Addr& stAddr);
    
int Connect(const std::string& strUser, const std::string& strPasswd);
    
int Close();

    
int CmdChgDir(const std::string& strDir);
    
int CmdChgLocalDir(const std::string& strDir);
    
int CmdMkDir(const std::string& strDir);
    
int CmdDelFile(const std::string& strFile);
    
int CmdType(bool bASCII = true);
    
int CmdNoop();
    
int CmdRenFile(const std::string& strOldFile, const std::string& strNewFile);
    
int CmdGetFile(const std::string& strRemoteFile, const std::string& strLocalFile);
    
int CmdPutFile(const std::string& strLocalFile, const std::string& strRemoteFile);
    
int CmdNLst(const std::string& strFilter, std::string& strOutput);
    
int CmdList(const std::string& strFilter, std::string& strOutput);

    
int SendCmdWithDataStream(const std::string& strCmd, std::ostream& os);
    
int SendCmdWithDataStream(const std::string& strCmd, char* pchData, size_t& szDataRead);
    
    
bool IsConnected() const { return _bConnected; }
    
bool IsASCII() const { return _bASCII; }
    
const ResultInfo_t& GetResultInfo() const;    
    size_t AffectedSize() 
const;

    
void PortMode() { _bPortMode = true; }
    
void PasvMode() { _bPortMode = false; }
    
void SetStopFlag(bool bStop) { _bTransStop = bStop; }
public:
    
int SendCommand(const std::string& strCmd);
    
int RecvResult(ResultInfo_t& stResult);
    
int Is200Result(const ResultInfo_t& stResult) const;
    
int Is100Result(const ResultInfo_t& stResult) const;
    
int Is300Result(const ResultInfo_t& stResult) const;
    
int GetLocalAddr(ACE_INET_Addr& stAddr);
    unsigned 
short GetRandPort();
    std::
string AnalyseAddr(const ACE_INET_Addr& stAddr) const;    
    ACE_INET_Addr AnalyseAddr(
const std::string& strAddr) const;
public:
    
int RecvResult();
    
int ResultCode() const;
    
const std::string& ResultInfo() const;

    
int Is200Result() const;
    
int Is100Result() const;
    
int Is300Result() const;
protected:
    
bool _bConnected;
    ACE_RANDR_TYPE _seed;
    
char _acResultBuf[2048];
    size_t _szResultSize;
    
bool _bASCII;
    size_t _szAffectedSize;
//can used after get or put command.
    bool _bTransStop;
    
bool _bPortMode;
private:
    size_t _szBufSize;
    ACE_Time_Value _stConnTimeout;
    ACE_Time_Value _stSelectTimeout;

    std::auto_ptr
<ACEX_TcpStream> _stTcpStreamPtr;
    ACE_SOCK_Connector _stConnector;
private:
    ResultInfo_t _stResultInfo;
};

//
class CFTPDataTcpObject
{
public:
    
enum TransStatus { TS_UNKNWON = -1, TS_READ = 0, TS_WRITE, TS_TIMEOUT };
public:
    CFTPDataTcpObject(CFTPCtrlTcpObject
& ctrlobject, size_t buffsize, const ACE_Time_Value& timeout)
        : _stCtrlObject(ctrlobject), _szBuffSize(buffsize), _stTimeout(timeout)
        , _stTcpStreamPtr(NULL)
        , _bStop(
false), _bOpen(false)
    {
    }
    
virtual ~CFTPDataTcpObject();

    
virtual int Get(const std::string& remote, const std::string& local, size_t& size) = 0;
    
virtual int Put(const std::string& remote, const std::string& local, size_t& size) = 0;
    
virtual int SendCmd(const std::string& strCmd, std::ostream& os) = 0;
    
virtual int SendCmd(const std::string& strCmd, char*& pchData, size_t& szDataRead) = 0;
    
void Stop() { _bStop = true; }
    
virtual void Close();

protected:
    TransStatus SelectRead(size_t
& size);
    TransStatus SelectWrite();
    
int Read(char*& data, size_t& size);
    
int Write(const char* data, size_t size);
    
int Read(std::ofstream& ofile, size_t& size);
    
int Write(std::ifstream& ifile, size_t& size);
    
int Read(std::ostream& os, size_t& size);
protected:
    CFTPCtrlTcpObject
& _stCtrlObject;
    size_t _szBuffSize;
    ACE_Time_Value _stTimeout;
protected:
    ACE_INET_Addr _stLocalAddr;
    ACE_INET_Addr _stRemoteAddr;
    std::auto_ptr
<ACEX_TcpStream> _stTcpStreamPtr;
protected:
    
bool _bStop;
    
bool _bOpen;
};
//
class CFTPPortDataTcpObject : public CFTPDataTcpObject
{
public:
    CFTPPortDataTcpObject(CFTPCtrlTcpObject
& ctrlobject, size_t buffsize= 64 * 1024const ACE_Time_Value& timeout = ACE_Time_Value(2));
    
virtual ~CFTPPortDataTcpObject();

    
virtual int Get(const std::string& remote, const std::string& local, size_t& size);
    
virtual int Put(const std::string& remote, const std::string& local, size_t& size);
    
virtual int SendCmd(const std::string& strCmd, char*& pchData, size_t& szDataRead);
    
virtual int SendCmd(const std::string& strCmd, std::ostream& os);
protected:
    
int Open();
    
int Port();
    
int Accept();
private:
    ACE_SOCK_Acceptor _stAcceptor;
};
//
class CFTPPasvDataTcpObject : public CFTPDataTcpObject
{
public:
    CFTPPasvDataTcpObject(CFTPCtrlTcpObject
& ctrlobject, size_t buffsize = 64 * 1024const ACE_Time_Value& timeout = ACE_Time_Value(2));
    
virtual ~CFTPPasvDataTcpObject();

    
virtual int Get(const std::string& remote, const std::string& local, size_t& size);
    
virtual int Put(const std::string& remote, const std::string& local, size_t& size);
    
virtual int SendCmd(const std::string& strCmd, char*& pchData, size_t& szDataRead);
    
virtual int SendCmd(const std::string& strCmd, std::ostream& os);
protected:
    
int Pasv();
    
int Connect();
private:
    ACE_SOCK_Connector _stConnector;
};


#endif


#include 
<stdexcept>
#include 
<sstream>

#include 
"ace/Handle_Set.h"
#include 
"ace/OS_NS_time.h"

#include 
"acex/ACEX.h"

#include 
"FTPOperaObject.h"

CFTPCtrlTcpObject::CFTPCtrlTcpObject(
const size_t buf_size, const ACE_Time_Value& connect_timeout, const ACE_Time_Value& select_timeout)
: _bConnected(
false)
, _seed(ACE_OS::time(NULL))
, _szResultSize(
0)
, _bASCII(
false)
, _szAffectedSize(
0)
, _bTransStop(
false)
, _bPortMode(
true)
, _szBufSize(buf_size)
, _stConnTimeout(connect_timeout)
, _stSelectTimeout(select_timeout)
{
}

CFTPCtrlTcpObject::
~CFTPCtrlTcpObject()
{
    Close();
}

int CFTPCtrlTcpObject::SendCommand(const std::string& strCmd)
{
    
if(!_stTcpStreamPtr->good())
        
return -1;
    std::
string str = strCmd + "\r\n";
    _stTcpStreamPtr
->write(str.c_str(), str.size());
    _stTcpStreamPtr
->flush();

    ACEX_LOG_OS(LM_DEBUG, 
"<<CFTPCtrlTcpObject>>SendCommand() cmd :" << str << std::endl);

    
return _stTcpStreamPtr->good() ? 0 : -1;
}

int CFTPCtrlTcpObject::RecvResult(ResultInfo_t& stResult)
{
    
if(!_stTcpStreamPtr->good())
    {
        stResult.m_iResult 
= REPLY_CODE_541;
        stResult.m_strInfo 
= REPLY_COMMENT_541;
        
return -1;
    }

    ACE_Handle_Set rd_set;
    rd_set.reset();
    rd_set.set_bit(_stTcpStreamPtr
->get_handle());
    
int iMaxFd = 0;
#if !defined(_WIN32)
    iMaxFd 
= _stTcpStreamPtr->get_handle() + 1;
#endif
    
if(ACE::select(iMaxFd, &rd_set, NULL, NULL, &_stSelectTimeout) <= 0)
    {
//timeout
        stResult.m_iResult = REPLY_CODE_542;
        stResult.m_strInfo 
= REPLY_COMMENT_542;
        
return -1;
    }
    _stTcpStreamPtr
->under_flow();
    
if(!_stTcpStreamPtr->good())
    {
        stResult.m_iResult 
= REPLY_CODE_541;
        stResult.m_strInfo 
= REPLY_COMMENT_541;
        
return -1;
    }
    size_t szRead 
= _stTcpStreamPtr->in_avail();
    
if(szRead <= 0)
    {
        stResult.m_iResult 
= REPLY_CODE_543;
        stResult.m_strInfo 
= REPLY_COMMENT_543;
        
return -1;
    }
    
while(szRead > 0)
    {
        _stTcpStreamPtr
->read(_acResultBuf + _szResultSize, szRead);
        _szResultSize 
+= szRead;
        szRead 
= _stTcpStreamPtr->in_avail();
    }
    std::
string strInfo;
    strInfo.assign(_acResultBuf, _szResultSize);

    stResult.m_strInfo 
= "";
    
try
    {
        std::
string::size_type szPos = strInfo.find_first_of("\r\n");
        
while(szPos != std::string::npos)
        {
            stResult.m_strInfo 
= strInfo.substr(0, szPos);
            strInfo 
= strInfo.substr(szPos + 2);
            szPos 
= strInfo.find_first_of("\r\n");
        }

        
if(stResult.m_strInfo.empty())
        {
            stResult.m_iResult 
= REPLY_CODE_544;
            stResult.m_strInfo 
= REPLY_COMMENT_544;
            
return -1;
        }
        szPos 
= stResult.m_strInfo.find_first_of(" ");
        
if(szPos == std::string::npos)
        {
            stResult.m_iResult 
= REPLY_CODE_545;
            stResult.m_strInfo 
= REPLY_COMMENT_545;
            
return -1;
        }
        stResult.m_iResult 
= atoi(stResult.m_strInfo.substr(0, szPos).c_str());
        stResult.m_strInfo 
= stResult.m_strInfo.substr(szPos + 1);
    }
    
catch(std::out_of_range& e)
    {
        ACEX_LOG_OS(LM_WARNING, 
"<<CFTPCtrlTcpObject>>RecvResult() exception(out_of_range) - remote maybe not FTP server." << std::endl);
        stResult.m_iResult 
= REPLY_CODE_546;
        stResult.m_strInfo 
= REPLY_COMMENT_546;
        
return -1;
    }

    _szResultSize 
= 0;

    
return 0;
}

int CFTPCtrlTcpObject::RecvResult()
{
    
if(RecvResult(_stResultInfo) == 0)
    {
        ACEX_LOG_OS(LM_DEBUG, 
"<<CFTPCtrlTcpObject>>RecvResult() Result(" << _stResultInfo.m_iResult << ")" << _stResultInfo.m_strInfo << std::endl);

        
return 0;
    }
    
else
    {
        ACEX_LOG_OS(LM_WARNING, 
"<<CFTPCtrlTcpObject>>RecvResult() Result(" << _stResultInfo.m_iResult << ")" << _stResultInfo.m_strInfo << std::endl);
        
return -1;
    }
}

int CFTPCtrlTcpObject::ResultCode() const
{
    
return _stResultInfo.m_iResult;
}

const std::string& CFTPCtrlTcpObject::ResultInfo() const
{
    
return _stResultInfo.m_strInfo;
}

int CFTPCtrlTcpObject::Open(const ACE_INET_Addr& stAddr)
{
    _stTcpStreamPtr.reset(
new ACEX_TcpStream(_szBufSize));
    ACE_Time_Value st(
5,0);
    
if(_stConnector.connect(*_stTcpStreamPtr.get(), stAddr, &st) == 0)
    {
        _stTcpStreamPtr
->block(0);
        RecvResult();
        
if(Is200Result() == 0)
        {
            _bConnected 
= true;
            
return 0;
        }
        
else
        {
            Close();
        }
    }
    
return -1;
}

int CFTPCtrlTcpObject::Connect(const std::string& strUser, const std::string& strPasswd)
{
    
//if(_bConnected)
    
//{
    
//    SendCommand("USER " + strUser);
    
//    RecvResult();
    
//    if(_stResultInfo.m_iResult == 331)
    
//    {
    
//        SendCommand("PASS " + strPasswd);
    
//        RecvResult();
    
//        if(Is200Result() == 0)
    
//        {//set default trans mode
    
//            return CmdType(_bASCII);
    
//        }
    
//    }
    
//}

    
//return -1;

    
if(_bConnected)
    {
        SendCommand(
"USER " + strUser);
        
while(RecvResult() == 0)
        {
            
if(_stResultInfo.m_iResult == 331)
            {
                SendCommand(
"PASS " + strPasswd);
                RecvResult();
                
if(Is200Result() == 0)
                {
//set default trans mode
                    return CmdType(_bASCII);
                }
            }
        }
    }

    
return -1;
}

int CFTPCtrlTcpObject::Close()
{
    
if(_bConnected)
    {
        SendCommand(
"QUIT");
        RecvResult();
        _stTcpStreamPtr
->close();
        _bConnected 
= false;
    }
    
return 0;
}

const CFTPCtrlTcpObject::ResultInfo_t& CFTPCtrlTcpObject::GetResultInfo() const
{
    
return _stResultInfo;
}

int CFTPCtrlTcpObject::Is200Result(const ResultInfo_t& stResult) const
{
    
if(stResult.m_iResult >=200 && stResult.m_iResult < 300)
        
return 0;
    
else
        
return -1;
}

int CFTPCtrlTcpObject::Is100Result(const ResultInfo_t& stResult) const
{
    
if(stResult.m_iResult >=100 && stResult.m_iResult < 200)
        
return 0;
    
else
        
return -1;
}

int CFTPCtrlTcpObject::Is300Result(const ResultInfo_t& stResult) const
{
    
if(stResult.m_iResult >=300 && stResult.m_iResult < 400)
        
return 0;
    
else
        
return -1;
}

int CFTPCtrlTcpObject::Is200Result() const
{
    
return Is200Result(_stResultInfo);
}

int CFTPCtrlTcpObject::Is100Result() const
{
    
return Is100Result(_stResultInfo);
}

int CFTPCtrlTcpObject::Is300Result() const
{
    
return Is300Result(_stResultInfo);
}

//////////////////////////////////////////////////////////////////////////

int CFTPCtrlTcpObject::CmdChgDir(const std::string& strDir)
{
    SendCommand(
"CWD " + strDir);
    RecvResult();
    
return Is200Result();
}

int CFTPCtrlTcpObject::CmdChgLocalDir(const std::string& strDir)
{
    
return ACE_OS::chdir(strDir.c_str());
}

int CFTPCtrlTcpObject::CmdMkDir(const std::string& strDir)
{
    SendCommand(
"MKD " + strDir);
    RecvResult();
    
return Is200Result();
}

int CFTPCtrlTcpObject::CmdDelFile(const std::string& strFile)
{
    SendCommand(
"DELE " + strFile);
    RecvResult();
    
return Is200Result();
}

int CFTPCtrlTcpObject::CmdType(bool bASCII /* = true */)
{
    _bASCII 
= bASCII;
    
if(bASCII)
    {
        SendCommand(
"TYPE A");
    }
    
else
    {
        SendCommand(
"TYPE I");
    }
    RecvResult();
    
return Is200Result();
}

int CFTPCtrlTcpObject::CmdNoop()
{
    SendCommand(
"NOOP");
    RecvResult();
    
return Is200Result();
}

int CFTPCtrlTcpObject::CmdRenFile(const std::string& strOldFile, const std::string& strNewFile)
{
    SendCommand(
"RNFR " + strOldFile);
    RecvResult();
    
if(_stResultInfo.m_iResult == 350)
    {
        SendCommand(
"RNTO " + strNewFile);
        RecvResult();
        
return Is200Result();
    }
    
else
    {
        
return  -1;
    }
}

int CFTPCtrlTcpObject::CmdGetFile(const std::string& strRemoteFile, const std::string& strLocalFile)
{
    _szAffectedSize 
= 0;

    TDataTcpObjectPtr ptr(NULL);
    
if(_bPortMode)
        ptr.reset(
new CFTPPortDataTcpObject(*this));
    
else
        ptr.reset(
new CFTPPasvDataTcpObject(*this));

    
if(ptr->Get(strRemoteFile, strLocalFile, _szAffectedSize) != 0)
        
return -1;

    ptr
->Close();

    RecvResult();
    
return Is200Result();
}

int CFTPCtrlTcpObject::CmdPutFile(const std::string& strLocalFile, const std::string& strRemoteFile)
{
    _szAffectedSize 
= 0;

    TDataTcpObjectPtr ptr(NULL);
    
if(_bPortMode)
        ptr.reset(
new CFTPPortDataTcpObject(*this));
    
else
        ptr.reset(
new CFTPPasvDataTcpObject(*this));

    
if(ptr->Put(strRemoteFile, strLocalFile, _szAffectedSize) != 0)
        
return -1;

    ptr
->Close();

    RecvResult();
    
return Is200Result();    
}

int CFTPCtrlTcpObject::CmdNLst(const std::string& strFilter, std::string& strOutput)
{
    std::
string strCmd = "NLST";
    
if(!strFilter.empty())
        strCmd 
+= " " + strFilter;
    std::ostringstream ostr;
    
if(SendCmdWithDataStream(strCmd, ostr) != 0)
        
return -1;
    strOutput 
= ostr.str();
    
return 0;
}

int CFTPCtrlTcpObject::CmdList(const std::string& strFilter, std::string& strOutput)
{
    std::
string strCmd = "LIST";
    
if(!strFilter.empty())
        strCmd 
+= " " + strFilter;
    std::ostringstream ostr;
    
if(SendCmdWithDataStream(strCmd, ostr) != 0)
        
return -1;
    strOutput 
= ostr.str();
    
return 0;
}

int CFTPCtrlTcpObject::SendCmdWithDataStream(const std::string& strCmd, std::ostream& os)
{
    TDataTcpObjectPtr ptr(NULL);
    
if(_bPortMode)
        ptr.reset(
new CFTPPortDataTcpObject(*this));
    
else
        ptr.reset(
new CFTPPasvDataTcpObject(*this));

    
if(ptr->SendCmd(strCmd, os) != 0)
        
return -1;

    ptr
->Close();

    RecvResult();
    
return Is200Result();    
}

int CFTPCtrlTcpObject::SendCmdWithDataStream(const std::string& strCmd, char* pchData, size_t& szDataRead)
{
    TDataTcpObjectPtr ptr(NULL);
    
if(_bPortMode)
        ptr.reset(
new CFTPPortDataTcpObject(*this));
    
else
        ptr.reset(
new CFTPPasvDataTcpObject(*this));

    
if(ptr->SendCmd(strCmd, pchData, szDataRead) != 0)
        
return -1;

    ptr
->Close();

    RecvResult();
    
return Is200Result();    
}

size_t CFTPCtrlTcpObject::AffectedSize() 
const
{
    
return _szAffectedSize;
}

int CFTPCtrlTcpObject::GetLocalAddr(ACE_INET_Addr& stAddr)
{
    
if(!_bConnected)
        
return -1;

    _stTcpStreamPtr
->get_local_addr(stAddr);
    
return 0;
}

unsigned 
short CFTPCtrlTcpObject::GetRandPort()
{
    
int iRand = ACE_OS::rand_r(_seed);
    
while(iRand < 20000)
    {
        iRand 
= ACE_OS::rand_r(_seed);
    }
    
return iRand;
}

std::
string CFTPCtrlTcpObject::AnalyseAddr(const ACE_INET_Addr& stAddr) const
{
    std::ostringstream ostr;
    ostr 
<< stAddr.get_host_addr();
    ostr 
<< "," << stAddr.get_port_number() / 256 << "," << stAddr.get_port_number() % 256;

    std::
string str = ostr.str();
    std::
string::size_type st = str.find_first_of(".");
    
while(st != std::string::npos)
    {
        str 
= str.replace(st, 1",");
        st 
= str.find_first_of(".");
    }
    
return str;
}

ACE_INET_Addr CFTPCtrlTcpObject::AnalyseAddr(
const std::string& strAddr) const
{
    std::
string str = strAddr;
    std::ostringstream ostr;
    std::
string::size_type pos = str.find(',');
    
if(pos != std::string::npos)
        ostr 
<< str.substr(0, pos) << ".";
    
else
        
return ACE_INET_Addr("0.0.0.0:0");
    str 
= str.substr(pos + 1);

    pos 
= str.find(',');
    
if(pos != std::string::npos)
        ostr 
<< str.substr(0, pos) << ".";
    
else
        
return ACE_INET_Addr("0.0.0.0:0");
    str 
= str.substr(pos + 1);

    pos 
= str.find(',');
    
if(pos != std::string::npos)
        ostr 
<< str.substr(0, pos) << ".";
    
else
        
return ACE_INET_Addr("0.0.0.0:0");
    str 
= str.substr(pos + 1);

    pos 
= str.find(',');
    
if(pos != std::string::npos)
        ostr 
<< str.substr(0, pos) << ":";
    
else
        
return ACE_INET_Addr("0.0.0.0:0");
    str 
= str.substr(pos + 1);

    pos 
= str.find(',');
    
if(pos != std::string::npos)
        ostr 
<< atoi(str.substr(0, pos).c_str()) * 256 + atoi(str.substr(pos + 1).c_str());

    
return ACE_INET_Addr(ostr.str().c_str());
}

//////////////////////////////////////////////////////////////////////////
CFTPDataTcpObject::~CFTPDataTcpObject()
{
    Close();
}

CFTPDataTcpObject::TransStatus CFTPDataTcpObject::SelectRead(size_t 
&size)
{
    
if(!_stTcpStreamPtr->good())
        
return TS_UNKNWON;

    ACE_Handle_Set rd_set;
    rd_set.reset();
    rd_set.set_bit(_stTcpStreamPtr
->get_handle());
    
int iMaxFd = 0;
#if !defined(_WIN32)
    iMaxFd 
= _stTcpStreamPtr->get_handle() + 1;
#endif
    
if(ACE::select(iMaxFd, &rd_set, NULL, NULL, &_stTimeout) <= 0)
    {
        
return TS_TIMEOUT;
    }

    _stTcpStreamPtr
->under_flow();
    size 
= _stTcpStreamPtr->in_avail();

    
return TS_READ;
}

CFTPDataTcpObject::TransStatus CFTPDataTcpObject::SelectWrite()
{
    
if(!_stTcpStreamPtr->good())
        
return TS_UNKNWON;

    ACE_Handle_Set wr_set;
    wr_set.reset();
    wr_set.set_bit(_stTcpStreamPtr
->get_handle());
    
int iMaxFd = 0;
#if !defined(_WIN32)
    iMaxFd 
= _stTcpStreamPtr->get_handle() + 1;
#endif
    
if(ACE::select(iMaxFd, NULL, &wr_set, NULL, &_stTimeout) <= 0)
    {
        
return TS_TIMEOUT;
    }
    
return TS_WRITE;
}

int CFTPDataTcpObject::Read(char*& data, size_t& size)
{
    size 
= 0;
    data 
= NULL;

    
while(_stTcpStreamPtr->good())
    {
        
if(_bStop)
            
return -1;

        size_t szRead 
= 0;
        TransStatus eTrans 
= SelectRead(szRead);
        
if(eTrans == TS_READ)
        {
            
if(szRead == 0)
                
break;
            
char* pch = new char[szRead];
            
if(_stTcpStreamPtr->read(pch, szRead).good())
            {
                
if(data != NULL)
                {
                    delete [] data;
                    data 
= new char[size + szRead];
                }
                memcpy(data 
+ size, pch, szRead);
                size 
+= szRead;
            }
            
else
            {
                delete [] pch;
                
return -1;
            }
            delete [] pch;
        }
        
else if(eTrans == CFTPDataTcpObject::TS_TIMEOUT)
        {
            
continue;
        }
        
else
        {
            
return -1;
        }
    }

    
return 0;
}

int CFTPDataTcpObject::Write(const char* data, size_t size)
{
    size_t szWrite 
= 0;

    
while(szWrite < size)
    {
        
if(_bStop)
            
return -1;

        TransStatus eTrans 
= SelectWrite();
        
if(eTrans == CFTPDataTcpObject::TS_WRITE)
        {
            
if(size - szWrite >= 5120)
            {
                _stTcpStreamPtr
->write(data + szWrite, 5120);
                
if(!_stTcpStreamPtr->flush().good())
                    
return -1;
            }
            
else
            {
                _stTcpStreamPtr
->write(data + szWrite, size - szWrite);
                
if(!_stTcpStreamPtr->flush().good())
                    
return -1;
            }
            szWrite 
+= 5120;
        }
        
else if(eTrans == CFTPDataTcpObject::TS_TIMEOUT)
        {
            
continue;
        }
        
else
        {
            
break;
        }
    }

    
return 0;
}

int CFTPDataTcpObject::Read(std::ofstream &ofile, size_t& size)
{
    
if(!ofile.good())
        
return -1;

    size 
= 0;

    
while(_stTcpStreamPtr->good())
    {
        
if(_bStop)
            
return -1;

        size_t szRead 
= 0;
        TransStatus eTrans 
= SelectRead(szRead);
        
if(eTrans == TS_READ)
        {
            
if(szRead == 0)
                
break;
            
char* pch = new char[szRead];
            
if(_stTcpStreamPtr->read(pch, szRead).good())
            {
                ofile.write(pch, szRead);
                size 
+= szRead;
                
if(!ofile.good())
                {
                    delete [] pch;
                    
return -1;
                }
            }
            
else
            {
                delete [] pch;
                
return -1;
            }
            delete [] pch;
        }
        
else if(eTrans == CFTPDataTcpObject::TS_TIMEOUT)
        {
            
continue;
        }
        
else
        {
            
return -1;
        }
    }

    
return 0;
}

int CFTPDataTcpObject::Write(std::ifstream &ifile, size_t &size)
{
    
if(!ifile.good())
        
return -1;
    size 
= 0;

    
char buf[5120];
    
while(!ifile.eof() && ifile.good())
    {
        
if(_bStop)
            
return -1;

        TransStatus eTrans 
= SelectWrite();
        
if(eTrans == CFTPDataTcpObject::TS_WRITE)
        {    
            size_t szRead 
= ifile.read(buf, 5120).gcount();
            
if(szRead > 0)
            {
                _stTcpStreamPtr
->write(buf, szRead);
                
if(!_stTcpStreamPtr->flush().good())
                    
return -1;
                size 
+= szRead;
            }
            
else
            {
                
break;
            }
        }
        
else if(eTrans == CFTPDataTcpObject::TS_TIMEOUT)
        {
            
continue;
        }
        
else
        {
            
break;
        }
    }
    
return 0;
}


int CFTPDataTcpObject::Read(std::ostream &os, size_t& size)
{
    
if(!os.good())
        
return -1;

    size 
= 0;

    
while(_stTcpStreamPtr->good())
    {
        
if(_bStop)
            
return -1;

        size_t szRead 
= 0;
        TransStatus eTrans 
= SelectRead(szRead);
        
if(eTrans == TS_READ)
        {
            
if(szRead == 0)
                
break;
            
char* pch = new char[szRead];
            
if(_stTcpStreamPtr->read(pch, szRead).good())
            {
                os.write(pch, szRead);
                size 
+= szRead;
                
if(!os.good())
                {
                    delete [] pch;
                    
return -1;
                }
            }
            
else
            {
                delete [] pch;
                
return -1;
            }
            delete [] pch;
        }
        
else if(eTrans == CFTPDataTcpObject::TS_TIMEOUT)
        {
            
continue;
        }
        
else
        {
            
return -1;
        }
    }

    
return 0;
}

void CFTPDataTcpObject::Close()
{
    
if(_stTcpStreamPtr.get() != NULL)
    {
        _stTcpStreamPtr
->close();
        _stTcpStreamPtr.reset(NULL);
    }    
}

////
CFTPPortDataTcpObject::CFTPPortDataTcpObject(CFTPCtrlTcpObject &ctrlobject, size_t buffsize, const ACE_Time_Value &timeout)
: CFTPDataTcpObject(ctrlobject, buffsize, timeout)
{
}

CFTPPortDataTcpObject::
~CFTPPortDataTcpObject()
{
}

int CFTPPortDataTcpObject::Get(const std::string &remote, const std::string &local, size_t& size)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    std::ios_base::openmode mode 
= std::ios::trunc | std::ios::out;
    
if(!_stCtrlObject.IsASCII())
        mode 
|= std::ios_base::binary;

    std::ofstream ofile(local.c_str(), mode);

    
if(!ofile.is_open() || !ofile.good())
        
return -1;

    
if(Open() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Get>open() failed." << std::endl);
        
return -1;
    }
    
if(Port() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Get>port() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand("RETR " + remote);
    _stCtrlObject.RecvResult();
    
if(_stCtrlObject.Is100Result() != 0)
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Get>RETR command failed." << std::endl);
        
return -1;
    }

    
if(Accept() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Get>accept() failed." << std::endl);
        
return -1;
    }
    
if(Read(ofile, size) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Get>Read() failed." << std::endl);
        
return -1;
    }

    ofile.close();

    
return 0;
}

int CFTPPortDataTcpObject::Put(const std::string& remote, const std::string& local, size_t& size)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    std::ios_base::openmode mode 
= std::ios_base::in;
    
if(!_stCtrlObject.IsASCII())
        mode 
|= std::ios_base::binary;

    std::ifstream ifile(local.c_str(), mode);
    
if(!ifile.is_open() || !ifile.good())
        
return -1;

    
if(Open() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Put>open() failed." << std::endl);
        
return -1;
    }
    
if(Port() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Put>port() failed." << std::endl);
        
return -1;
    }
//    CFTPCtrlTcpObject::ResultInfo_t result;

    _stCtrlObject.SendCommand(
"STOR " + remote);
    _stCtrlObject.RecvResult();
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Put>STOR command failed." << std::endl);
        
return -1;
    }
    
if(Accept() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Put>accept() failed." << std::endl);
        
return -1;
    }
    
if(Write(ifile, size) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::Put>Write() failed." << std::endl);
        
return -1;
    }

    ifile.close();

    
return 0;
}

int CFTPPortDataTcpObject::SendCmd(const std::string& strCmd, char*& pchData, size_t& szDataRead)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    
if(Open() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>open() failed." << std::endl);
        
return -1;
    }
    
if(Port() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>port() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand(strCmd);
    _stCtrlObject.RecvResult();
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>" << strCmd << " command failed." << std::endl);
        
return -1;
    }

    
if(Accept() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>accept() failed." << std::endl);
        
return -1;
    }

    
if(Read(pchData, szDataRead) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>Read() failed." << std::endl);
        
return -1;
    }

    
return 0;
}

int CFTPPortDataTcpObject::SendCmd(const std::string& strCmd, std::ostream& os)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    
if(Open() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>open() failed." << std::endl);
        
return -1;
    }
    
if(Port() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>port() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand(strCmd);
    _stCtrlObject.RecvResult();
    
//if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>" << strCmd << " command failed." << std::endl);
        
return -1;
    }

    
if(Accept() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>accept() failed." << std::endl);
        
return -1;
    }

    size_t szDataRead 
= 0;
    
if(Read(os, szDataRead) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPortDataTcpObject::SendCmd>Read() failed." << std::endl);
        
return -1;
    }

    
return 0;
}

int CFTPPortDataTcpObject::Open()
{
    _stCtrlObject.GetLocalAddr(_stLocalAddr);
    _stLocalAddr.set_port_number(_stCtrlObject.GetRandPort());

    
while(_stAcceptor.open(_stLocalAddr) != 0)
    {
        _stLocalAddr.set_port_number(_stCtrlObject.GetRandPort());
    }

    _bOpen 
= true;

    
return 0;
}

int CFTPPortDataTcpObject::Port()
{
//    CFTPCtrlTcpObject::ResultInfo_t result;

    std::
string strPort = _stCtrlObject.AnalyseAddr(_stLocalAddr);
    _stCtrlObject.SendCommand(
"PORT " + strPort);
    _stCtrlObject.RecvResult();
    
if(_stCtrlObject.Is200Result() != 0)
        
return -1;

    
return 0;    
}

int CFTPPortDataTcpObject::Accept()
{
    ACE_Handle_Set rd_set;
    rd_set.reset();
    rd_set.set_bit(_stAcceptor.get_handle());
    
int iMaxFd = 0;
#if !defined(_WIN32)
    iMaxFd 
= _stAcceptor.get_handle() + 1;
#endif
    
if(ACE::select(iMaxFd, &rd_set, NULL, NULL, &_stTimeout) > 0)
    {
        
if(rd_set.is_set(_stAcceptor.get_handle()))
        {
            _stTcpStreamPtr.reset(
new ACEX_TcpStream(_szBuffSize));
            
if(_stAcceptor.accept(*_stTcpStreamPtr.get()) == 0)
            {
                _stTcpStreamPtr
->block(0);
            }
            
else
            {
                _stTcpStreamPtr.reset(NULL);
            }
        }
    }
    _stAcceptor.close();

    
if(_stTcpStreamPtr.get() != NULL)
        
return 0;
    _bOpen 
= false;
    
return -1;
}

//
CFTPPasvDataTcpObject::CFTPPasvDataTcpObject(CFTPCtrlTcpObject& ctrlobject, size_t buffsize /* = 64 * 1024 */const ACE_Time_Value& timeout /* = ACE_Time_Value */)
: CFTPDataTcpObject(ctrlobject, buffsize, timeout)
{
}

CFTPPasvDataTcpObject::
~CFTPPasvDataTcpObject()
{
}

int CFTPPasvDataTcpObject::Get(const std::string &remote, const std::string &local, size_t &size)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    std::ios_base::openmode mode 
= std::ios::trunc | std::ios::out;
    
if(!_stCtrlObject.IsASCII())
        mode 
|= std::ios_base::binary;

    std::ofstream ofile(local.c_str(), mode);

    
if(!ofile.is_open() || !ofile.good())
        
return -1;

    
if(Pasv() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Get>Pasv() failed." << std::endl);
        
return -1;
    }

    
if(Connect() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Get>Connect() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand("RETR " + remote);
    _stCtrlObject.RecvResult();
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Get>RETR command failed." << std::endl);
        
return -1;
    }

    
if(Read(ofile, size) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Get>Read() failed." << std::endl);
        
return -1;
    }

    ofile.close();

    
return 0;
}

int CFTPPasvDataTcpObject::Put(const std::string& remote, const std::string& local, size_t& size)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    std::ios_base::openmode mode 
= std::ios_base::in;
    
if(!_stCtrlObject.IsASCII())
        mode 
|= std::ios_base::binary;

    std::ifstream ifile(local.c_str(), mode);
    
if(!ifile.is_open() || !ifile.good())
        
return -1;

    
if(Pasv() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Put>Pasv() failed." << std::endl);
        
return -1;
    }
    
if(Connect() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Put>Connect() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand("STOR " + remote);
    _stCtrlObject.RecvResult();
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Put>STOR command failed." << std::endl);
        
return -1;
    }

    
if(Write(ifile, size) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::Put>Write() failed." << std::endl);
        
return -1;
    }

    ifile.close();

    
return 0;
}

int CFTPPasvDataTcpObject::SendCmd(const std::string& strCmd, char*& pchData, size_t& szDataRead)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    
if(Pasv() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>Pasv() failed." << std::endl);
        
return -1;
    }

    
if(Connect() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>Connect() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand(strCmd);
    _stCtrlObject.RecvResult();
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>" << strCmd << " command failed." << std::endl);
        
return -1;
    }

    
if(Read(pchData, szDataRead) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>Read() failed." << std::endl);
        
return -1;
    }

    
return 0;
}

int CFTPPasvDataTcpObject::SendCmd(const std::string& strCmd, std::ostream& os)
{
    
if(!_stCtrlObject.IsConnected())
        
return -1;

    
if(Pasv() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>Pasv() failed." << std::endl);
        
return -1;
    }

    
if(Connect() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>Connect() failed." << std::endl);
        
return -1;
    }

//    CFTPCtrlTcpObject::ResultInfo_t result;
    _stCtrlObject.SendCommand(strCmd);
    _stCtrlObject.RecvResult();
//    if(result.m_iResult < 100 || result.m_iResult > 199)
    if(_stCtrlObject.Is100Result() != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>" << strCmd << " command failed." << std::endl);
        
return -1;
    }

    size_t szDataRead 
= 0;
    
if(Read(os, szDataRead) != 0)
    {
        ACEX_LOG_OS(LM_ERROR, 
"<CFTPPasvDataTcpObject::SendCmd>Read() failed." << std::endl);
        
return -1;
    }

    
return 0;
}

int CFTPPasvDataTcpObject::Pasv()
{
//    CFTPCtrlTcpObject::ResultInfo_t result;

    _stCtrlObject.SendCommand(
"PASV");
    _stCtrlObject.RecvResult();
    
if(_stCtrlObject.ResultCode() != 227)
    {
        
return -1;
    }
    std::
string info = _stCtrlObject.ResultInfo();
    std::
string str = "";
//    unsigned short port = 0; 
//    size_t pos = 0;
    for(std::string::const_iterator it = info.begin(); it != info.end(); ++ it)
    {
        
if((*it >= '0' && *it <= '9'|| (*it == ','))
        {
            str 
+= *it;
        }
    }
    _stRemoteAddr 
= _stCtrlObject.AnalyseAddr(str);

    
return 0;    
}

int CFTPPasvDataTcpObject::Connect()
{
    _stTcpStreamPtr.reset(
new ACEX_TcpStream(_szBuffSize));
    
if(_stConnector.connect(*_stTcpStreamPtr.get(), _stRemoteAddr, &_stTimeout) == 0)
    {
        _stTcpStreamPtr
->block(0);
    }
    
else
    {
        _stTcpStreamPtr.reset(NULL);
    }

    
if(_stTcpStreamPtr.get() != NULL)
        
return 0;
    _bOpen 
= false;
    
return -1;
}


posted on 2009-07-31 10:22 codejie 阅读(1307) 评论(14)  编辑 收藏 引用 所属分类: C++

评论

# re: 轮子:FTP接口实现 2009-08-06 14:27 uwinb

我也做过这样的轮子,不过我在实现FTP客户端之前先干了一件事,就是按照流对象的概念封装了套接口,让它自己智能地维护缓冲区!在单线程环境下,客户端也能实现以PORT方式登录Unix的FTP服务端。还有遇到一个问题,就是调用一次recv()不一定就能收全数据,可能需要反复测试可读性和调用接收函数,这也是封装套接口的原因之一。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-08-06 17:28 codejie

我是按照FTP的思想封装的,分Contral和Data两个对象,然后再分Pasv和Port两个Data子对象。
你说到的流对象封装,我是在Data中作的~
嘿嘿,咱们做轮子的想法都一样~  回复  更多评论   

# re: 轮子:FTP接口实现 2009-08-08 19:19 uwinb

FTP其实就是个有问有答的协商协议,Pasv和Port只不过是两种登录的方式,在我的方案中发现其中一种失效会自动去尝试另一种,不管咋的建立联接以后的数据传递没啥子区别。所以对你的类框架模型稍有质疑,呵呵!
还有涉及访问网络这种充满不可预测的事件的对象,你居然不使用异常来反馈错误!学术讨论而已,不必介意我的观点。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-08-09 20:16 codejie

先说异常的问题,你说的很对,在第一版本时,实现中都是异常方式,由于外层调用的代码不是我写的,并且不喜欢异常,我被迫改成0和-1方式了,因此也增加了很多自定义的错误码~实话说,我还是喜欢异常方式的,嘿嘿~
关于PASV和PORT方式,我认为不是你说的‘登录’方式,而应该是数据传输的两种连接模式,当然如果你说的‘登录’就是指这个连接,那么我们就一样了~我说明一下吧,PASV模式是指在数据传输时,服务器端做Server;而PORT模式相反。具体可以参看FTP协议。
我不知道你方案中的“发现其中一种失效会自动去尝试另一种”是何种需求下实现的,我这里只是对两种模式的对象封装,至于如何使用这些对象,是上层调用去决定的。像某些FTP服务端为了防止‘请求’方式攻击,而仅支持PORT方式。所以这里的对象自身无需去考虑“失效”和“尝试”的问题。

我很介意你的观点,因为我认为--不同的思想只有去碰撞,才能共同改善~  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-08 19:57 白云深

谢谢楼主的代码。

另外有个问题请教一下:“ACEX_TcpStream”这个对象是什么库里面的,还是楼主自己封装的?  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-08 22:38 codejie

客气了~
ACEX_TcpStream类是对Socket的封装,出自工作中使用的ACEX库,这个不是我封的,从功能看应该基于Socket和Stream两个基类。把这个类看成Socket流对象就可以了,这个不是重点了。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-08 23:31 白云深

多谢楼主答复。

本人是个菜鸟,所以细节上有好多问题不明白。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-09 21:32 codejie

客气了~有问题,咱一起想~  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-09 21:46 白云深

呵呵,谢谢楼主的热心。

虽然不耻下问是个好习惯,但基础的东西还是要靠自己的学习,自己不学习随意张口问别人也不是学习的好态度,同时也是浪费别人的时间和自己的智商。不说了,学习了,呵呵。以后会经常来博主这,向博主请益,博主可要勤奋点哦,博主多写文章就是对俺们菜鸟最大的帮助。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-09 22:35 codejie

求你多来问吧,么发现近来代码越来越少了吗?这里都成了我发牢骚的地方了。你的问题也是让我前进的动力~  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-10 21:39 白云深

今天把博主的代码整理了一下,暂时调试了一下port模式。把下面这个函数

int CFTPDataTcpObject::Read(char*& data, size_t& size)

改为:

int CFTPDataTcpObject::Read(std::string & data)

博主的实现中,用new来动态分配内存,但好像没有释放,按博主的实现,这个释放动作应该要由上层调用来完成,不知道我的理解是否正确。如果所说无误,这是否可以理解为这个接口会造成泄漏或与上层程序有耦合?

另外,如果不用std::string来返回数据以防止内存泄露,个人以为改为类似于系统调用ssize_t read (void * buf, unsinged long)的接口更为合理,由返回值指出实际所读取数据的长度,而第二个参数来指定上层用户所使用的缓冲区的大小,但若这样实现必须考虑用户一次无法读取所以数据的情况。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-10 21:42 白云深

晕,刚才又仔细看了下,那个接口好像在实际应用中并不会被调用,这样的话上面我怀疑的问题也就不是问题了。  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-12 22:17 codejie

@白云深
这里先说明一下Read和Write,这两个函数和Port或者Pasv方式没有关系,就是从Socket上读数据和写数据,因此这两个函数放在FTP数据传输链路的基类CFTPDataTcpObject中。两个函数不同的参数完成不同的需求,如下:
int Read(char*& data, size_t& size);
用于从Socket接收数据到data中,接收的长度放在size中返回;
int Read(std::ofstream& ofile, size_t& size);
用于从Scket接收数据到文件ofile中,接收的长度放在size中返回;
int Read(std::ostream& os, size_t& size);
用于从Socket接收数据到输出流os中,接收的长度放在size中返回;
int Write(const char* data, size_t size);
用于将data中的数据写入到Socket中;
int Write(std::ifstream& ifile, size_t& size);
用于将输入文件ifile的数据写入到Socket中;

在我们使用中,FTP数据操作多数是针对文件的,因此下面两个函数方式是最常用的:
int Read(std::ofstream& ofile, size_t& size);
int Write(std::ifstream& ifile, size_t& size);

因此,我觉得你尝试覆盖上面的函数应该会被调用到,除非你不是针对文件操作,就像使用FTP的LIST命令,是将结果放在输出流或者一块空间中。

关于你的问题:
1.String代替data,这种方式是可行的,只是使用起来应该比较危险和不方便,虽然string可以存放非可见字符,但显示中很少用string来操作他们;简单地说,你不能确定FTP只是传文本文件吧?传个图片什么的,FTP应该支持吧?
2.关于data被new而没有delete的问题,先看看函数声明:
int Read(char*& data, size_t& size);
可见data是传出参数,如果函数里面就delete了,那调用这个函数还有什么意义呢?如果data由调用方new好,但调用方(上层程序)又怎么预先知道new多少空间够呢?这种由函数自己new,而由调用方管理的定义方式应该算常见的,简单说当使用类似(char*&)这样的参数时,就该意识到,使用者有义务管理好返回的指针。(多说一句:函数参数char*和char*&是不同的)
  回复  更多评论   

# re: 轮子:FTP接口实现 2009-09-14 23:20 白云深

受教了。

呵呵,看着又是指针又是引用有点晕,还是自己写的代码太少了。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

Using C++

导航

统计

留言簿(73)

随笔分类(513)

积分与排名

最新评论

阅读排行榜

评论排行榜