QT实现FTP服务器(三)

  QFtpClient类的实现:

#include "QFtpClient.h"
#include <QDebug>
#include <QThread>
#include <QDebug>
#include <QHostAddress>
#include <QFileInfo>
#include <QDir>
#include <QFileInfoList>
#include <QStringList>
#include <QDateTime>
#include <QElapsedTimer>
#include <QCoreApplication>
#include "QClientThread.h"
#include <QHostInfo>

#define PACKET_SIZE 4096
const char *Month[12] =     //文件日期填充需要用到的月份表示
{
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
};

/***********************************************************************************************
*函数名 : QFtpClient
*函数功能描述 : FTP客户端构造函数
*函数参数 : socketDescriptor 控制socket的描述符 parent 富对象
*函数返回值 :
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
QFtpClient::QFtpClient(qintptr socketDescriptor,QObject *parent) : QObject(parent)
{
    m_socketDescriptor = socketDescriptor;

    m_Server = NULL;
    m_pDataSocket = NULL;
    m_strRemoteHost = "";
    m_strRemotePort = -1;
    m_dwRestartOffset = 0;
    m_bPassiveMode = false;
    m_bdataSocketConnected = false;

    m_nStatus = STATUS_IDLE;
    m_strLocalIPv4 = "";
}

/***********************************************************************************************
*函数名 : InitilizeAfterConstructed
*函数功能描述 : FTP客户端构造以后的初始化工作
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::InitilizeAfterConstructed()
{
    m_pCtrlSocket = new QTcpSocket;
    m_pCtrlSocket->setSocketDescriptor(m_socketDescriptor);
    m_pCtrlSocket->moveToThread(m_pThreadIn);
    connect(m_pCtrlSocket,SIGNAL(readyRead()),this,SLOT(slotReadCtrlSocket()));
    connect(m_pCtrlSocket,SIGNAL(disconnected()),this,SLOT(slotCtrlSocketDisConnected()));
    connect(m_pThreadIn,SIGNAL(finished()),this,SLOT(slotThreadFinished()));

    SendResponse("220 Welcome Using Nelson FTPS");
    m_strCtrlSocketBuff.clear();
}

/***********************************************************************************************
*函数名 : slotThreadFinished
*函数功能描述 :线程结束后发出finished信号的槽函数
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::slotThreadFinished()
{
    if(m_pCtrlSocket)
    {
        delete m_pCtrlSocket;
        m_pCtrlSocket = NULL;
    }
}

/***********************************************************************************************
*函数名 : slotCtrlSocketDisConnected
*函数功能描述 : 控制socket断开连接
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/4
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::slotCtrlSocketDisConnected()
{
    if(m_upLoadFile.isOpen())
        m_upLoadFile.close();        //关闭文件

    m_pCtrlSocket->close();

    DestroyDataConnection();

    if(m_pThreadIn->isRunning())
    {
        QClientThread* p = (QClientThread* )m_pThreadIn;

        p->ClientExit();
        m_pThreadIn->quit();
    }
}

/***********************************************************************************************
*函数名 : slotReadCtrlSocket
*函数功能描述 : 读控制socket的槽函数
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::slotReadCtrlSocket()
{
    while(m_pCtrlSocket->bytesAvailable())
    {
        int length = m_pCtrlSocket->bytesAvailable();
        QByteArray readBufArr = m_pCtrlSocket->read(length);

        QString msg = readBufArr;

        int nIndex = msg.indexOf("
");
        if(nIndex == -1)
        {
            return ;
        }
        msg = msg.left(nIndex);

        m_CtrlSocketCmdsList.append(msg);
        while(m_CtrlSocketCmdsList.size() > 0)
        {
            ProcessCommand();
        }
    }
}

/***********************************************************************************************
*函数名 : GetCommandLine
*函数功能描述 : 从控制socket读缓存中获取命令参数
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::GetCommandLine()
{
    QString strtmp;
    int nIndex;

    while(!m_strCtrlSocketBuff.isEmpty())
    {
        nIndex = m_strCtrlSocketBuff.indexOf("
");
        if(nIndex != -1)
        {
            strtmp = m_strCtrlSocketBuff.left(nIndex);
            m_strCtrlSocketBuff = m_strCtrlSocketBuff.mid(nIndex+2);
            if(!strtmp.isEmpty())
            {
                m_CtrlSocketCmdsList.append(strtmp);
                ProcessCommand();
            }
        }
        else
            break;
    }
}

/***********************************************************************************************
*函数名 : ProcessCommand
*函数功能描述 : 处理FTP客户端发来的命令
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::ProcessCommand(void)
{
    QString strCommand,strArgs;

    QString strBuf = m_CtrlSocketCmdsList.takeFirst();
    int nIndex = strBuf.indexOf(" ");                           //找空格
    if(nIndex == -1)
    {
        strCommand = strBuf;
    }
    else
    {
        strCommand = strBuf.left(nIndex);
        strArgs = strBuf.mid(nIndex+1);
    }
    strCommand.toUpper();

    //qDebug()<<strCommand<<" "<<strArgs;  //<<"        ThreadID:"<<QThread::currentThreadId();

    if(strCommand == "USER")
    {
        if(strArgs == "mm")
        {
            SendResponse("331 User name ok,need password.");
            m_UserName = strArgs;
        }
        else
        {
            SendResponse("530 Not logged in. No such accout.");
        }
    }
    else if(strCommand == "PASS")
    {
        if(m_UserName.isEmpty())
        {
            SendResponse("503 Login with USER first.");
            return ;
        }

        if(strArgs == "mm")
        {
            m_UserPassword = strArgs;
            m_nUserStatus = STATUS_IDLE;

            SendResponse("230 User Login Success.");

            m_strHomeDir = "E:/FTPDIR";                         //设置主目录
            m_strCurrentDir = m_strHomeDir;                     //设置当前目录
        }
    }
    else if(strCommand == "TYPE")
    {
        SendResponse("200 Type set to %s",strArgs.toLatin1().data());
    }
    else if(strCommand == "QUIT")
    {
        SendResponse("220 GoodBye.");

        if(m_upLoadFile.isOpen())
            m_upLoadFile.close();        //关闭文件

        m_pCtrlSocket->close();

        DestroyDataConnection();

        if(m_pThreadIn->isRunning())
        {
            QClientThread* p = (QClientThread* )m_pThreadIn;
            p->ClientExit();
            m_pThreadIn->quit();
        }
    }
    else if(strCommand == "PWD" || strCommand == "XPWD")
    {
        QString strRelativePath;
        GetRelativePath(m_strCurrentDir, strRelativePath);

        SendResponse("257 "%s" is current directory.",strRelativePath.toLatin1().data());
    }
    else if(strCommand == "CWD")
    {
        DoChangeDirectory(strArgs);
    }
    else if(strCommand == "PORT")
    {
        if(!ExtractRemoteHost(strArgs))
        {
            return ;
        }

        SendResponse("200 Port command successful.");
    }
    else if(strCommand == "PASV")
    {
        DestroyDataConnection();
        newDataServer();
    }
    else if(strCommand == "SIZE")
    {
        QString strLocalPath;
        GetLocalPath(strArgs,strLocalPath);

        QFileInfo fi(strLocalPath);
        if(fi.isFile() && fi.exists())
        {
            SendResponse("213 %d",fi.size());
        }
        else
        {
            SendResponse("550 File not found.");
        }
    }
    else if(strCommand == "RETR")              //download
    {
        DoRetrieveFile(strArgs);
    }
    else if((strCommand == "LIST")||(strCommand == "NLIST"))   //list files
    {
        QString strResult;

        if(!GetDirectoryList(strArgs,strResult))
        {
            return ;
        }

        SendResponse("150 Opening ASCII mode data connection for directory list.");

        QByteArray cstr = strResult.toUtf8();
        qint64  actWritten;

        actWritten = m_pDataSocket->write(cstr);
        if(actWritten != cstr.size())
        {
             SendResponse("426 connection closed:transfer aborted.");
        }
        m_pDataSocket->waitForBytesWritten();
        m_pDataSocket->close();
        DestroyDataConnection();
        SendResponse("226 Transfer complete");
    }
    else if(strCommand == "STOR")                            //upload
    {
        SendResponse("150 Opening BINARY mode data connection for file transfer.");
        m_nStatus = STATUS_UPLOAD;
        m_strUpLoading = m_strCurrentDir +"/" + strArgs;
        m_upLoadFile.setFileName(m_strUpLoading);
        m_upLoadFile.open(QIODevice::WriteOnly);

        if(m_pDataSocket == NULL)
            m_pDataSocket = new QTcpSocket;

        connect(m_pDataSocket,SIGNAL(readyRead()),this,SLOT(slotReadDataSocket()));
        connect(m_pDataSocket,SIGNAL(disconnected()),this,SLOT(dataSocketDisconnected()));
        m_pDataSocket->connectToHost(m_strRemoteHost,m_strRemotePort);
    }
    else if(strCommand == "DELE")                            //delete files
    {
        DoDeleteFile(strArgs);
    }
    else if((strCommand == "RMD")||(strCommand == "XRMD"))   //remove directory
    {
        DoDeleteDirectory(strArgs);
    }
    else if(strCommand == "RNFR")
    {
        DoRenameFrom(strArgs);
    }
    else if(strCommand == "RNTO")
    {
        DoRenameTo(strArgs);
    }
}

/***********************************************************************************************
*函数名 : newDataServer
*函数功能描述 : 新建数据socket服务器,然后等待客户端连接上来
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/7
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::newDataServer()
{
    QString localHostName = QHostInfo::localHostName();

    QHostInfo hostInfo = QHostInfo::fromName(localHostName);  //获取主机信息
    for (int i = 0;i < hostInfo.addresses().size();i++)
    {
        if(hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol)
        {
            if(hostInfo.addresses()[i].toString().startsWith("192.168.1."))
            {
                m_strLocalIPv4 = hostInfo.addresses()[i].toString();
            }
        }
    }

    QHostAddress m_HostInfo;
    m_HostInfo.setAddress(m_strLocalIPv4);
    m_Server=new QTcpServer(this);
    m_Server->listen(m_HostInfo,0);
    connect(m_Server,SIGNAL(newConnection()),this,SLOT(slotNewDataSocketConnected()));

    quint16 uPort = m_Server->serverPort();             //套接字端口号
    QString strIP = m_HostInfo.toString();              //套接字IP
    strIP.replace(QString("."),QString(","));
    SendResponse("227 Entering Passive Mode (%s,%d,%d).",strIP.toLatin1().data(),uPort/256,uPort%256);
    m_bPassiveMode = true;
    QElapsedTimer t;
    t.start();
    while(t.elapsed()<5000)                             //在此等待5S 等客户连接上来
    {
        if(m_bdataSocketConnected == true)
        {
            m_bdataSocketConnected = false;
            break;
        }
        QCoreApplication::processEvents();
    }
}

/***********************************************************************************************
*函数名 : DoRenameTo
*函数功能描述 : 处理重命名的RETO命令
*函数参数 : strname  要重命名的文件名或者文件夹名
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/7
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DoRenameTo(QString strname)
{
    QString fileName;
    fileName = m_strCurrentDir + "/" + strname;
    fileName.replace(QString("//"),QString("/"));

    if(m_strRenameFile.isEmpty())
    {
        SendResponse("503 Bad sequence of commands.");
        return ;
    }

    QString strRelativePath;                 //要求回复用相对路径
    GetRelativePath(m_strRenameFile,strRelativePath);

    if(m_bRenameFile)
    {
        QFile file(fileName);                //要被重命名的名字
        if(file.exists())
        {
            SendResponse("550 File already exists.");
            return ;
        }

        file.setFileName(m_strRenameFile);   //原文件
        if(file.rename(fileName))
        {
            SendResponse("250 File "%s" renamed successfully.",strRelativePath.toLatin1().data());
        }
        else
        {
            SendResponse("450 Internal error renaming the file: "%s".",strRelativePath.toLatin1().data());
        }
    }
    else
    {
        QDir dir(fileName);
        if(dir.exists())                    //文件夹存在
        {
            SendResponse("550 Directory already exists.");
            return ;
        }

        dir.setCurrent(m_strRenameFile);    //当前目录
        if(dir.rename(m_strRenameFile,fileName))
        {
            SendResponse("250 File "%s" renamed successfully.",strRelativePath.toLatin1().data());
        }
        else
        {
            SendResponse("450 Internal error renaming the file: "%s".",strRelativePath.toLatin1().data());
        }
    }

    m_strRenameFile.clear();
}

/***********************************************************************************************
*函数名 : DoRenameFrom
*函数功能描述 :处理重命名的REFR命令
*函数参数 : 被重命名的文件名或者文件夹名
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/7
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DoRenameFrom(QString strname)
{
    QString fileName;
    fileName = m_strCurrentDir + "/" + strname;
    fileName.replace(QString("//"),QString("/"));

    m_bRenameFile = false;

    QFile file(fileName);
    if(file.exists())
    {
        m_bRenameFile = true;
        m_strRenameFile = fileName;
        SendResponse("350 File exists,ready for destination name.");
        return ;
    }
    else
    {
        QDir dir(fileName);
        if(dir.exists())
        {
            m_bRenameFile = false;
            m_strRenameFile = fileName;
            SendResponse("350 File exists,ready for destination name.");
            return ;
        }
        else
        {
            SendResponse("550 File/Directory not found.");
            m_bRenameFile = false;
            m_strRenameFile.clear();
            return ;
        }
    }
}

/***********************************************************************************************
*函数名 : DoDeleteDirectory
*函数功能描述 : 删除服务器上的文件夹
*函数参数 : strdir 被删除的文件夹名
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/7
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DoDeleteDirectory(QString strdir)
{
    QString strLocalPath;
    GetLocalPath(strdir,strLocalPath);

    QDir dir(strLocalPath);
    if(!dir.exists())
    {
        SendResponse("550 Directory not found.");
        return ;
    }

    if(!dir.removeRecursively())
    {
        SendResponse("450 Internal error deleting the directory: "%s".",strdir.toLatin1().data());
        return;
    }
    else
    {
        SendResponse("250 Directory "%s" was deleted successfully.",strdir.toLatin1().data());
    }
}

/***********************************************************************************************
*函数名 : DoDeleteFile
*函数功能描述 : 删除服务器上的文件
*函数参数 : strfile 被删除的文件名
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/7
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DoDeleteFile(QString strfile)
{
    QString filePath;

    filePath = m_strCurrentDir + "/" + strfile;
    filePath.replace(QString("//"),QString("/"));

    QFile file(filePath);
    if(!file.exists())
    {
        SendResponse("550 File not found.");
        return ;
    }

    if(file.remove())
    {
        SendResponse("250 File "%s" was deleted successful.",strfile.toLatin1().data());
    }
    else
    {
        SendResponse("450 Internal error deleting the file: "%s".",strfile.toLatin1().data());
    }
}

/***********************************************************************************************
*函数名 : ExtractRemoteHost
*函数功能描述 : 提取客户端发来的PORT命令携带的对方IP和端口信息
*函数参数 : strArgs PORT命令附带的参数
*函数返回值 : 成功返回true,失败返回false
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
bool QFtpClient::ExtractRemoteHost(QString strArgs)
{
    int port=0;
    QString strIP="";
    int nCount = strArgs.count(QChar(','), Qt::CaseSensitive);
    if(nCount != 5)
    {
        return false;
    }

    strArgs += ",";                          //为了方便提取参数,增加一个','
    nCount = 0;
    while(1)
    {
        int index = strArgs.indexOf(",");    //例:"192,168,1,9,84,65"
        if(index == -1)
        {
            break;
        }
        nCount++;
        switch(nCount)
        {
            case 1:  //"192"
            strIP += strArgs.left(index)+".";
            strArgs = strArgs.right(strArgs.length()-index-1);
            break;
            case 2:  //"168"
            strIP += strArgs.left(index)+".";
            strArgs = strArgs.right(strArgs.length()-index-1);
            break;
            case 3:  //"1"
            strIP += strArgs.left(index)+".";
            strArgs = strArgs.right(strArgs.length()-index-1);
            break;
            case 4:
            strIP += strArgs.left(index);
            strArgs = strArgs.right(strArgs.length()-index-1);
            break;
            case 5:
            port = strArgs.left(index).toInt()*256;
            strArgs = strArgs.right(strArgs.length()-index-1);
            break;
            case 6:
            port += strArgs.left(index).toInt();
            strArgs.clear();
            break;
        }
    }

    m_strRemotePort = port;
    m_strRemoteHost = strIP;

    return true;
}

/***********************************************************************************************
*函数名 :  GetDirectoryList
*函数功能描述 : 获取当前文件夹下的文件信息
*函数参数 : strArgs 当前文件夹目录 strResult 文件信息结果存储
*函数返回值 : 成功返回true 失败返回false
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
bool QFtpClient::GetDirectoryList(QString strArgs,QString &strResult)
{
    QString strDirectory = strArgs;

    strDirectory = "";

    QDir dir(m_strCurrentDir);
    if(!dir.exists())
        return false;

    QStringList strFileList;
    QFileInfoList list=dir.entryInfoList(strFileList,QDir::AllEntries,QDir::DirsFirst);

    for(int i=0;i<list.count();i++)
    {
        QFileInfo tempFileInfo = list.at(i);
        if(tempFileInfo.isDir())
        {
            strResult += "drwx------";
        }
        else
        {
            strResult += "-rwx------";
        }

        strResult += " 1 user group ";                              // groups
        QString strLength = QString("%1").arg(tempFileInfo.size()); //file size
        QString strFiller = "              ";
        strResult += strFiller.left(strFiller.size()-strLength.size());
        strResult += strLength;

        QString fileTime;
        QDateTime fileCreateDat =  tempFileInfo.created();          //文件创建日期
        QDateTime nowTime =QDateTime::currentDateTime();            //当前日期
        qint64 dis = nowTime.daysTo(fileCreateDat);
        if((dis > 350)||(tempFileInfo.lastModified()>nowTime))
        {
            QString strMonth = QString(" %1").arg( Month[fileCreateDat.date().month() - 1]);
            QString strDay =  QString(" %1").arg(fileCreateDat.date().day());
            QString strYear = QString(" %1  ").arg(fileCreateDat.date().year());
            fileTime += strMonth + strDay + strYear;               //月日年格式
        }
        else
        {
            QString strMonth = QString(" %1").arg(Month[fileCreateDat.date().month() - 1]);
            QString strDay =  QString(" %1").arg(fileCreateDat.date().day());
            QString time = QString(" %1:%2 ").arg(fileCreateDat.time().hour()).arg(fileCreateDat.time().second());
            fileTime += strMonth + strDay + time;
        }

        strResult += fileTime;                                      //文件时间
        strResult+=tempFileInfo.fileName();                         //加上文件名
        strResult+="
";
    }

    return true;
}

/***********************************************************************************************
*函数名 :  StripParamters
*函数功能描述 :
*函数参数 :
*函数返回值 :
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::StripParamters(QString args)
{
    QString strBuff = "";
    bool bIsParam = false;

    for(int i=0;i<args.length();i++)
    {
        if(args.at(i) == QChar('-'))
        {
            bIsParam = true;
        }

        if(args.at(i) == QChar(' ') && bIsParam)
        {
            bIsParam = false;
            continue ;
        }

        if(!bIsParam)
        {
            strBuff += args.at(i);
        }
    }
    args = strBuff;
}

/***********************************************************************************************
*函数名 : DoRetrieveFile
*函数功能描述 : 下载文件
*函数参数 : filename 下载文件的文件名
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DoRetrieveFile(QString filename)
{
    QString strLocalPath;
    GetLocalPath(filename,strLocalPath);

    QFileInfo fi(strLocalPath);
    if(fi.isFile() && fi.exists())
    {
        SendResponse("150 Opening BINARY mode data connection for file transfer");
    }
    else
    {
        SendResponse("550 File not found.");
        return ;
    }

    SendFile(strLocalPath);   //本地地址名
}

/***********************************************************************************************
*函数名 :  SendFile
*函数功能描述 : 发送文件
*函数参数 :
*函数返回值 :
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::SendFile(QString lpszFileName)
{
    if(!PreSendFile(lpszFileName))
    {
        SendResponse("426 Connection closed: transfer aborted.");
        return ;
    }

    QFile file(lpszFileName);
    if(!file.open(QIODevice::ReadOnly))
    {
        return ;
    }

    m_nTotalBytesTransfered = 0;           //先设定死

    while(m_nTotalBytesTransfered < m_nTotalBytesSend)
    {
        qint64  actWritten;
        QByteArray readByte = file.read(PACKET_SIZE);
        m_nTotalBytesTransfered += readByte.size();

        actWritten = m_pDataSocket->write(readByte);
        if(actWritten != readByte.size())
        {
            SendResponse("426 connection closed:transfer aborted.");
        }
    }

    m_pDataSocket->close();                //关闭数据连接端口

    SendResponse("226 Transfer complete");
}

/***********************************************************************************************
*函数名 : PreSendFile
*函数功能描述 : 发送文件前检查文件是否存在
*函数参数 : lpszFileName 要发送的文件名
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
bool QFtpClient::PreSendFile(QString lpszFileName)
{
    QFile file(lpszFileName);

    if(file.open(QIODevice::ReadOnly))
    {
        m_nTotalBytesSend = file.size();

        if(m_dwRestartOffset < m_nTotalBytesSend)
        {
            m_nTotalBytesTransfered = m_dwRestartOffset;
        }
        else
        {
            m_nTotalBytesTransfered = 0;
        }

        file.close();
        return true;
    }
    else
    {
        return false;
    }
}

/***********************************************************************************************
*函数名 : slotNewDataSocketConnected
*函数功能描述 : tcp数据服务监听端口
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::slotNewDataSocketConnected()
{
    m_pDataSocket = m_Server->nextPendingConnection();

    m_Server->close();

    connect(m_pDataSocket,SIGNAL(readyRead()),this,SLOT(slotReadDataSocket()));

    m_bdataSocketConnected = true;
}

/***********************************************************************************************
*函数名 :  slotReadDataSocket
*函数功能描述 : 读数据套接字
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::slotReadDataSocket()
{
    while(m_pDataSocket->bytesAvailable())
    {
        QByteArray dataArray;

        int length = m_pDataSocket->bytesAvailable();
        dataArray =  m_pDataSocket->read(length);

        if(m_nStatus == STATUS_UPLOAD)
        {
            qint64 n = m_upLoadFile.write(dataArray);
            if(n != dataArray.size())
            {
                qDebug()<<"write file data error!";
            }
        }
    }
}

/***********************************************************************************************
*函数名 : dataSocketDisconnected
*函数功能描述 : 数据套接字被断开的槽函数
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::dataSocketDisconnected()
{
    m_pDataSocket->close();          //数据套接字关闭
    SendResponse("226 Transfer complete");
}

/***********************************************************************************************
*函数名 : DestroyDataConnection
*函数功能描述 : 销毁数据连接
*函数参数 : 无
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DestroyDataConnection()
{
    if(m_pDataSocket != NULL)
    {
        delete m_pDataSocket;
        m_pDataSocket = NULL;
    }

    if(m_Server != NULL)
    {
        if(m_Server->isListening())
            m_Server->close();

        delete m_Server;
        m_Server = NULL;
    }

    m_strRemoteHost = "";
    m_strRemotePort = -1;
    m_dwRestartOffset = 0;
    m_bPassiveMode = false;
    m_pDataSocket = NULL;
}

/***********************************************************************************************
*函数名 :  DoChangeDirectory
*函数功能描述 : 更改当前的工作目录
*函数参数 : strPath
*函数返回值 :
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::DoChangeDirectory(QString strPath)
{
    if(strPath.endsWith(".."))
    {
        strPath = strPath.left(strPath.length() - 2);   //remove ..
        m_strCurrentDir = m_strCurrentDir + strPath;
        m_strCurrentDir = m_strCurrentDir.replace("//","/");

        QDir dir(m_strCurrentDir);
        if(!dir.cdUp())
        {
            SendResponse("550 Directory not found.");
            return ;
        }
        m_strCurrentDir = dir.path();
    }
    else
    {
        m_strCurrentDir = m_strCurrentDir + strPath;
        m_strCurrentDir = m_strCurrentDir.replace("//","/");
        QDir dir(m_strCurrentDir);
        if(!dir.exists())
        {
            SendResponse("550 Directory not found.");
            return ;
        }
        m_strCurrentDir = dir.path();
    }

    QString strRelativePath;
    GetRelativePath(m_strCurrentDir,strRelativePath);
    SendResponse("250 Directory changed to "%s".",strRelativePath.toLatin1().data());
}

/***********************************************************************************************
*函数名 : GetRelativePath
*函数功能描述 : 从绝对路径转相对路径,从当前路径得到相对于主目录的路径
*函数参数 : lpszLocalPath 绝对路径 strRelativePath 相对路径
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::GetRelativePath(QString lpszLocalPath, QString &strRelativePath)
{
    QString strOffset = m_strHomeDir;                //这是主目录 "E:/FTPDIR"
    QString strLocalPath = lpszLocalPath;            //传进来的参数是当前

    if (strOffset.right(1) != QString("/"))          //最后有没有反斜杠
       strOffset += "/";                             //没有就加上

    if (strLocalPath.right(1) != QString("/"))       //当前路径
       strLocalPath += "/";

    if (strOffset == strLocalPath)          //如果当前路径就是主目录
    {
       strRelativePath = "/";               //那么相对路径就设置为"/"
    }
    else
    {                                       //例如当前路径是"E:tempADir",而主目录是“E:temp”,那么相对路径就是ADir
       strRelativePath = strLocalPath;      //相对路径就是当前路径,然后将主目录的部分用“/”代替
       strRelativePath.replace(strOffset, "/");
    }
}

/***********************************************************************************************
*函数名 : GetLocalPath
*函数功能描述 : 从相对路径获得绝对路径
*函数参数 : lpszRelativePath 相对路径 strLocalPath 绝对路径
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
void QFtpClient::GetLocalPath(QString lpszRelativePath, QString &strLocalPath)
{
    QString strRelativePath = lpszRelativePath;      //相对路径

    if(strRelativePath.left(1) == "/")
    {
        strLocalPath = m_strHomeDir+strRelativePath;
    }
    else
    {
        strLocalPath = m_strCurrentDir+"/"+strRelativePath;
    }

    strLocalPath.replace(QString("//"), QString("/"));
}

/***********************************************************************************************
*函数名 : SendResponse
*函数功能描述 : 给客户段的回复
*函数参数 : pcFormat 回复字符串
*函数返回值 : 无
*作者 : nelson
*函数创作日期 : 2016/3/3
*函数修改日期 :
*修改人 :
*修改原因 :
*版本 : 1.0
*历史版本 : 无
***********************************************************************************************/
bool QFtpClient::SendResponse(const char *pcFormat, ...)
{
    QString input;
    char buf[100]={0};
    va_list  pArg;
    va_start(pArg,pcFormat);
    vsprintf(buf,pcFormat,pArg);

    input = buf;

    input += "
";

    if(m_pCtrlSocket->state() == QAbstractSocket::ConnectedState)
    {
        m_pCtrlSocket->write(input.toStdString().c_str(), strlen(input.toStdString().c_str()));
        m_pCtrlSocket->waitForBytesWritten();
    }

    return 0;
}
原文地址:https://www.cnblogs.com/kanite/p/5261887.html