|
Posted on 2008-04-26 09:24 HYin 阅读(6006) 评论(2) 编辑 收藏 引用
我们网络实验使用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 get( const 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, 0, 4096 ); 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::get( const 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, 0, 4096 ); 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 ( ++i == num ) break; ch = str[i]; continue; }
temp_string.append(ch); if ( ++i == 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
# re: 使用QTcpSocket实现FTP客户端的一个beta1.0版本(Qt4.3) 回复 更多评论
2008-04-26 23:08 by
dddd
|