C++ Forever

my feeling and C++'s
posts - 5, comments - 12, trackbacks - 0, articles - 0

我们网络实验使用socket实现一个ftp客户端,前几天我一个很困惑一些技术上的问题,因为在网上基本上没有这样的样例,所以我将代码贴了上来,不是完整的代码,只有一个核心部分,我想要实现的就是像QFtp一样的功能,不过还有很多地方要完善。

/*
* HLftp.h
* \date 2008-04-22
* \author HanYin
*/


#ifndef HL_FTP_H
#define HL_FTP_H

#include 
"HLutil.h"

#include 
<QtNetwork>
#include 
<QString>

class QUrlInfo;

namespace hy
{
    
class HLftp : public QObject
    
{
        Q_OBJECT
    
public :
        
explicit HLftp( QObject * parent =NULL );
        
~HLftp();

        
enum State {
            Unconnected,
            HostLookup,
            Connecting,
            Connected,
            LoggingIn,
            LoggedIn,
            Closing,
            Closed
        }
;

        
enum Command {
            None,
            SetTransferMode,
            SetProxy,
            ConnectToHost,
            Login,
            Close,
            List,
            Cd,
            Get,
            Put,

            Remove,    
// haven't accomplished
            Mkdir,
            Rmdir,
            Rename,
            RawCommand
        }
;

        
enum TransferType {
            Binary,
            Ascii
        }
;

        
enum TransferMode {
            Active,
            Passive
        }
;

    
public :
        
/**
        * Connects to an FTP server and logs in with the supplied username and* password.
        
*/

        
int connectToHost( const QString & hostname, uint port = 21 );

        
/**
        * logs in with the supplied username and* password.
        
*/

        
int login( const QString & username = QString("anonymous"), const QString & password = QString("anonymous") );

        
/**
        * Disconnects from the FTP server.
        
*/

        
int disconnect();

        
/**
        * Get the directory dir of the FTP server it is connected to.
        * Default to working directory.
        
*/

        
int list( const QString &dir = QString() );

        
/**
        * Changes the working directory (like cd). Returns true if successful.
        
*/

        
int cwd( const QString & dir );

        
/**
        * Enter binary mode for sending binary files.
        
*/

        
int bin();

        
/**
        * Enter ASCII mode for sending text files. This is usually the default mode.
        * Make sure you use binary mode if you are sending images or other binary
        * data, as ASCII mode is likely to corrupt them.
        
*/

        
int ascii();

        
/**
        * Sends a file to be stored on the FTP server. 
        * The file is sent in passive mode to avoid NAT or
        * firewall problems at the client end.
        
*/

        
int put( QIODevice *dev, const QString &file );

        
/**
        * download a file from the FTP server.
        * The file is sent in passive mode to avoid NAT or
        * firewall problems at the client end.
        
*/

        
int getconst QString &file, QIODevice *dev );

        
/**
        * Get information of Server system.
        
*/

        
int syst();

        
/**
        * Cut down current data link.
        
*/

        
int abort();

        
/**
        * Set transfer mode.
        
*/

        
int setTransferMode( TransferMode mode );

        
/**
        * get current state.
        
*/

        State currentState() 
const;

        
/**
        * get current command.
        
*/

        Command currentCommand() 
const;

        
/**
        * get current transfer type.
        
*/

        TransferType currentTransferType() 
const;

        
/**
        * get current transfer type.
        
*/

        
void openDebug( bool b )
        
{ m_bDebug = b; }

signals :
        
void commandStarted( int id );
        
void commandFinished( int id, bool error );
        
void listInfo(const QUrlInfo & url);

        
void readyRead();

        
void dataTransferProgress(qint64, qint64);        // haven't accomplished.

    
protected :
        
/**
        * Returns the working directory of the FTP server it is connected to.
        
*/

        QString pwd();

        
/**
        * Send a raw command to the FTP server.
        
*/

        
void sendLine( QString cmd );

        
/**
        * Get a raw command to the FTP server.
        
*/

        QString getLine();

    
private :
        QTcpSocket 
* m_pControlSocket;
        QTcpSocket 
* m_pDataSocket;

