zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    Vasya and Multisets
    tp5.1 输出json格式字符串被转义
    异步委托(实现多线程的方式)
    模糊查询(like)
    webService
    EL表达式
    远程登陆服务器(window系统)
    output引用类型
    存储过程的定义、修改和删除
    leetcode刷题笔记一百六十二题 寻求峰值
  • 原文地址:https://www.cnblogs.com/kanite/p/5261887.html
Copyright © 2011-2022 走看看