        State m_eCurrentState;
        Command m_eCurrentCommand;
        TransferType m_eTransferType;
        TransferMode m_eTransferMode;
        ServerSystemType m_eServerSystemType;

        QString m_sDataIP;
        
uint m_iDataPort;
        
bool m_bSetModeSuccess;

        
// all ids for states.
        int connect_id;
        
int login_id;
        
int close_id;
        
int list_id;
        
int pwd_id;
        
int cwd_id;
        
int bin_id;
        
int ascii_id;
        
int put_id;
        
int get_id;
        
int transfertype_id;
        
int syst_id;
        
int abort_id;

        
// For Debug.
        bool m_bDebug;

    }
;

    inline HLftp::State HLftp::currentState() 
const
    
{
        
return m_eCurrentState;
    }


    inline HLftp::Command HLftp::currentCommand() 
const 
    
{
        
return m_eCurrentCommand;
    }


    inline HLftp::TransferType HLftp::currentTransferType() 
const
    
{
        
return m_eTransferType;
    }

}


#endif

/*
* HLftp.cpp
* \date 2008-04-22
* \author HanYin
*/


#include 
"HLftp.h"

#include 
<QDataStream>
#include 
<QUrlInfo>

namespace hy
{
    HLftp::HLftp( QObject 
* parent )
        : m_pDataSocket(NULL), m_pControlSocket(NULL)
    
{
        m_bDebug 
= true;
        m_eCurrentState 
= Unconnected;
        m_eCurrentCommand 
= None;
        m_eTransferType 
= Ascii;
        m_eTransferMode 
= Active;
        m_eServerSystemType 
= SYS_OTHERS;
        m_pControlSocket 
= new QTcpSocket();

        
// temporarily for this.
        connect_id = 100;
        login_id 
= 200;
        close_id 
= 300;
    }


    HLftp::
~HLftp()
    
{
        
if ( NULL != m_pControlSocket )
            delete m_pControlSocket;
        
if ( NULL != m_pDataSocket )
            delete m_pDataSocket;
    }


    
int HLftp::connectToHost( const QString & hostname, uint port )
    
{
        
// if the socket is busy, close it.
        if ( m_pControlSocket->isOpen() )
            m_pControlSocket
->close();

        
// connect to the host
        m_eCurrentState = Connecting;
        m_eCurrentCommand 
= ConnectToHost;
        emit commandStarted(connect_id);

        m_pControlSocket
->connectToHost( hostname, port );
        QString response 
= getLine();
        
if ( ! response.startsWith("220") )
        
{
            emit commandFinished( connect_id, 
true );        // connect failure
            return connect_id;
        }

        
else
        
{
            emit commandFinished( connect_id, 
false );        // connect successfully
        }


        
// connect to the host
        m_eCurrentState = Connected;
        
return connect_id;
    }


    
int HLftp::login( const QString & username, const QString & password )
    
{
        
// set current state to be logging in.
        m_eCurrentState = LoggingIn;
        m_eCurrentCommand 
= Login;
        emit commandStarted(login_id);


        
// send user name and password if needed
        sendLine( QObject::tr("USER "+ username );
        QString response 
= getLine();
        
if ( !response.startsWith("331"&& !response.startsWith("230") )
        
{
            emit commandFinished( login_id, 
true );
            
return login_id;
        }


        
// if needing password
        if ( response.startsWith("331") )
        
{
            sendLine( QObject::tr(
"PASS "+ password );
            response 
= getLine();
            
if ( ! response.startsWith("230") )
            
{
                emit commandFinished( login_id, 
true );
                
return login_id;
            }

            
else
            
{
                emit commandFinished( login_id, 
false );
            }

        }

        
else
        
{
            emit commandFinished( login_id, 
false );
        }


        
// set current state to be connected.
        m_eCurrentState = LoggedIn;
        
return login_id;
    }


    
int HLftp::disconnect()
    
{
        
// set current state to be closing.
        m_eCurrentState = Closing;
        m_eCurrentCommand 
= Close;
        emit commandStarted(close_id);

        
// Aborts the current command and deletes all scheduled commands. 
        m_pControlSocket->abort();

        
// Closes the connection to the FTP server.
        m_pControlSocket->close();

        emit commandFinished( close_id, 
false );

        
// set current state to be closing.
        m_eCurrentState = Closed;
        
return close_id;
    }


    
int HLftp::list( const QString &dir )
    
{
        m_eCurrentCommand 
= List;
        
// Get directory to be listed.
        QString directory = dir;
        
if ( directory.isEmpty() )  //  if empty, current working directory.
        {
            directory 
= pwd();
        }


        
// sending command, make it passive.
        sendLine("PASV");

        QString response 
= getLine();
        
if ( ! response.startsWith("227") )
        
{
            emit commandFinished( get_id, 
true );
            
return list_id;
        }

        
else
        
{
            m_eTransferMode 
= Passive;
        }

        
int begin = response.indexOf('('+1;
        
int end = response.indexOf( ')', begin );
        QString substring 
= response.mid( begin, end-begin );        // cut out ip and data port.

        
int i;
        QString temp_ip;
        temp_ip.clear();
        
// get IP string.
        end =-1;
        
for ( i=0; i<3; i++ )
        
{
            begin 
= end +1;
            end 
= substring.indexOf( ',', begin+1 );
            temp_ip.append( substring.mid( begin, end
-begin ) );
            temp_ip.append(
".");
        }


        begin 
= end +1;
        end 
= substring.indexOf( ',', begin+1 );
        temp_ip.append( substring.mid( begin, end
-begin ) );

        
// get port.
        uint temp_port =0;
        begin 
= end +1;
        end 
= substring.indexOf( ',', begin );
        temp_port 
= substring.mid( begin, end-begin ).toUInt() * 256;

        begin 
= end +1;
        temp_port 
+= substring.mid( begin, -1 ).toUInt();

        
// Create a new data link from Server to get all lists.
        emit commandStarted(list_id);
        sendLine( QObject::tr(
"LIST "+ directory );

        m_pDataSocket 
= new QTcpSocket(this);
        m_pDataSocket
->connectToHost( temp_ip, temp_port );
        m_pDataSocket
->write( "USER\r\n"8 );

        response 
= getLine();
        
if (! response.startsWith("150") )
        
{
            emit commandFinished( put_id, 
true );
            
return list_id;
        }


        
char buffer[500];
        
int byteRead = 0;
        
while (true{
            
if ( ! m_pDataSocket->waitForReadyRead() )
                
break;

            byteRead 
= m_pDataSocket->readLine( buffer, 500 );
            QString temp_string(buffer);
            QUrlInfo url 
= HLutil::parseFileList( temp_string, m_eServerSystemType );
            emit listInfo( url );
        }


        m_pDataSocket
->flush();
        m_pDataSocket
->close();
        delete m_pDataSocket;

        response 
= getLine();
        
if ( ! response.startsWith("226 ") )
        
{
            emit commandFinished( get_id, 
true );
        }

        
else
        
{
            emit commandFinished( get_id, 
false );
        }


        
return list_id;
    }


    
int HLftp::cwd( const QString & dir )
    
{
        m_eCurrentCommand 
= Cd;
        emit commandStarted(cwd_id);
        
// sending command.
        sendLine( QObject::tr("CWD "+ dir );

        QString response 
= getLine();
        
if ( ! response.startsWith("250") )
        
{
            emit commandFinished( cwd_id, 
true );
            
return cwd_id;
        }

        
else
        
{
            emit commandFinished( cwd_id, 
false );
            
return cwd_id;
        }

    }


    
int HLftp::bin()
    
{
        emit commandStarted(bin_id);
        
// sending command.
        sendLine( QObject::tr("TYPE I") );

        QString response 
= getLine();
        
if ( ! response.startsWith("200") )
        
{
            m_eTransferType 
= Binary;
            emit commandFinished( bin_id, 
true );
            
return bin_id;
        }

        
else
        
{
            emit commandFinished( bin_id, 
false );
            
return bin_id;
        }

    }


    
int HLftp::ascii()
    
{
        emit commandStarted(ascii_id);
        
// sending command.
        sendLine( QObject::tr("TYPE A") );

        QString response 
= getLine();
        
if ( ! response.startsWith("200") )
        
{
            m_eTransferType 
= Ascii;
            emit commandFinished( ascii_id, 
true );
            
return ascii_id;
        }

        
else
        
{
            emit commandFinished( ascii_id, 
false );
            
return ascii_id;
        }

    }


    
int HLftp::put(QIODevice *dev, const QString &file )
    
{
        m_eCurrentCommand 
= Put;
        
// sending command, make it passive.
        sendLine("PASV");

        QString response 
= getLine();
        
if ( ! response.startsWith("227") )
        
{
            emit commandFinished( put_id, 
true );
            
return put_id;
        }

        
else
        
{
            m_eTransferMode 
= Passive;
        }

        
int begin = response.indexOf('(')+1;
        
int end = response.indexOf(')', begin);
        QString substring 
= response.mid( begin, end-begin );        // cut out ip and data port.

        
int i;
        QString temp_ip;
        temp_ip.clear();
        
// get IP string.
        for ( i=0; i<4; i++ )
        
{
            begin 
= end +1;
            end 
= substring.indexOf( ',', begin );
            temp_ip.append( substring.mid( begin, end
-begin ) );
        }


        
// get port.
        uint temp_port =0;
        begin 
= end +1;
        end 
= substring.indexOf( ',', begin );
        temp_port 
= substring.mid( begin, end-begin ).toUInt() * 256;

        begin 
= end +2;
        temp_port 
+= substring.mid( begin, -1 ).toUInt();

        
// now set up a new data link use given ip and port.
        emit commandStarted(put_id);
        sendLine( QObject::tr(
"STOR "+ file );

        m_pDataSocket 
= new QTcpSocket();
        m_pDataSocket
->connectToHost( temp_ip, temp_port );

        response 
= getLine();
        
if (! response.startsWith("150 ") )
        
{
            emit commandFinished( put_id, 
true );
            
return put_id;
        }


        
char buffer[4096];
        
int byteRead = 0;
        memset( buffer, 
04096 );
        
while ( ( byteRead = dev->read( buffer, 4096 ) ) != -1 ) {
            m_pDataSocket
->write( buffer, byteRead );
        }


        m_pDataSocket
->flush();
        m_pDataSocket
->close();
        dev
->close();
        delete m_pDataSocket;

        response 
= getLine();
        
if ( ! response.startsWith("226 ") )
        
{
            emit commandFinished( put_id, 
true );
        }

        
else
        
{
            emit commandFinished( put_id, 
false );
        }


        
return put_id;

    }


    
int HLftp::getconst QString &file, QIODevice * dev )
    
{
        m_eCurrentCommand 
= Get;

        
// sending command, make it passive.
        sendLine("PASV");

        QString response 
= getLine();
        
if ( ! response.startsWith("227") )
        
{
            emit commandFinished( get_id, 
true );
            
return list_id;
        }

        
else
        
{
            m_eTransferMode 
= Passive;
        }

        
int begin = response.indexOf('('+1;
        
int end = response.indexOf( ')', begin );
        QString substring 
= response.mid( begin, end-begin );        // cut out ip and data port.

        
int i;
        QString temp_ip;
        temp_ip.clear();
        
// get IP string.
        end =-1;
        
for ( i=0; i<3; i++ )
        
{
            begin 
= end +1;
            end 
= substring.indexOf( ',', begin+1 );
            temp_ip.append( substring.mid( begin, end
-begin ) );
            temp_ip.append(
".");
        }


        begin 
= end +1;
        end 
= substring.indexOf( ',', begin+1 );
        temp_ip.append( substring.mid( begin, end
-begin ) );

        
// get port.
        uint temp_port =0;
        begin 
= end +1;
        end 
= substring.indexOf( ',', begin );
        temp_port 
= substring.mid( begin, end-begin ).toUInt() * 256;

        begin 
= end +1;
        temp_port 
+= substring.mid( begin, -1 ).toUInt();

        
// now set up a new data link use given ip and port.
        emit commandStarted(get_id);
        sendLine( QObject::tr(
"RETR "+ file );

        m_pDataSocket 
= new QTcpSocket();
        m_pDataSocket
->connectToHost( temp_ip, temp_port );

        response 
= getLine();
        
if (! response.startsWith("150 ") )
        
{
            emit commandFinished( put_id, 
true );
            
return put_id;
        }


        
char buffer[4096];
        
int byteRead = 0;
        memset( buffer, 
04096 );
        
while ( m_pDataSocket->waitForReadyRead() ) {
            
if ( ( byteRead = m_pDataSocket->read( buffer, 4096 ) ) == -1 )
            
{
                
break;
            }

            dev
->write( buffer, byteRead );
        }


        m_pDataSocket
->flush();
        m_pDataSocket
->close();
        dev
->close();
        delete m_pDataSocket;
        m_pDataSocket 
= NULL;

        response 
= getLine();
        
if ( ! response.startsWith("226 ") )
        
{
            emit commandFinished( get_id, 
true );
        }

        
else
        
{
            emit commandFinished( get_id, 
false );
        }


        
return get_id;
    }


    
int HLftp::syst()
    
{
        emit commandStarted(syst_id);
        
// sending command.
        sendLine( QObject::tr("SYST") );

        QString response 
= getLine();
        
if ( ! response.startsWith("200") )
        
{
            emit commandFinished( syst_id, 
true );
            
return syst_id;
        }

        
else
        
{
            emit commandFinished( syst_id, 
false );
            
return syst_id;
        }

    }


    
int HLftp::abort()
    
{
        
if ( NULL != m_pDataSocket )
            m_pDataSocket
->abort();

        
return abort_id;
    }


    QString HLftp::pwd()
    

        emit commandStarted(pwd_id);
        
// send command.
        sendLine( QObject::tr("PWD") );

        QString response 
= getLine();
        
if ( response.startsWith("257") )
        
{
            
int opening = response.indexOf('/');
            
int closing = response.indexOf('\"', opening + 1);

            emit commandFinished( pwd_id, 
false );
            QString temp 
= response.mid( opening, closing-opening );
            
return temp;
        }

        
else
        
{
            emit commandFinished( pwd_id, 
true );
            
return QString();
        }

    }


    QString HLftp::getLine()
    
{
        
if ( ! m_pControlSocket->isOpen() )
        
{
            qDebug() 
<< "error : HLftp::getLine() :  socket is not open\n" << endl;
            
return QString();
        }


        
char buffer[500];
        m_pControlSocket
->waitForReadyRead();
        m_pControlSocket
->readLine(  buffer, 500 );
        m_pControlSocket
->read(m_pControlSocket->bytesAvailable());

        qDebug() 
<< "> get: " << buffer << endl;

        
return QString(buffer);
    }


    
void HLftp::sendLine( QString cmd )
    
{
        
if ( ! m_pControlSocket->isOpen() )
        
{
            qDebug() 
<< "error : HLftp::sendLine( QString cmd ) :  socket is not open\n" << endl;
        }


        m_pControlSocket
->waitForBytesWritten();
        cmd.append(
"\r\n");
        m_pControlSocket
->write( cmd.toStdString().c_str(), cmd.size() );
        qDebug() 
<< "> send: " << cmd.toStdString().c_str() << endl;

    }


}


下面是一个辅助类,主要是提供一些算法
/*
* HLutil.h
* \date 2008-04-22
* \author HanYin
*/


#ifndef HL_UTIL_H
#define  HL_UTIL_H

class QString;
class QUrlInfo;
class QStringList;

namespace hy
{
    typedef unsigned 
int UINT;

    
// Temporarily put here, to be upgraded.
    enum ServerSystemType {
        SYS_WINDOWS,
        SYS_UNIX,
        SYS_OTHERS
    }
;

    
/**
    * Provide enough.
    
*/

    
class HLutil
    
{
    
public :
        
static HLutil * getUtil();
        
~HLutil();

        
static QUrlInfo parseFileList( const QString & str, ServerSystemType systemType  );

        
static QStringList seperateString( char sep, const QString & str );

    
private :
        
// To make it could not be create by instantiated.
        HLutil()  {}
        HLutil( 
const HLutil & ) {}
        HLutil 
& operator =const HLutil & util ) {}

        
// a static member to control single.
        static UINT m_uNum;
        
static HLutil * m_pInstance;
    }
;
}


#endif

/*
* HLutil.cpp
* \date 2008-04-22
* \author HanYin
*/


#include 
"HLutil.h"

#include 
<QUrlInfo>
#include 
<QString>
#include 
<QChar>
#include 
<QStringList>
#include 
<QDebug>

namespace hy
{
    UINT HLutil::m_uNum 
= 0;
    HLutil 
* HLutil::m_pInstance =     NULL;

    HLutil::
~HLutil()
    
{
        
if ( NULL != m_pInstance )
        
{
            delete m_pInstance;
            m_pInstance 
= NULL;
            m_uNum 
= 0;
        }

    }


    HLutil 
* HLutil::getUtil()
    
{
        
if ( m_uNum == 0 )
        
{
            m_uNum 
= 1;
            m_pInstance 
= new HLutil();
        }


        
return m_pInstance;
    }


    QUrlInfo HLutil::parseFileList( 
const QString & str, ServerSystemType systemType )
    
{
        QStringList strList 
= seperateString( ' ', str );
        QUrlInfo url;

        
uint num = strList.size();
        
if ( num == 4 )
        
{
            
if ( strList.at(2).toUpper()  == QString("<DIR>").toUpper() )
            
{
                url.setDir(
true);
            }

            
else
            
{
                url.setFile(
true);
                url.setSize( strList.at(
2).toUInt() );
            }

            url.setName( strList.at(
3) );
        }

        
else if ( num == 9 || num == 12 )
        
{
            QString temp 
= strList.at(0);
            
if ( temp[0]=='d' || temp[0]=='D' )
            
{
                url.setDir(
true);
                url.setName( strList.at(
8) );
            }

            
else if ( temp[0]=='-' )
            
{
                url.setFile(
true);
                url.setSize( strList.at(
4).toUInt() );
                url.setName( strList.at(
8) );
            }

            
else if ( temp[0]=='l' )
            
{
                url.setSymLink(
true);
                url.setDir(
true);
                url.setName( strList.at(
11) );
            }

            
else
            
{
                qDebug() 
<< "HLutil::parseFileList( const QString & str, ServerSystemType systemType ) :\n"
                    
"there is unrecognized URL - DIR" << endl;
                
return QUrlInfo();
            }

        }

        
else
        
{
            qDebug() 
<< "HLutil::parseFileList( const QString & str, ServerSystemType systemType ) :\n"
                
"there is unrecognized SYS_TYPE" << endl;
            
return QUrlInfo();
        }


        
return url;

    }


    QStringList HLutil::seperateString( 
char sep, const QString & str )
    
{
        QStringList strList;
        strList.clear();

        
uint i=0, num = str.size();
        QChar ch 
= str[0];
        QString temp_string;
        temp_string.clear();
        
while (true)
        
{
            
if ( ch == ' ' || ch == '\t' )
            
{
                
if ( ++== num )
                    
break;
                ch 
= str[i];
                
continue;
            }


            temp_string.append(ch);
            
if ( ++== num )
                
break;
            ch 
= str[i];
            
if ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' )
            
{
                strList.append(temp_string);
            }

        }


        
return strList;
    }


}


Feedback

# re: 使用QTcpSocket实现FTP客户端的一个beta1.0版本(Qt4.3)  回复  更多评论   

2008-04-26 23:08 by http://www.zhaomysf.com.cn
http://www.zhaomysf.com.cn

# re: 使用QTcpSocket实现FTP客户端的一个beta1.0版本(Qt4.3)  回复  更多评论   

2008-04-26 23:08 by 魔域私服
dddd

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