日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Qt网络编程例子

發布時間:2023/12/9 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt网络编程例子 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

Qt網絡編程

1.TCP編程例子

2.UDP編程例子

3.HTTP例子

4.FTP例子






Qt各個版本的下載地址:http://download.qt.io/archive/qt/

在Windows上與VS配合使用的Qt的編譯器用的是MSVC有個問題:在QtCreator中寫的代碼對字符串老報錯,對tr()里面的字符串也是這樣。

所以如果只是在Windows上純用Qt的話,就考慮用MinGW版本的編譯器。

MinGW:Minimalist GNU for Windows是一套Windows上的GNU工具集,用其開發的Qt程序不需要第三方dll支持的情況下就可以直接在Windows上運行!(參考:http://www.mingw.org/)



要對套接字的底層有更多的了解與編程控制,就要去看伯克利套接字和WinSock。

這兩個套接字編程的層次更接近于操作系統的系統功能調用,I/O模型,線程同步控制等。

?

高層應用:如果只是普通應用程序編程,對于Winsock有MFC的封裝。

也可以用Qt的網絡編程模塊。

?


Network Programming with Qt

Qt中的Qt Network模塊用來編寫基于TCP/IP的網絡應用程序。


  • ?較低層次的類:用來表示低層次的網絡概念QTcpSocket、QTcpServer、QUdpSocket
  • 高層次的類:使用通用協議(http、ftp)來進行網絡操作QNetworkRequest、QNetworkReply、QNetworkAccessManage
  • 負載管理類:QNetworkConfiguration、QNetworkConfigurationManager、QNetworkSession?
  • 網絡代理類:QNetworkProxy
  • 通信安全類QSsl

  • (SSL(Secure Sockets Layer 安全套接層),及其繼任者傳輸層安全(Transport Layer Security,TLS)是為網絡通信提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡連接進行加密。)

    (SSL協議位于TCP/IP協議與各種應用層協議之間,為數據通訊提供安全支持。SSL協議可分為兩層: SSL記錄協議(SSLRecord Protocol):它建立在可靠的傳輸協議(如TCP)之上,為高層協議提供數據封裝、壓縮、加密等基本功能的支持。 SSL握手協議(SSL Handshake Protocol):它建立在SSL記錄協議之上,用于在實際的數據傳輸開始前,通訊雙方進行身份認證、協商加密算法、交換加密密鑰等。)


    1. UDP (QUdpSocket)
    UDP(User Datagram Protocol,用戶數據報協議)輕量級、不可靠、面向數據報、無連接的傳輸層協議。


    下面是一個用QUdpSocket廣播IPv4數據報的例子。
    廣播一般用來實現網絡發現協議,例如,查找網絡上哪臺主機擁有最多的硬盤空間,該主機就向網絡中廣播一個數據報,然后所以收到該數據報的主機,就會把自己的硬盤空間信息以應答包的方式發回給請求端。發送端(請求端)一直等待,直到它接受到所有主機的應答,然后選擇擁有最多空閑磁盤空間的主機服務器來存儲數據。

    要廣播一個數據報,只需要把數據報發送到一個特殊的地址QHostAddress::Broadcast(255.255.255.255),或者是本網絡的廣播地址。

    用QUdpSocket實現發生廣播數據報:可以運行多個接受端,只有IP地址和端口號設置對,就可以將發生者的廣播信息發生出去,接收者接受廣播信息。

    UDP發送和接收都不需要建立連接,只需要綁定好地址與端口后就可以發送和接收數據了。發送端直接發送,接受端只需要循環判斷數據接收完了沒有,沒接收完就循環接收就OK。

    Qt中信號與槽的機制很好的實現了收到數據發送信號觸發處理函數。類似與WinSock里面的異步模型,需要事件對象來對這一流程做同步的情況。面向界面GUI的時候,這一信號與槽機制比MFC里的一大堆線程同步類好用。


    UDP發送端:

    udpsender:

    sender.h:

    #ifndef SENDER_H #define SENDER_H#include <QDialog> class QUdpSocket;namespace Ui { class Sender; }class Sender : public QDialog {Q_OBJECTpublic:explicit Sender(QWidget *parent = 0);~Sender();private slots:void on_pushButton_clicked();private:Ui::Sender *ui;QUdpSocket *sender; };#endif // SENDER_H

    sender.cpp

    #include "sender.h" #include "ui_sender.h" #include <QtNetwork>Sender::Sender(QWidget *parent) :QDialog(parent),ui(new Ui::Sender) {ui->setupUi(this);sender = new QUdpSocket(this); }Sender::~Sender() {delete ui; }void Sender::on_pushButton_clicked() {QByteArray datagram = "hello world!";//調用writeDatagram函數來發送數據sender->writeDatagram(datagram.data(), datagram.size(),QHostAddress::Broadcast, 45454);}

    udpreceiver:

    receiver.h

    #ifndef RECEIVER_H #define RECEIVER_H#include <QDialog> class QUdpSocket; namespace Ui { class Receiver; }class Receiver : public QDialog {Q_OBJECTpublic:explicit Receiver(QWidget *parent = 0);~Receiver();private:Ui::Receiver *ui;QUdpSocket *receiver;private slots:void processPendingDatagram();};#endif // RECEIVER_H

    receiver.cpp

    #include "receiver.h" #include "ui_receiver.h" #include <QtNetwork>Receiver::Receiver(QWidget *parent) :QDialog(parent),ui(new Ui::Receiver) {ui->setupUi(this);receiver = new QUdpSocket(this);receiver->bind(45454, QUdpSocket::ShareAddress);connect(receiver, &QUdpSocket::readyRead, this, &Receiver::processPendingDatagram);}Receiver::~Receiver() {delete ui; }void Receiver::processPendingDatagram() {// 擁有等待的數據報while(receiver->hasPendingDatagrams()){QByteArray datagram;// 讓datagram的大小為等待處理的數據報的大小,這樣才能接收到完整的數據datagram.resize(receiver->pendingDatagramSize());// 接收數據報,將其存放到datagram中receiver->readDatagram(datagram.data(), datagram.size());ui->label->setText(datagram);} }

    2. TCP(QTcpSocket、QTcpServe)
    TCP(Transmission Control Protocol,傳輸控制協議)一個用于數據傳輸的底層網絡協議。(Http、FTP都是基于TCP)
    TCP面向數據流、連接的可靠傳輸層協議。
    可以用QTcpSocket來實現POP3、SMTP、NNTP等標準網絡協議。
    QTcpSocket傳輸的是連續的數據流,適合連續傳輸數據。

    TCP:C/S模型,在數據傳輸之前、必須在C/S直接建立TCP連接。

    下面的例子實現:服務器端一直監聽端口,一旦有客戶端連接請求到達,便建立連接,連接建立好后向客戶端發送一個字符串,客戶端收到該字符串并顯示出來。

    服務器端:

    server.h

    #ifndef SERVER_H #define SERVER_H#include <QDialog> class QTcpServer;namespace Ui { class Server; }class Server : public QDialog {Q_OBJECTpublic:explicit Server(QWidget *parent = 0);~Server();private:Ui::Server *ui;QTcpServer *tcpServer;private slots:void sendMessage();};#endif // SERVER_H

    server.cpp

    #include "server.h" #include "ui_server.h" #include <QtNetwork>Server::Server(QWidget *parent) :QDialog(parent),ui(new Ui::Server) {ui->setupUi(this);tcpServer = new QTcpServer(this);// 使用了IPv4的本地主機地址,等價于QHostAddress("127.0.0.1")if (!tcpServer->listen(QHostAddress::LocalHost, 6666)) {qDebug() << tcpServer->errorString();close();}connect(tcpServer, &QTcpServer::newConnection,this, &Server::sendMessage);}Server::~Server() {delete ui; }void Server::sendMessage() {// 用于暫存要發送的數據QByteArray block;QDataStream out(&block, QIODevice::WriteOnly);// 設置數據流的版本,客戶端和服務器端使用的版本要相同out.setVersion(QDataStream::Qt_5_6);out << (quint16)0;out << tr("hello TCP!!!");out.device()->seek(0);out << (quint16)(block.size() - sizeof(quint16));// 獲取已經建立的連接的套接字QTcpSocket *clientConnection = tcpServer->nextPendingConnection();connect(clientConnection, &QTcpSocket::disconnected,clientConnection, &QTcpSocket::deleteLater);clientConnection->write(block);clientConnection->disconnectFromHost();// 發送數據成功后,顯示提示ui->label->setText(tr("發送數據成功!!!")); }


    客戶端:

    client.h

    #ifndef CLIENT_H #define CLIENT_H#include <QDialog> #include <QAbstractSocket> class QTcpSocket;namespace Ui { class Client; }class Client : public QDialog {Q_OBJECTpublic:explicit Client(QWidget *parent = 0);~Client();private:Ui::Client *ui;QTcpSocket *tcpSocket;QString message;// 用來存放數據的大小信息quint16 blockSize;private slots:void newConnect();void readMessage();void displayError(QAbstractSocket::SocketError);void on_connectButton_clicked(); };#endif // CLIENT_H

    client.cpp

    #include "client.h" #include "ui_client.h" #include <QtNetwork>Client::Client(QWidget *parent) :QDialog(parent),ui(new Ui::Client) {ui->setupUi(this);tcpSocket = new QTcpSocket(this);connect(tcpSocket, &QTcpSocket::readyRead, this, &Client::readMessage);connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(displayError(QAbstractSocket::SocketError)));}Client::~Client() {delete ui; }void Client::newConnect() {// 初始化數據大小信息為0blockSize = 0;// 取消已有的連接tcpSocket->abort();tcpSocket->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt()); }void Client::readMessage() {QDataStream in(tcpSocket);// 設置數據流版本,這里要和服務器端相同in.setVersion(QDataStream::Qt_5_6);// 如果是剛開始接收數據if (blockSize == 0) {//判斷接收的數據是否大于兩字節,也就是文件的大小信息所占的空間//如果是則保存到blockSize變量中,否則直接返回,繼續接收數據if(tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return;in >> blockSize;}// 如果沒有得到全部的數據,則返回,繼續接收數據if(tcpSocket->bytesAvailable() < blockSize) return;// 將接收到的數據存放到變量中in >> message;// 顯示接收到的數據ui->messageLabel->setText(message); }void Client::displayError(QAbstractSocket::SocketError) {qDebug() << tcpSocket->errorString(); }void Client::on_connectButton_clicked() {newConnect(); }


    3.HTTP

    HTTP(HyperText Transfer Protocol,超文本傳輸協議)是一個客戶端和服務器端之間進行請求應答的標準。

    HTTP的C/S交互過程:

    通常由HTTP客戶端發起一個請求,建立一個到服務器指定端口(默認是80端口)的TCP連接;

    HTTP服務器在指定端口(80端口)監聽客戶端發過來的請求,一旦收到請求,服務器端就會向客戶端發回一個應答


    在Qt中,高層協議的應用就是用好這三個類:QNetworkAccessManager、QNetworkRequest、QNetworkReply;

    The QNetworkAccessManager class allows the application to send network requests and receive replies.

    所以網絡請求的發送和網絡應答的接收都是通過QNetworkAccessManager類調用相關的方法來完成了。

    調用get()方法發送一個網絡請求。得到一個網頁page數據。

    QNetworkReply *get(const QNetworkRequest& request)


    QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)

    Posts a request to obtain the contents of the target request and returns a new QNetworkReply object opened for reading which emits the readyRead() signal whenever new data arrives.

    The contents as well as associated headers will be downloaded.

    See also post(), put(), deleteResource(), and sendCustomRequest().


    Once a QNetworkAccessManager object has been created, the application can use it to send requests over the network. A group of standard functions are supplied that take a request and optional data, and each return a QNetworkReply object. The returned object is used to obtain any data returned in response to the corresponding request.


    QNetworkRequest:

    The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.

    QNetworkRequest is part of the Network Access API and is the class holding the information necessary to send a request over the network. It contains a URL and some ancillary information that can be used to modify the request.


    The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager

    The QNetworkReply class contains the data and meta data related to a request posted with QNetworkAccessManager. Like QNetworkRequest, it contains a URL and headers (both in parsed and raw form), some information about the reply's state and the contents of the reply itself.

    QNetworkReply is a sequential-access QIODevice, which means that once data is read from the object, it no longer kept by the device. It is therefore the application's responsibility to keep this data if it needs to. Whenever more data is received from the network and processed, the readyRead() signal is emitted.

    The downloadProgress() signal is also emitted when data is received, but the number of bytes contained in it may not represent the actual bytes received, if any transformation is done to the contents (for example, decompressing and removing the protocol overhead).

    Even though QNetworkReply is a QIODevice connected to the contents of the reply, it also emits the uploadProgress() signal, which indicates the progress of the upload for operations that have such content.

    Note: Do not delete the object in the slot connected to the error() or finished() signal. Use deleteLater().

    因為QNetworkReply繼承自QIODevice類,所以可以像操作一般的I/O設備一樣操作該類。

    下面的例子使用了readAll()函數來讀取所有的應答數據,完成數據的讀取后需要使用deleteLater()來刪除reply對象。上面的Note中已經指出,不能在與QNetworkReply對象的完成信號和錯誤信號關聯的槽函數中delete調reply對象,應該調用deleteLater()滯后刪除該對象。


    下面的代碼通過網絡訪問接口實現HTTP通信的例子:

    網絡請求使用QNetworkRequset類表示,get()函數返回一個QNetworkReply對象

    將返回的網頁以文本的方式顯示:

    mainwindow.h

    #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QNetworkReply; class QNetworkAccessManager;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private:Ui::MainWindow *ui;QNetworkAccessManager *manager;private slots:void replyFinished(QNetworkReply *);};#endif // MAINWINDOW_H

    mainwindow.cpp

    #include "mainwindow.h" #include "ui_mainwindow.h" #include <QtNetwork>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);manager = new QNetworkAccessManager(this);/*這里首先創建了一個QNetworkAccessManager類的實例,用它來發送網絡請求和接受應答*//*將QNetworkAccessManager的finished信號和自定義的槽函數replyFinished相關聯,* 每當網絡請求結束時,都會收到finished信號并觸發執行槽函數replyFinished*/connect(manager, &QNetworkAccessManager::finished,this, &MainWindow::replyFinished);manager->get(QNetworkRequest(QUrl("http://www.yangbo.pro")));/*用get方法發送一個網絡請求,網絡請求使用QNetworkRequest()表示,* get()函數返回一個QNetworkReply對象信號函數的參數和槽函數的參數是對應的!管理器還提供了發送HTTP POST請求的:post()函數HTTP PUT請求的put()函數HTTP DELETE請求的deleteResource()請求 */ }MainWindow::~MainWindow() {delete ui; }void MainWindow::replyFinished(QNetworkReply *reply) {QString all = reply->readAll();ui->textBrowser->setText(all);//僅以文本形式呈現網頁!reply->deleteLater(); }


    使用HTTP協議從Web服務器上下載一個文件:

    下面的例子從Web服務器上下載一個文件,需要輸入正確的URL路徑,

    mainwindow.h

    #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QNetworkReply; class QNetworkAccessManager; #include <QUrl> class QFile;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();void startRequest(QUrl url);private:Ui::MainWindow *ui;QNetworkAccessManager *manager;QNetworkReply *reply;QUrl url;QFile *file;private slots:void httpFinished();void httpReadyRead();void updateDataReadProgress(qint64, qint64);void on_pushButton_clicked(); };#endif // MAINWINDOW_H

    mainwindow.cpp

    #include "mainwindow.h" #include "ui_mainwindow.h" #include <QtNetwork>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);manager = new QNetworkAccessManager(this);ui->progressBar->hide(); }MainWindow::~MainWindow() {delete ui; }/*URL要寫得規范,沒有寫http://會報錯;對于Web服務器,當然要指明協議是http:// 我們平時在瀏覽器上只輸入網站是因為瀏覽器幫我們自動補全了http://如果服務器是ftp服務器,當然要輸入完整的ftp://如果服務器是sftp服務器,輸入sftp://101.132.67.215就可以下載Web服務器上的文件了,就像打開本地文件一樣地發開并保存遠端Web服務器上的文件!http://www.yangbo.pro/yangboresumeE2.pdf http://www.yangbo.pro/piece3.jpghttp://www.yangbo.pro/images/10project2.png\然后到C:\Users\Administrator\Desktop\QtNetwork\18-2\build-myHTTP-Desktop_Qt_5_8_0_MSVC2013_32bit-Debug 編譯文件目錄下就可以看到下載好了文件了! */void MainWindow::startRequest(QUrl url) {/*get()函數返回一個QNetworkReply對象,QNetworkReply對象里就有readyRead、downloadProgress、finished這些信號!*/reply = manager->get(QNetworkRequest(url));connect(reply, &QNetworkReply::readyRead, this, &MainWindow::httpReadyRead);connect(reply, &QNetworkReply::downloadProgress,this, &MainWindow::updateDataReadProgress);connect(reply, &QNetworkReply::finished, this, &MainWindow::httpFinished); }void MainWindow::httpReadyRead() {if (file) file->write(reply->readAll()); }void MainWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes) {ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesRead); }void MainWindow::httpFinished() {ui->progressBar->hide();if(file) {file->close();delete file;file = 0;}reply->deleteLater();reply = 0; }void MainWindow::on_pushButton_clicked() {url = ui->lineEdit->text();QFileInfo info(url.path());QString fileName(info.fileName());if (fileName.isEmpty()) fileName = "index.html";file = new QFile(fileName);if(!file->open(QIODevice::WriteOnly)){delete file;file = 0;return;}startRequest(url);ui->progressBar->setValue(0);ui->progressBar->show(); }/**Windows命令行: C:\Users\Administrator>ftp ftp> open www.yangbo.pro 連接到 www.yangbo.pro。 220---------- Welcome to Pure-FTPd [privsep] ---------- 220-You are user number 1 of 5000 allowed. 220-Local time is now 10:06. Server port: 21. 220-This is a private system - No anonymous login 220-IPv6 connections are also welcome on this server. 220 You will be disconnected after 15 minutes of inactivity. 200 OK, UTF-8 enabled 用戶(www.yangbo.pro:(none)): root 331 User root OK. Password required 密碼: 421 Unable to read the indexed puredb file (or old format detected) - Try pure-pw mkdb 遠程主機關閉連接。 ftp> */

    下面這個小例子用ftp協議從指定的ftp服務器上下載一個文件,服務器端主機名、登陸的用戶名和密碼等都再代碼里指定:

    .h文件

    #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QNetworkReply; class QNetworkAccessManager; #include <QUrl> class QFile;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();void startRequest(QUrl url);private:Ui::MainWindow *ui;QNetworkAccessManager *manager;QNetworkReply *reply;QUrl url;QFile *file;private slots:void httpFinished();void httpReadyRead();void updateDataReadProgress(qint64, qint64);void on_pushButton_clicked(); };#endif // MAINWINDOW_H

    .cpp文件

    #include "mainwindow.h" #include "ui_mainwindow.h" #include <QtNetwork>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);manager = new QNetworkAccessManager(this);ui->progressBar->hide(); }MainWindow::~MainWindow() {delete ui; }void MainWindow::startRequest(QUrl url) {reply = manager->get(QNetworkRequest(url));connect(reply, &QNetworkReply::readyRead, this, &MainWindow::httpReadyRead);connect(reply, &QNetworkReply::downloadProgress,this, &MainWindow::updateDataReadProgress);connect(reply, &QNetworkReply::finished, this, &MainWindow::httpFinished); }void MainWindow::httpReadyRead() {if (file) file->write(reply->readAll()); }void MainWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes) {ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesRead); }void MainWindow::httpFinished() {ui->progressBar->hide();if(file) {file->close();delete file;file = 0;}reply->deleteLater();reply = 0; }void MainWindow::on_pushButton_clicked() {url = ui->lineEdit->text();QUrl url;url.setScheme("ftp");url.setHost("v0.ftp.upyun.com");url.setPath("readme.txt");url.setUserName("qtertest/qtftptest");url.setPassword("pwd123456");/*Qt Creator多行注釋和取消多行注釋快捷鍵:Ctrl+/ */// QUrl url;// url.setScheme("sftp");// url.setHost("101.132.67.215");// url.setPath("./data/wwwroot/default/images/10project2.png");// url.setUserName("root");// url.setPassword("159114");QFileInfo info(url.path());QString fileName(info.fileName());if (fileName.isEmpty()) fileName = "index.html";file = new QFile(fileName);if(!file->open(QIODevice::WriteOnly)){delete file;file = 0;return;}startRequest(url);ui->progressBar->setValue(0);ui->progressBar->show(); }


    4.FTP

    FTP(File Transfer Protocol,文件傳輸協議)是一個用于瀏覽遠程ftp服務器主機目錄和下載文件的協議。

    FTP協議使用兩個TCP網絡連接,一個用來發送控制命令(端口號:21),一個用來傳輸數據(端口號:20);(而STFP的端口號是22)。

    FTP協議有一個狀態,并且需要客戶端在傳輸文件之前發送一些控制命令。

    FTP客戶端建立一個連接,并且在整個會話期間一直保持打開。在每個會話期間,可以發生多個傳輸。


    下面的這個例子實現瀏覽遠程ftp服務器的文件和目錄,并可下載任意文件:


    .pro文件:

    #------------------------------------------------- # # Project created by QtCreator 2016-07-10T00:49:54 # #-------------------------------------------------QT += core gui QT += network greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = myFTP TEMPLATE = appSOURCES += main.cpp\mainwindow.cpp \qftp.cpp \qurlinfo.cppHEADERS += mainwindow.h \qftp.h \qurlinfo.hFORMS += mainwindow.ui

    mainwindow.h:

    #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QFtp; #include <QHash> class QFile; class QUrlInfo; class QTreeWidgetItem;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private:Ui::MainWindow *ui;QFtp *ftp;// 用來存儲一個路徑是否為目錄的信息QHash<QString, bool> isDirectory;// 用來存儲當前路徑QString currentPath;// 用來表示下載的文件QFile *file;private slots:void ftpCommandStarted(int);void ftpCommandFinished(int, bool);// 更新進度條void updateDataTransferProgress(qint64, qint64 );// 將服務器上的文件添加到Tree Widget部件中void addToList(const QUrlInfo &urlInfo);// 雙擊一個目錄時顯示其內容void processItem(QTreeWidgetItem*, int);void on_connectButton_clicked();void on_cdToParentButton_clicked();void on_downloadButton_clicked(); };#endif // MAINWINDOW_H

    qftp.h

    /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/#ifndef QFTP_H #define QFTP_H#include <QtCore/qstring.h> #include <QtCore/qobject.h> #include <qurlinfo.h>QT_BEGIN_NAMESPACEclass QFtpPrivate;class QFtp : public QObject {Q_OBJECTpublic:explicit QFtp(QObject *parent = 0);virtual ~QFtp();enum State {Unconnected,HostLookup,Connecting,Connected,LoggedIn,Closing};enum Error {NoError,UnknownError,HostNotFound,ConnectionRefused,NotConnected};enum Command {None,SetTransferMode,SetProxy,ConnectToHost,Login,Close,List,Cd,Get,Put,Remove,Mkdir,Rmdir,Rename,RawCommand};enum TransferMode {Active,Passive};enum TransferType {Binary,Ascii};int setProxy(const QString &host, quint16 port);int connectToHost(const QString &host, quint16 port=21);int login(const QString &user = QString(), const QString &password = QString());int close();int setTransferMode(TransferMode mode);int list(const QString &dir = QString());int cd(const QString &dir);int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);int put(const QByteArray &data, const QString &file, TransferType type = Binary);int put(QIODevice *dev, const QString &file, TransferType type = Binary);int remove(const QString &file);int mkdir(const QString &dir);int rmdir(const QString &dir);int rename(const QString &oldname, const QString &newname);int rawCommand(const QString &command);qint64 bytesAvailable() const;qint64 read(char *data, qint64 maxlen);QByteArray readAll();int currentId() const;QIODevice* currentDevice() const;Command currentCommand() const;bool hasPendingCommands() const;void clearPendingCommands();State state() const;Error error() const;QString errorString() const;public Q_SLOTS:void abort();Q_SIGNALS:void stateChanged(int);void listInfo(const QUrlInfo&);void readyRead();void dataTransferProgress(qint64, qint64);void rawCommandReply(int, const QString&);void commandStarted(int);void commandFinished(int, bool);void done(bool);private:Q_DISABLE_COPY(QFtp)QScopedPointer<QFtpPrivate> d;Q_PRIVATE_SLOT(d, void _q_startNextCommand())Q_PRIVATE_SLOT(d, void _q_piFinished(const QString&))Q_PRIVATE_SLOT(d, void _q_piError(int, const QString&))Q_PRIVATE_SLOT(d, void _q_piConnectState(int))Q_PRIVATE_SLOT(d, void _q_piFtpReply(int, const QString&)) };QT_END_NAMESPACE#endif // QFTP_H


    qurlinfo.h

    /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/#ifndef QURLINFO_H #define QURLINFO_H#include <QtCore/qdatetime.h> #include <QtCore/qstring.h> #include <QtCore/qiodevice.h>QT_BEGIN_NAMESPACEclass QUrl; class QUrlInfoPrivate;class QUrlInfo { public:enum PermissionSpec {ReadOwner = 00400, WriteOwner = 00200, ExeOwner = 00100,ReadGroup = 00040, WriteGroup = 00020, ExeGroup = 00010,ReadOther = 00004, WriteOther = 00002, ExeOther = 00001 };QUrlInfo();QUrlInfo(const QUrlInfo &ui);QUrlInfo(const QString &name, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable);QUrlInfo(const QUrl &url, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable);QUrlInfo &operator=(const QUrlInfo &ui);virtual ~QUrlInfo();virtual void setName(const QString &name);virtual void setDir(bool b);virtual void setFile(bool b);virtual void setSymLink(bool b);virtual void setOwner(const QString &s);virtual void setGroup(const QString &s);virtual void setSize(qint64 size);virtual void setWritable(bool b);virtual void setReadable(bool b);virtual void setPermissions(int p);virtual void setLastModified(const QDateTime &dt);void setLastRead(const QDateTime &dt);bool isValid() const;QString name() const;int permissions() const;QString owner() const;QString group() const;qint64 size() const;QDateTime lastModified() const;QDateTime lastRead() const;bool isDir() const;bool isFile() const;bool isSymLink() const;bool isWritable() const;bool isReadable() const;bool isExecutable() const;static bool greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy);static bool lessThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy);static bool equal(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy);bool operator==(const QUrlInfo &i) const;inline bool operator!=(const QUrlInfo &i) const{ return !operator==(i); }private:QUrlInfoPrivate *d; };QT_END_NAMESPACE#endif // QURLINFO_H


    main.cpp

    #include "mainwindow.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.show();return a.exec(); }

    mainwindow.cpp

    #include "mainwindow.h" #include "ui_mainwindow.h" #include "qftp.h" #include <QTextCodec> #include <QFile> #include <QTreeWidgetItem>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);ui->progressBar->setValue(0);connect(ui->fileList, &QTreeWidget::itemActivated,this, &MainWindow::processItem); }MainWindow::~MainWindow() {delete ui; }void MainWindow::ftpCommandStarted(int) {int id = ftp->currentCommand();switch (id){case QFtp::ConnectToHost :ui->label->setText(tr("正在連接到服務器…"));break;case QFtp::Login :ui->label->setText(tr("正在登錄…"));break;case QFtp::Get :ui->label->setText(tr("正在下載…"));break;case QFtp::Close :ui->label->setText(tr("正在關閉連接…"));} }void MainWindow::ftpCommandFinished(int, bool error) {if(ftp->currentCommand() == QFtp::ConnectToHost) {if (error)ui->label->setText(tr("連接服務器出現錯誤:%1").arg(ftp->errorString()));else ui->label->setText(tr("連接到服務器成功"));} else if (ftp->currentCommand() == QFtp::Login) {if (error)ui->label->setText(tr("登錄出現錯誤:%1").arg(ftp->errorString()));else {ui->label->setText(tr("登錄成功"));ftp->list(); //login命令結束時調用了list()函數來顯示目錄中的內容。}} else if (ftp->currentCommand() == QFtp::Get) {if(error)ui->label->setText(tr("下載出現錯誤:%1").arg(ftp->errorString()));else {ui->label->setText(tr("已經完成下載"));file->close();}ui->downloadButton->setEnabled(true);} else if (ftp->currentCommand() == QFtp::List) //對QFtp::List命令的判斷{if (isDirectory.isEmpty())//如果目錄為空,則進行提示{ui->fileList->addTopLevelItem(new QTreeWidgetItem(QStringList()<< tr("<empty>")));ui->fileList->setEnabled(false);ui->label->setText(tr("該目錄為空"));}}else if (ftp->currentCommand() == QFtp::Close){ui->label->setText(tr("已經關閉連接"));} }// 連接按鈕 void MainWindow::on_connectButton_clicked() {ui->fileList->clear();currentPath.clear();isDirectory.clear();ui->progressBar->setValue(0);ftp = new QFtp(this); //創建QFtp對象connect(ftp, &QFtp::commandStarted, this,&MainWindow::ftpCommandStarted);connect(ftp, &QFtp::commandFinished,this, &MainWindow::ftpCommandFinished);connect(ftp, &QFtp::listInfo, this, &MainWindow::addToList);connect(ftp, &QFtp::dataTransferProgress,this, &MainWindow::updateDataTransferProgress);//一下登陸ftp服務器QString ftpServer = ui->ftpServerLineEdit->text();QString userName = ui->userNameLineEdit->text();QString passWord = ui->passWordLineEdit->text();ftp->connectToHost(ftpServer, 21);//ftp發送控制命令的端口號是21端口號,傳輸數據的端口號是20ftp->login(userName, passWord); }void MainWindow::addToList(const QUrlInfo &urlInfo) {// 注意:因為服務器上文件使用UTF-8編碼,所以要進行編碼轉換,這樣顯示中文才不會亂碼QString name = QString::fromUtf8(urlInfo.name().toLatin1());QString owner = QString::fromUtf8(urlInfo.owner().toLatin1());QString group = QString::fromUtf8(urlInfo.group().toLatin1());QTreeWidgetItem *item = new QTreeWidgetItem;item->setText(0, name);item->setText(1, QString::number(urlInfo.size()));item->setText(2, owner);item->setText(3, group);item->setText(4, urlInfo.lastModified().toString("yyyy-MM-dd"));QPixmap pixmap(urlInfo.isDir() ? "../myFTP/dir.png" : "../myFTP/file.png");item->setIcon(0, pixmap);isDirectory[name] = urlInfo.isDir();ui->fileList->addTopLevelItem(item);if (!ui->fileList->currentItem()) {ui->fileList->setCurrentItem(ui->fileList->topLevelItem(0));ui->fileList->setEnabled(true);} }void MainWindow::processItem(QTreeWidgetItem *item, int) {// 如果這個文件是個目錄,則打開if (isDirectory.value(item->text(0))) {// 注意:因為目錄名稱可能是中文,在使用ftp命令cd()前需要先進行編碼轉換QString name = QLatin1String(item->text(0).toUtf8());ui->fileList->clear();isDirectory.clear();currentPath += "/";currentPath += name;ftp->cd(name);ftp->list();ui->cdToParentButton->setEnabled(true);} }// 返回上級目錄按鈕 void MainWindow::on_cdToParentButton_clicked() {ui->fileList->clear();isDirectory.clear();currentPath = currentPath.left(currentPath.lastIndexOf('/'));if (currentPath.isEmpty()) {ui->cdToParentButton->setEnabled(false);ftp->cd("/");} else {ftp->cd(currentPath);}ftp->list(); }// 下載按鈕 void MainWindow::on_downloadButton_clicked() {// 注意:因為文件名稱可能是中文,所以在使用get()函數前需要進行編碼轉換QString fileName = ui->fileList->currentItem()->text(0);QString name = QLatin1String(fileName.toUtf8());file = new QFile(fileName);if (!file->open(QIODevice::WriteOnly)) {delete file;return;}ui->downloadButton->setEnabled(false);ftp->get(name, file); }void MainWindow::updateDataTransferProgress(qint64 readBytes,qint64 totalBytes) {ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(readBytes); }

    qtfp.cpp

    /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************///#define QFTPPI_DEBUG //#define QFTPDTP_DEBUG#include "qftp.h" #include "qabstractsocket.h"#ifndef QT_NO_FTP#include "qcoreapplication.h" #include "qtcpsocket.h" #include "qurlinfo.h" #include "qstringlist.h" #include "qregexp.h" #include "qtimer.h" #include "qfileinfo.h" #include "qhash.h" #include "qtcpserver.h" #include "qlocale.h"QT_BEGIN_NAMESPACEclass QFtpPI;/*The QFtpDTP (DTP = Data Transfer Process) controls all client sidedata transfer between the client and server. */ class QFtpDTP : public QObject {Q_OBJECTpublic:enum ConnectState {CsHostFound,CsConnected,CsClosed,CsHostNotFound,CsConnectionRefused};QFtpDTP(QFtpPI *p, QObject *parent = 0);void setData(QByteArray *);void setDevice(QIODevice *);void writeData();void setBytesTotal(qint64 bytes);bool hasError() const;QString errorMessage() const;void clearError();void connectToHost(const QString & host, quint16 port);int setupListener(const QHostAddress &address);void waitForConnection();QTcpSocket::SocketState state() const;qint64 bytesAvailable() const;qint64 read(char *data, qint64 maxlen);QByteArray readAll();void abortConnection();static bool parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info);signals:void listInfo(const QUrlInfo&);void readyRead();void dataTransferProgress(qint64, qint64);void connectState(int);private slots:void socketConnected();void socketReadyRead();void socketError(QAbstractSocket::SocketError);void socketConnectionClosed();void socketBytesWritten(qint64);void setupSocket();void dataReadyRead();private:void clearData();QTcpSocket *socket;QTcpServer listener;QFtpPI *pi;QString err;qint64 bytesDone;qint64 bytesTotal;bool callWriteData;// If is_ba is true, ba is used; ba is never 0.// Otherwise dev is used; dev can be 0 or not.union {QByteArray *ba;QIODevice *dev;} data;bool is_ba;QByteArray bytesFromSocket; };/************************************************************************ QFtpPI - Protocol Interpreter**********************************************************************/class QFtpPI : public QObject {Q_OBJECTpublic:QFtpPI(QObject *parent = 0);void connectToHost(const QString &host, quint16 port);bool sendCommands(const QStringList &cmds);bool sendCommand(const QString &cmd){ return sendCommands(QStringList(cmd)); }void clearPendingCommands();void abort();QString currentCommand() const{ return currentCmd; }bool rawCommand;bool transferConnectionExtended;QFtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it// makes the design simpler this way signals:void connectState(int);void finished(const QString&);void error(int, const QString&);void rawFtpReply(int, const QString&);private slots:void hostFound();void connected();void connectionClosed();void delayedCloseFinished();void readyRead();void error(QAbstractSocket::SocketError);void dtpConnectState(int);private:// the states are modelled after the generalized state diagram of RFC 959,// page 58enum State {Begin,Idle,Waiting,Success,Failure};enum AbortState {None,AbortStarted,WaitForAbortToFinish};bool processReply();bool startNextCmd();QTcpSocket commandSocket;QString replyText;char replyCode[3];State state;AbortState abortState;QStringList pendingCommands;QString currentCmd;bool waitForDtpToConnect;bool waitForDtpToClose;QByteArray bytesFromSocket;friend class QFtpDTP; };/************************************************************************ QFtpCommand implemenatation**********************************************************************/ class QFtpCommand { public:QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba);QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev = 0);~QFtpCommand();int id;QFtp::Command command;QStringList rawCmds;// If is_ba is true, ba is used; ba is never 0.// Otherwise dev is used; dev can be 0 or not.union {QByteArray *ba;QIODevice *dev;} data;bool is_ba;static QBasicAtomicInt idCounter; };QBasicAtomicInt QFtpCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba): command(cmd), rawCmds(raw), is_ba(true) {id = idCounter.fetchAndAddRelaxed(1);data.ba = new QByteArray(ba); }QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev): command(cmd), rawCmds(raw), is_ba(false) {id = idCounter.fetchAndAddRelaxed(1);data.dev = dev; }QFtpCommand::~QFtpCommand() {if (is_ba)delete data.ba; }/************************************************************************ QFtpDTP implemenatation**********************************************************************/ QFtpDTP::QFtpDTP(QFtpPI *p, QObject *parent) :QObject(parent),socket(0),listener(this),pi(p),callWriteData(false) {clearData();listener.setObjectName(QLatin1String("QFtpDTP active state server"));connect(&listener, SIGNAL(newConnection()), SLOT(setupSocket())); }void QFtpDTP::setData(QByteArray *ba) {is_ba = true;data.ba = ba; }void QFtpDTP::setDevice(QIODevice *dev) {is_ba = false;data.dev = dev; }void QFtpDTP::setBytesTotal(qint64 bytes) {bytesTotal = bytes;bytesDone = 0;emit dataTransferProgress(bytesDone, bytesTotal); }void QFtpDTP::connectToHost(const QString & host, quint16 port) {bytesFromSocket.clear();if (socket) {delete socket;socket = 0;}socket = new QTcpSocket(this); #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the socketsocket->setProperty("_q_networksession", property("_q_networksession")); #endifsocket->setObjectName(QLatin1String("QFtpDTP Passive state socket"));connect(socket, SIGNAL(connected()), SLOT(socketConnected()));connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));socket->connectToHost(host, port); }int QFtpDTP::setupListener(const QHostAddress &address) { #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the socketlistener.setProperty("_q_networksession", property("_q_networksession")); #endifif (!listener.isListening() && !listener.listen(address, 0))return -1;return listener.serverPort(); }void QFtpDTP::waitForConnection() {// This function is only interesting in Active transfer mode; it works// around a limitation in QFtp's design by blocking, waiting for an// incoming connection. For the default Passive mode, it does nothing.if (listener.isListening())listener.waitForNewConnection(); }QTcpSocket::SocketState QFtpDTP::state() const {return socket ? socket->state() : QTcpSocket::UnconnectedState; }qint64 QFtpDTP::bytesAvailable() const {if (!socket || socket->state() != QTcpSocket::ConnectedState)return (qint64) bytesFromSocket.size();return socket->bytesAvailable(); }qint64 QFtpDTP::read(char *data, qint64 maxlen) {qint64 read;if (socket && socket->state() == QTcpSocket::ConnectedState) {read = socket->read(data, maxlen);} else {read = qMin(maxlen, qint64(bytesFromSocket.size()));memcpy(data, bytesFromSocket.data(), read);bytesFromSocket.remove(0, read);}bytesDone += read;return read; }QByteArray QFtpDTP::readAll() {QByteArray tmp;if (socket && socket->state() == QTcpSocket::ConnectedState) {tmp = socket->readAll();bytesDone += tmp.size();} else {tmp = bytesFromSocket;bytesFromSocket.clear();}return tmp; }void QFtpDTP::writeData() {if (!socket)return;if (is_ba) { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::writeData: write %d bytes", data.ba->size()); #endifif (data.ba->size() == 0)emit dataTransferProgress(0, bytesTotal);elsesocket->write(data.ba->data(), data.ba->size());socket->close();clearData();} else if (data.dev) {callWriteData = false;const qint64 blockSize = 16*1024;char buf[16*1024];qint64 read = data.dev->read(buf, blockSize); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::writeData: write() of size %lli bytes", read); #endifif (read > 0) {socket->write(buf, read);} else if (read == -1 || (!data.dev->isSequential() && data.dev->atEnd())) {// error or EOFif (bytesDone == 0 && socket->bytesToWrite() == 0)emit dataTransferProgress(0, bytesTotal);socket->close();clearData();}// do we continue uploading?callWriteData = data.dev != 0;} }void QFtpDTP::dataReadyRead() {writeData(); }inline bool QFtpDTP::hasError() const {return !err.isNull(); }inline QString QFtpDTP::errorMessage() const {return err; }inline void QFtpDTP::clearError() {err.clear(); }void QFtpDTP::abortConnection() { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::abortConnection, bytesAvailable == %lli",socket ? socket->bytesAvailable() : (qint64) 0); #endifcallWriteData = false;clearData();if (socket)socket->abort(); }static void _q_fixupDateTime(QDateTime *dateTime) {// Adjust for future tolerance.const int futureTolerance = 86400;if (dateTime->secsTo(QDateTime::currentDateTime()) < -futureTolerance) {QDate d = dateTime->date();d.setDate(d.year() - 1, d.month(), d.day());dateTime->setDate(d);} }static void _q_parseUnixDir(const QStringList &tokens, const QString &userName, QUrlInfo *info) {// Unix style, 7 + 1 entries// -rw-r--r-- 1 ftp ftp 17358091 Aug 10 2004 qt-x11-free-3.3.3.tar.gz// drwxr-xr-x 3 ftp ftp 4096 Apr 14 2000 compiled-examples// lrwxrwxrwx 1 ftp ftp 9 Oct 29 2005 qtscape -> qtmozillaif (tokens.size() != 8)return;char first = tokens.at(1).at(0).toLatin1();if (first == 'd') {info->setDir(true);info->setFile(false);info->setSymLink(false);} else if (first == '-') {info->setDir(false);info->setFile(true);info->setSymLink(false);} else if (first == 'l') {info->setDir(true);info->setFile(false);info->setSymLink(true);}// Resolve filenameQString name = tokens.at(7);if (info->isSymLink()) {int linkPos = name.indexOf(QLatin1String(" ->"));if (linkPos != -1)name.resize(linkPos);}info->setName(name);// Resolve owner & groupinfo->setOwner(tokens.at(3));info->setGroup(tokens.at(4));// Resolve sizeinfo->setSize(tokens.at(5).toLongLong());QStringList formats;formats << QLatin1String("MMM dd yyyy") << QLatin1String("MMM dd hh:mm") << QLatin1String("MMM d yyyy")<< QLatin1String("MMM d hh:mm") << QLatin1String("MMM d yyyy") << QLatin1String("MMM dd yyyy");QString dateString = tokens.at(6);dateString[0] = dateString[0].toUpper();// Resolve the modification date by parsing all possible formatsQDateTime dateTime;int n = 0; #ifndef QT_NO_DATESTRINGdo {dateTime = QLocale::c().toDateTime(dateString, formats.at(n++));} while (n < formats.size() && (!dateTime.isValid())); #endifif (n == 2 || n == 4) {// Guess the year.dateTime.setDate(QDate(QDate::currentDate().year(),dateTime.date().month(),dateTime.date().day()));_q_fixupDateTime(&dateTime);}if (dateTime.isValid())info->setLastModified(dateTime);// Resolve permissionsint permissions = 0;QString p = tokens.at(2);permissions |= (p[0] == QLatin1Char('r') ? QUrlInfo::ReadOwner : 0);permissions |= (p[1] == QLatin1Char('w') ? QUrlInfo::WriteOwner : 0);permissions |= (p[2] == QLatin1Char('x') ? QUrlInfo::ExeOwner : 0);permissions |= (p[3] == QLatin1Char('r') ? QUrlInfo::ReadGroup : 0);permissions |= (p[4] == QLatin1Char('w') ? QUrlInfo::WriteGroup : 0);permissions |= (p[5] == QLatin1Char('x') ? QUrlInfo::ExeGroup : 0);permissions |= (p[6] == QLatin1Char('r') ? QUrlInfo::ReadOther : 0);permissions |= (p[7] == QLatin1Char('w') ? QUrlInfo::WriteOther : 0);permissions |= (p[8] == QLatin1Char('x') ? QUrlInfo::ExeOther : 0);info->setPermissions(permissions);bool isOwner = info->owner() == userName;info->setReadable((permissions & QUrlInfo::ReadOther) || ((permissions & QUrlInfo::ReadOwner) && isOwner));info->setWritable((permissions & QUrlInfo::WriteOther) || ((permissions & QUrlInfo::WriteOwner) && isOwner)); }static void _q_parseDosDir(const QStringList &tokens, const QString &userName, QUrlInfo *info) {// DOS style, 3 + 1 entries// 01-16-02 11:14AM <DIR> epsgroup// 06-05-03 03:19PM 1973 readme.txtif (tokens.size() != 4)return;Q_UNUSED(userName);QString name = tokens.at(3);info->setName(name);info->setSymLink(name.toLower().endsWith(QLatin1String(".lnk")));if (tokens.at(2) == QLatin1String("<DIR>")) {info->setFile(false);info->setDir(true);} else {info->setFile(true);info->setDir(false);info->setSize(tokens.at(2).toLongLong());}// Note: We cannot use QFileInfo; permissions are for the server-side// machine, and QFileInfo's behavior depends on the local platform.int permissions = QUrlInfo::ReadOwner | QUrlInfo::WriteOwner| QUrlInfo::ReadGroup | QUrlInfo::WriteGroup| QUrlInfo::ReadOther | QUrlInfo::WriteOther;QString ext;int extIndex = name.lastIndexOf(QLatin1Char('.'));if (extIndex != -1)ext = name.mid(extIndex + 1);if (ext == QLatin1String("exe") || ext == QLatin1String("bat") || ext == QLatin1String("com"))permissions |= QUrlInfo::ExeOwner | QUrlInfo::ExeGroup | QUrlInfo::ExeOther;info->setPermissions(permissions);info->setReadable(true);info->setWritable(info->isFile());QDateTime dateTime; #ifndef QT_NO_DATESTRINGdateTime = QLocale::c().toDateTime(tokens.at(1), QLatin1String("MM-dd-yy hh:mmAP"));if (dateTime.date().year() < 1971) {dateTime.setDate(QDate(dateTime.date().year() + 100,dateTime.date().month(),dateTime.date().day()));} #endifinfo->setLastModified(dateTime);}bool QFtpDTP::parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info) {if (buffer.isEmpty())return false;QString bufferStr = QString::fromLatin1(buffer).trimmed();// Unix style FTP serversQRegExp unixPattern(QLatin1String("^([\\-dl])([a-zA-Z\\-]{9,9})\\s+\\d+\\s+(\\S*)\\s+""(\\S*)\\s+(\\d+)\\s+(\\S+\\s+\\S+\\s+\\S+)\\s+(\\S.*)"));if (unixPattern.indexIn(bufferStr) == 0) {_q_parseUnixDir(unixPattern.capturedTexts(), userName, info);return true;}// DOS style FTP serversQRegExp dosPattern(QLatin1String("^(\\d\\d-\\d\\d-\\d\\d\\d?\\d?\\ \\ \\d\\d:\\d\\d[AP]M)\\s+""(<DIR>|\\d+)\\s+(\\S.*)$"));if (dosPattern.indexIn(bufferStr) == 0) {_q_parseDosDir(dosPattern.capturedTexts(), userName, info);return true;}// Unsupportedreturn false; }void QFtpDTP::socketConnected() {bytesDone = 0; #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsConnected)"); #endifemit connectState(QFtpDTP::CsConnected); }void QFtpDTP::socketReadyRead() {if (!socket)return;if (pi->currentCommand().isEmpty()) {socket->close(); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsClosed)"); #endifemit connectState(QFtpDTP::CsClosed);return;}if (pi->abortState == QFtpPI::AbortStarted) {// discard datasocket->readAll();return;}if (pi->currentCommand().startsWith(QLatin1String("LIST"))) {while (socket->canReadLine()) {QUrlInfo i;QByteArray line = socket->readLine(); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP read (list): '%s'", line.constData()); #endifif (parseDir(line, QLatin1String(""), &i)) {emit listInfo(i);} else {// some FTP servers don't return a 550 if the file or directory// does not exist, but rather write a text to the data socket// -- try to catch these casesif (line.endsWith("No such file or directory\r\n"))err = QString::fromLatin1(line);}}} else {if (!is_ba && data.dev) {do {QByteArray ba;ba.resize(socket->bytesAvailable());qint64 bytesRead = socket->read(ba.data(), ba.size());if (bytesRead < 0) {// a read following a readyRead() signal will// never fail.return;}ba.resize(bytesRead);bytesDone += bytesRead; #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP read: %lli bytes (total %lli bytes)", bytesRead, bytesDone); #endifif (data.dev) // make sure it wasn't deleted in the slotdata.dev->write(ba);emit dataTransferProgress(bytesDone, bytesTotal);// Need to loop; dataTransferProgress is often connected to// slots that update the GUI (e.g., progress bar values), and// if events are processed, more data may have arrived.} while (socket->bytesAvailable());} else { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP readyRead: %lli bytes available (total %lli bytes read)",bytesAvailable(), bytesDone); #endifemit dataTransferProgress(bytesDone+socket->bytesAvailable(), bytesTotal);emit readyRead();}} }void QFtpDTP::socketError(QAbstractSocket::SocketError e) {if (e == QTcpSocket::HostNotFoundError) { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsHostNotFound)"); #endifemit connectState(QFtpDTP::CsHostNotFound);} else if (e == QTcpSocket::ConnectionRefusedError) { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsConnectionRefused)"); #endifemit connectState(QFtpDTP::CsConnectionRefused);} }void QFtpDTP::socketConnectionClosed() {if (!is_ba && data.dev) {clearData();}bytesFromSocket = socket->readAll(); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsClosed)"); #endifemit connectState(QFtpDTP::CsClosed); }void QFtpDTP::socketBytesWritten(qint64 bytes) {bytesDone += bytes; #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::bytesWritten(%lli)", bytesDone); #endifemit dataTransferProgress(bytesDone, bytesTotal);if (callWriteData)writeData(); }void QFtpDTP::setupSocket() {socket = listener.nextPendingConnection();socket->setObjectName(QLatin1String("QFtpDTP Active state socket"));connect(socket, SIGNAL(connected()), SLOT(socketConnected()));connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));listener.close(); }void QFtpDTP::clearData() {is_ba = false;data.dev = 0; }/************************************************************************ QFtpPI implemenatation**********************************************************************/ QFtpPI::QFtpPI(QObject *parent) :QObject(parent),rawCommand(false),transferConnectionExtended(true),dtp(this),commandSocket(0),state(Begin), abortState(None),currentCmd(QString()),waitForDtpToConnect(false),waitForDtpToClose(false) {commandSocket.setObjectName(QLatin1String("QFtpPI_socket"));connect(&commandSocket, SIGNAL(hostFound()),SLOT(hostFound()));connect(&commandSocket, SIGNAL(connected()),SLOT(connected()));connect(&commandSocket, SIGNAL(disconnected()),SLOT(connectionClosed()));connect(&commandSocket, SIGNAL(readyRead()),SLOT(readyRead()));connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),SLOT(error(QAbstractSocket::SocketError)));connect(&dtp, SIGNAL(connectState(int)),SLOT(dtpConnectState(int))); }void QFtpPI::connectToHost(const QString &host, quint16 port) {emit connectState(QFtp::HostLookup); #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the socket & DTPcommandSocket.setProperty("_q_networksession", property("_q_networksession"));dtp.setProperty("_q_networksession", property("_q_networksession")); #endifcommandSocket.connectToHost(host, port); }/*Sends the sequence of commands \a cmds to the FTP server. When the commandsare all done the finished() signal is emitted. When an error occurs, theerror() signal is emitted.If there are pending commands in the queue this functions returns false andthe \a cmds are not added to the queue; otherwise it returns true. */ bool QFtpPI::sendCommands(const QStringList &cmds) {if (!pendingCommands.isEmpty())return false;if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {emit error(QFtp::NotConnected, QFtp::tr("Not connected"));return true; // there are no pending commands}pendingCommands = cmds;startNextCmd();return true; }void QFtpPI::clearPendingCommands() {pendingCommands.clear();dtp.abortConnection();currentCmd.clear();state = Idle; }void QFtpPI::abort() {pendingCommands.clear();if (abortState != None)// ABOR already sentreturn;abortState = AbortStarted; #if defined(QFTPPI_DEBUG)qDebug("QFtpPI send: ABOR"); #endifcommandSocket.write("ABOR\r\n", 6);if (currentCmd.startsWith(QLatin1String("STOR ")))dtp.abortConnection(); }void QFtpPI::hostFound() {emit connectState(QFtp::Connecting); }void QFtpPI::connected() {state = Begin; #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [connected()]", state); #endif// try to improve performance by setting TCP_NODELAYcommandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);emit connectState(QFtp::Connected); }void QFtpPI::connectionClosed() {commandSocket.close();emit connectState(QFtp::Unconnected); }void QFtpPI::delayedCloseFinished() {emit connectState(QFtp::Unconnected); }void QFtpPI::error(QAbstractSocket::SocketError e) {if (e == QTcpSocket::HostNotFoundError) {emit connectState(QFtp::Unconnected);emit error(QFtp::HostNotFound,QFtp::tr("Host %1 not found").arg(commandSocket.peerName()));} else if (e == QTcpSocket::ConnectionRefusedError) {emit connectState(QFtp::Unconnected);emit error(QFtp::ConnectionRefused,QFtp::tr("Connection refused to host %1").arg(commandSocket.peerName()));} else if (e == QTcpSocket::SocketTimeoutError) {emit connectState(QFtp::Unconnected);emit error(QFtp::ConnectionRefused,QFtp::tr("Connection timed out to host %1").arg(commandSocket.peerName()));} }void QFtpPI::readyRead() {if (waitForDtpToClose)return;while (commandSocket.canReadLine()) {// read line with respect to line continuationQString line = QString::fromLatin1(commandSocket.readLine());if (replyText.isEmpty()) {if (line.length() < 3) {// protocol errorreturn;}const int lowerLimit[3] = {1,0,0};const int upperLimit[3] = {5,5,9};for (int i=0; i<3; i++) {replyCode[i] = line[i].digitValue();if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {// protocol errorreturn;}}}QString endOfMultiLine;endOfMultiLine[0] = '0' + replyCode[0];endOfMultiLine[1] = '0' + replyCode[1];endOfMultiLine[2] = '0' + replyCode[2];endOfMultiLine[3] = QLatin1Char(' ');QString lineCont(endOfMultiLine);lineCont[3] = QLatin1Char('-');QString lineLeft4 = line.left(4);while (lineLeft4 != endOfMultiLine) {if (lineLeft4 == lineCont)replyText += line.mid(4); // strip 'xyz-'elsereplyText += line;if (!commandSocket.canReadLine())return;line = QString::fromLatin1(commandSocket.readLine());lineLeft4 = line.left(4);}replyText += line.mid(4); // strip reply code 'xyz 'if (replyText.endsWith(QLatin1String("\r\n")))replyText.chop(2);if (processReply())replyText = QLatin1String("");} }/*Process a reply from the FTP server.Returns true if the reply was processed or false if the reply has to beprocessed at a later point. */ bool QFtpPI::processReply() { #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [processReply() begin]", state);if (replyText.length() < 400)qDebug("QFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());elseqDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]); #endifint replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];// process 226 replies ("Closing Data Connection") only when the data// connection is really closed to avoid short reads of the DTPif (replyCodeInt == 226 || (replyCodeInt == 250 && currentCmd.startsWith(QLatin1String("RETR")))) {if (dtp.state() != QTcpSocket::UnconnectedState) {waitForDtpToClose = true;return false;}}switch (abortState) {case AbortStarted:abortState = WaitForAbortToFinish;break;case WaitForAbortToFinish:abortState = None;return true;default:break;}// get new statestatic const State table[5] = {/* 1yz 2yz 3yz 4yz 5yz */Waiting, Success, Idle, Failure, Failure};switch (state) {case Begin:if (replyCode[0] == 1) {return true;} else if (replyCode[0] == 2) {state = Idle;emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));break;}// reply codes not starting with 1 or 2 are not handled.return true;case Waiting:if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)state = Failure;else #if defined(Q_OS_IRIX) && defined(Q_CC_GNU){// work around a crash on 64 bit gcc IRIXState *t = (State *) table;state = t[replyCode[0] - 1];} #elseif (replyCodeInt == 202)state = Failure;elsestate = table[replyCode[0] - 1]; #endifbreak;default:// ignore unrequested messagereturn true;} #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [processReply() intermediate]", state); #endif// special actions on certain repliesemit rawFtpReply(replyCodeInt, replyText);if (rawCommand) {rawCommand = false;} else if (replyCodeInt == 227) {// 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)// rfc959 does not define this response precisely, and gives// both examples where the parenthesis are used, and where// they are missing. We need to scan for the address and host// info.QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));if (addrPortPattern.indexIn(replyText) == -1) { #if defined(QFTPPI_DEBUG)qDebug("QFtp: bad 227 response -- address and port information missing"); #endif// this error should be reported} else {QStringList lst = addrPortPattern.capturedTexts();QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];quint16 port = (lst[5].toUInt() << 8) + lst[6].toUInt();waitForDtpToConnect = true;dtp.connectToHost(host, port);}} else if (replyCodeInt == 229) {// 229 Extended Passive mode OK (|||10982|)int portPos = replyText.indexOf(QLatin1Char('('));if (portPos == -1) { #if defined(QFTPPI_DEBUG)qDebug("QFtp: bad 229 response -- port information missing"); #endif// this error should be reported} else {++portPos;QChar delimiter = replyText.at(portPos);QStringList epsvParameters = replyText.mid(portPos).split(delimiter);waitForDtpToConnect = true;dtp.connectToHost(commandSocket.peerAddress().toString(),epsvParameters.at(3).toInt());}} else if (replyCodeInt == 230) {if (currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&pendingCommands.first().startsWith(QLatin1String("PASS "))) {// no need to send the PASS -- we are already logged inpendingCommands.pop_front();}// 230 User logged in, proceed.emit connectState(QFtp::LoggedIn);} else if (replyCodeInt == 213) {// 213 File status.if (currentCmd.startsWith(QLatin1String("SIZE ")))dtp.setBytesTotal(replyText.simplified().toLongLong());} else if (replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR "))) {dtp.waitForConnection();dtp.writeData();}// react on new stateswitch (state) {case Begin:// should never happenbreak;case Success:// success handlingstate = Idle;// no break!case Idle:if (dtp.hasError()) {emit error(QFtp::UnknownError, dtp.errorMessage());dtp.clearError();}startNextCmd();break;case Waiting:// do nothingbreak;case Failure:// If the EPSV or EPRT commands fail, replace them with// the old PASV and PORT instead and try again.if (currentCmd.startsWith(QLatin1String("EPSV"))) {transferConnectionExtended = false;pendingCommands.prepend(QLatin1String("PASV\r\n"));} else if (currentCmd.startsWith(QLatin1String("EPRT"))) {transferConnectionExtended = false;pendingCommands.prepend(QLatin1String("PORT\r\n"));} else {emit error(QFtp::UnknownError, replyText);}if (state != Waiting) {state = Idle;startNextCmd();}break;} #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [processReply() end]", state); #endifreturn true; }/*Starts next pending command. Returns false if there are no pending commands,otherwise it returns true. */ bool QFtpPI::startNextCmd() {if (waitForDtpToConnect)// don't process any new commands until we are connectedreturn true;#if defined(QFTPPI_DEBUG)if (state != Idle)qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state); #endifif (pendingCommands.isEmpty()) {currentCmd.clear();emit finished(replyText);return false;}currentCmd = pendingCommands.first();// PORT and PASV are edited in-place, depending on whether we// should try the extended transfer connection commands EPRT and// EPSV. The PORT command also triggers setting up a listener, and// the address/port arguments are edited in.QHostAddress address = commandSocket.localAddress();if (currentCmd.startsWith(QLatin1String("PORT"))) {if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended) {int port = dtp.setupListener(address);currentCmd = QLatin1String("EPRT |");currentCmd += (address.protocol() == QTcpSocket::IPv4Protocol) ? QLatin1Char('1') : QLatin1Char('2');currentCmd += QLatin1Char('|') + address.toString() + QLatin1Char('|') + QString::number(port);currentCmd += QLatin1Char('|');} else if (address.protocol() == QTcpSocket::IPv4Protocol) {int port = dtp.setupListener(address);QString portArg;quint32 ip = address.toIPv4Address();portArg += QString::number((ip & 0xff000000) >> 24);portArg += QLatin1Char(',') + QString::number((ip & 0xff0000) >> 16);portArg += QLatin1Char(',') + QString::number((ip & 0xff00) >> 8);portArg += QLatin1Char(',') + QString::number(ip & 0xff);portArg += QLatin1Char(',') + QString::number((port & 0xff00) >> 8);portArg += QLatin1Char(',') + QString::number(port & 0xff);currentCmd = QLatin1String("PORT ");currentCmd += portArg;} else {// No IPv6 connection can be set up with the PORT// command.return false;}currentCmd += QLatin1String("\r\n");} else if (currentCmd.startsWith(QLatin1String("PASV"))) {if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended)currentCmd = QLatin1String("EPSV\r\n");}pendingCommands.pop_front(); #if defined(QFTPPI_DEBUG)qDebug("QFtpPI send: %s", currentCmd.left(currentCmd.length()-2).toLatin1().constData()); #endifstate = Waiting;commandSocket.write(currentCmd.toLatin1());return true; }void QFtpPI::dtpConnectState(int s) {switch (s) {case QFtpDTP::CsClosed:if (waitForDtpToClose) {// there is an unprocessed replyif (processReply())replyText = QLatin1String("");elsereturn;}waitForDtpToClose = false;readyRead();return;case QFtpDTP::CsConnected:waitForDtpToConnect = false;startNextCmd();return;case QFtpDTP::CsHostNotFound:case QFtpDTP::CsConnectionRefused:emit error(QFtp::ConnectionRefused,QFtp::tr("Connection refused for data connection"));startNextCmd();return;default:return;} }/************************************************************************ QFtpPrivate**********************************************************************/class QFtpPrivate {Q_DECLARE_PUBLIC(QFtp) public:inline QFtpPrivate(QFtp *owner) : close_waitForStateChange(false), state(QFtp::Unconnected),transferMode(QFtp::Passive), error(QFtp::NoError), q_ptr(owner){ }~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }// private slotsvoid _q_startNextCommand();void _q_piFinished(const QString&);void _q_piError(int, const QString&);void _q_piConnectState(int);void _q_piFtpReply(int, const QString&);int addCommand(QFtpCommand *cmd);QFtpPI pi;QList<QFtpCommand *> pending;bool close_waitForStateChange;QFtp::State state;QFtp::TransferMode transferMode;QFtp::Error error;QString errorString;QString host;quint16 port;QString proxyHost;quint16 proxyPort;QFtp *q_ptr; };int QFtpPrivate::addCommand(QFtpCommand *cmd) {pending.append(cmd);if (pending.count() == 1) {// don't emit the commandStarted() signal before the ID is returnedQTimer::singleShot(0, q_func(), SLOT(_q_startNextCommand()));}return cmd->id; }/************************************************************************ QFtp implementation**********************************************************************/ /*!\class QFtp\brief The QFtp class provides an implementation of the client side of FTP protocol.\ingroup network\inmodule QtNetworkThis class provides a direct interface to FTP that allows you tohave more control over the requests. However, for newapplications, it is recommended to use QNetworkAccessManager andQNetworkReply, as those classes possess a simpler, yet morepowerful API.The class works asynchronously, so there are no blockingfunctions. If an operation cannot be executed immediately, thefunction will still return straight away and the operation will bescheduled for later execution. The results of scheduled operationsare reported via signals. This approach depends on the event loopbeing in operation.The operations that can be scheduled (they are called "commands"in the rest of the documentation) are the following:connectToHost(), login(), close(), list(), cd(), get(), put(),remove(), mkdir(), rmdir(), rename() and rawCommand().All of these commands return a unique identifier that allows youto keep track of the command that is currently being executed.When the execution of a command starts, the commandStarted()signal with the command's identifier is emitted. When the commandis finished, the commandFinished() signal is emitted with thecommand's identifier and a bool that indicates whether the commandfinished with an error.In some cases, you might want to execute a sequence of commands,e.g. if you want to connect and login to a FTP server. This issimply achieved:\snippet doc/src/snippets/code/src_network_access_qftp.cpp 0In this case two FTP commands have been scheduled. When the lastscheduled command has finished, a done() signal is emitted witha bool argument that tells you whether the sequence finished withan error.If an error occurs during the execution of one of the commands ina sequence of commands, all the pending commands (i.e. scheduled,but not yet executed commands) are cleared and no signals areemitted for them.Some commands, e.g. list(), emit additional signals to reporttheir results.Example: If you want to download the INSTALL file from the QtFTP server, you would write this:\snippet doc/src/snippets/code/src_network_access_qftp.cpp 1For this example the following sequence of signals is emitted(with small variations, depending on network traffic, etc.):\snippet doc/src/snippets/code/src_network_access_qftp.cpp 2The dataTransferProgress() signal in the above example is usefulif you want to show a \link QProgressBar progress bar \endlink toinform the user about the progress of the download. ThereadyRead() signal tells you that there is data ready to be read.The amount of data can be queried then with the bytesAvailable()function and it can be read with the read() or readAll()function.If the login fails for the above example, the signals would looklike this:\snippet doc/src/snippets/code/src_network_access_qftp.cpp 3You can then get details about the error with the error() anderrorString() functions.For file transfer, QFtp can use both active or passive mode, andit uses passive file transfer mode by default; see thedocumentation for setTransferMode() for more details about this.Call setProxy() to make QFtp connect via an FTP proxy server.The functions currentId() and currentCommand() provide moreinformation about the currently executing command.The functions hasPendingCommands() and clearPendingCommands()allow you to query and clear the list of pending commands.If you are an experienced network programmer and want to havecomplete control you can use rawCommand() to execute arbitrary FTPcommands.\warning The current version of QFtp doesn't fully supportnon-Unix FTP servers.\sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,{FTP Example} *//*!Constructs a QFtp object with the given \a parent. */ QFtp::QFtp(QObject *parent): QObject(parent), d(new QFtpPrivate(this)) {d->errorString = tr("Unknown error");connect(&d->pi, SIGNAL(connectState(int)),SLOT(_q_piConnectState(int)));connect(&d->pi, SIGNAL(finished(QString)),SLOT(_q_piFinished(QString)));connect(&d->pi, SIGNAL(error(int,QString)),SLOT(_q_piError(int,QString)));connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),SLOT(_q_piFtpReply(int,QString)));connect(&d->pi.dtp, SIGNAL(readyRead()),SIGNAL(readyRead()));connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),SIGNAL(dataTransferProgress(qint64,qint64)));connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),SIGNAL(listInfo(QUrlInfo))); }/*!\enum QFtp::StateThis enum defines the connection state:\value Unconnected There is no connection to the host.\value HostLookup A host name lookup is in progress.\value Connecting An attempt to connect to the host is in progress.\value Connected Connection to the host has been achieved.\value LoggedIn Connection and user login have been achieved.\value Closing The connection is closing down, but it is not yetclosed. (The state will be \c Unconnected when the connection isclosed.)\sa stateChanged() state() */ /*!\enum QFtp::TransferModeFTP works with two socket connections; one for commands andanother for transmitting data. While the command connection isalways initiated by the client, the second connection can beinitiated by either the client or the server.This enum defines whether the client (Passive mode) or the server(Active mode) should set up the data connection.\value Passive The client connects to the server to transmit itsdata.\value Active The server connects to the client to transmit itsdata. */ /*!\enum QFtp::TransferTypeThis enum identifies the data transfer type used with get andput commands.\value Binary The data will be transferred in Binary mode.\value Ascii The data will be transferred in Ascii mode and new linecharacters will be converted to the local format. */ /*!\enum QFtp::ErrorThis enum identifies the error that occurred.\value NoError No error occurred.\value HostNotFound The host name lookup failed.\value ConnectionRefused The server refused the connection.\value NotConnected Tried to send a command, but there is no connection toa server.\value UnknownError An error other than those specified aboveoccurred.\sa error() *//*!\enum QFtp::CommandThis enum is used as the return value for the currentCommand() function.This allows you to perform specific actions for particularcommands, e.g. in a FTP client, you might want to clear thedirectory view when a list() command is started; in this case youcan simply check in the slot connected to the start() signal ifthe currentCommand() is \c List.\value None No command is being executed.\value SetTransferMode set the \link TransferMode transfer\endlink mode.\value SetProxy switch proxying on or off.\value ConnectToHost connectToHost() is being executed.\value Login login() is being executed.\value Close close() is being executed.\value List list() is being executed.\value Cd cd() is being executed.\value Get get() is being executed.\value Put put() is being executed.\value Remove remove() is being executed.\value Mkdir mkdir() is being executed.\value Rmdir rmdir() is being executed.\value Rename rename() is being executed.\value RawCommand rawCommand() is being executed.\sa currentCommand() *//*!\fn void QFtp::stateChanged(int state)This signal is emitted when the state of the connection changes.The argument \a state is the new state of the connection; it isone of the \l State values.It is usually emitted in response to a connectToHost() or close()command, but it can also be emitted "spontaneously", e.g. when theserver closes the connection unexpectedly.\sa connectToHost() close() state() State *//*!\fn void QFtp::listInfo(const QUrlInfo &i);This signal is emitted for each directory entry the list() commandfinds. The details of the entry are stored in \a i.\sa list() *//*!\fn void QFtp::commandStarted(int id)This signal is emitted when processing the command identified by\a id starts.\sa commandFinished() done() *//*!\fn void QFtp::commandFinished(int id, bool error)This signal is emitted when processing the command identified by\a id has finished. \a error is true if an error occurred duringthe processing; otherwise \a error is false.\sa commandStarted() done() error() errorString() *//*!\fn void QFtp::done(bool error)This signal is emitted when the last pending command has finished;(it is emitted after the last command's commandFinished() signal).\a error is true if an error occurred during the processing;otherwise \a error is false.\sa commandFinished() error() errorString() *//*!\fn void QFtp::readyRead()This signal is emitted in response to a get() command when thereis new data to read.If you specify a device as the second argument in the get()command, this signal is \e not emitted; instead the data iswritten directly to the device.You can read the data with the readAll() or read() functions.This signal is useful if you want to process the data in chunks assoon as it becomes available. If you are only interested in thecomplete data, just connect to the commandFinished() signal andread the data then instead.\sa get() read() readAll() bytesAvailable() *//*!\fn void QFtp::dataTransferProgress(qint64 done, qint64 total)This signal is emitted in response to a get() or put() request toindicate the current progress of the download or upload.\a done is the amount of data that has already been transferredand \a total is the total amount of data to be read or written. Itis possible that the QFtp class is not able to determine the totalamount of data that should be transferred, in which case \a totalis 0. (If you connect this signal to a QProgressBar, the progressbar shows a busy indicator if the total is 0).\warning \a done and \a total are not necessarily the size inbytes, since for large files these values might need to be"scaled" to avoid overflow.\sa get(), put(), QProgressBar *//*!\fn void QFtp::rawCommandReply(int replyCode, const QString &detail);This signal is emitted in response to the rawCommand() function.\a replyCode is the 3 digit reply code and \a detail is the textthat follows the reply code.\sa rawCommand() *//*!Connects to the FTP server \a host using port \a port.The stateChanged() signal is emitted when the state of theconnecting process changes, e.g. to \c HostLookup, then \cConnecting, then \c Connected.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa stateChanged() commandStarted() commandFinished() */ int QFtp::connectToHost(const QString &host, quint16 port) {QStringList cmds;cmds << host;cmds << QString::number((uint)port);int id = d->addCommand(new QFtpCommand(ConnectToHost, cmds));d->pi.transferConnectionExtended = true;return id; }/*!Logs in to the FTP server with the username \a user and thepassword \a password.The stateChanged() signal is emitted when the state of theconnecting process changes, e.g. to \c LoggedIn.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::login(const QString &user, const QString &password) {QStringList cmds;cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n"));cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n"));return d->addCommand(new QFtpCommand(Login, cmds)); }/*!Closes the connection to the FTP server.The stateChanged() signal is emitted when the state of theconnecting process changes, e.g. to \c Closing, then \cUnconnected.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa stateChanged() commandStarted() commandFinished() */ int QFtp::close() {return d->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n")))); }/*!Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.\sa QFtp::TransferMode */ int QFtp::setTransferMode(TransferMode mode) {int id = d->addCommand(new QFtpCommand(SetTransferMode, QStringList()));d->pi.transferConnectionExtended = true;d->transferMode = mode;return id; }/*!Enables use of the FTP proxy on host \a host and port \aport. Calling this function with \a host empty disables proxying.QFtp does not support FTP-over-HTTP proxy servers. UseQNetworkAccessManager for this. */ int QFtp::setProxy(const QString &host, quint16 port) {QStringList args;args << host << QString::number(port);return d->addCommand(new QFtpCommand(SetProxy, args)); }/*!Lists the contents of directory \a dir on the FTP server. If \adir is empty, it lists the contents of the current directory.The listInfo() signal is emitted for each directory entry found.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa listInfo() commandStarted() commandFinished() */ int QFtp::list(const QString &dir) {QStringList cmds;cmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");if (dir.isEmpty())cmds << QLatin1String("LIST\r\n");elsecmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));return d->addCommand(new QFtpCommand(List, cmds)); }/*!Changes the working directory of the server to \a dir.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::cd(const QString &dir) {return d->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n")))); }/*!Downloads the file \a file from the server.If \a dev is 0, then the readyRead() signal is emitted when thereis data available to read. You can then read the data with theread() or readAll() functions.If \a dev is not 0, the data is written directly to the device \adev. Make sure that the \a dev pointer is valid for the durationof the operation (it is safe to delete it when thecommandFinished() signal is emitted). In this case the readyRead()signal is \e not emitted and you cannot read data with theread() or readAll() functions.If you don't read the data immediately it becomes available, i.e.when the readyRead() signal is emitted, it is still availableuntil the next command is started.For example, if you want to present the data to the user as soonas there is something available, connect to the readyRead() signaland read the data immediately. On the other hand, if you only wantto work with the complete data, you can connect to thecommandFinished() signal and read the data when the get() commandis finished.The data is transferred as Binary or Ascii depending on the valueof \a type.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa readyRead() dataTransferProgress() commandStarted()commandFinished() */ int QFtp::get(const QString &file, QIODevice *dev, TransferType type) {QStringList cmds;if (type == Binary)cmds << QLatin1String("TYPE I\r\n");elsecmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String("SIZE ") + file + QLatin1String("\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");cmds << QLatin1String("RETR ") + file + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Get, cmds, dev)); }/*!\overloadWrites a copy of the given \a data to the file called \a file onthe server. The progress of the upload is reported by thedataTransferProgress() signal.The data is transferred as Binary or Ascii depending on the valueof \a type.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.Since this function takes a copy of the \a data, you can discardyour own copy when this function returns.\sa dataTransferProgress() commandStarted() commandFinished() */ int QFtp::put(const QByteArray &data, const QString &file, TransferType type) {QStringList cmds;if (type == Binary)cmds << QLatin1String("TYPE I\r\n");elsecmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");cmds << QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n");cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Put, cmds, data)); }/*!Reads the data from the IO device \a dev, and writes it to thefile called \a file on the server. The data is read in chunks fromthe IO device, so this overload allows you to transmit largeamounts of data without the need to read all the data into memoryat once.The data is transferred as Binary or Ascii depending on the valueof \a type.Make sure that the \a dev pointer is valid for the duration of theoperation (it is safe to delete it when the commandFinished() isemitted). */ int QFtp::put(QIODevice *dev, const QString &file, TransferType type) {QStringList cmds;if (type == Binary)cmds << QLatin1String("TYPE I\r\n");elsecmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");if (!dev->isSequential())cmds << QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n");cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Put, cmds, dev)); }/*!Deletes the file called \a file from the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::remove(const QString &file) {return d->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n")))); }/*!Creates a directory called \a dir on the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::mkdir(const QString &dir) {return d->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n")))); }/*!Removes the directory called \a dir from the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::rmdir(const QString &dir) {return d->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n")))); }/*!Renames the file called \a oldname to \a newname on the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::rename(const QString &oldname, const QString &newname) {QStringList cmds;cmds << QLatin1String("RNFR ") + oldname + QLatin1String("\r\n");cmds << QLatin1String("RNTO ") + newname + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Rename, cmds)); }/*!Sends the raw FTP command \a command to the FTP server. This isuseful for low-level FTP access. If the operation you wish toperform has an equivalent QFtp function, we recommend using thefunction instead of raw FTP commands since the functions areeasier and safer.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa rawCommandReply() commandStarted() commandFinished() */ int QFtp::rawCommand(const QString &command) {QString cmd = command.trimmed() + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(RawCommand, QStringList(cmd))); }/*!Returns the number of bytes that can be read from the data socketat the moment.\sa get() readyRead() read() readAll() */ qint64 QFtp::bytesAvailable() const {return d->pi.dtp.bytesAvailable(); }/*! \fn qint64 QFtp::readBlock(char *data, quint64 maxlen)Use read() instead. *//*!Reads \a maxlen bytes from the data socket into \a data andreturns the number of bytes read. Returns -1 if an error occurred.\sa get() readyRead() bytesAvailable() readAll() */ qint64 QFtp::read(char *data, qint64 maxlen) {return d->pi.dtp.read(data, maxlen); }/*!Reads all the bytes available from the data socket and returnsthem.\sa get() readyRead() bytesAvailable() read() */ QByteArray QFtp::readAll() {return d->pi.dtp.readAll(); }/*!Aborts the current command and deletes all scheduled commands.If there is an unfinished command (i.e. a command for which thecommandStarted() signal has been emitted, but for which thecommandFinished() signal has not been emitted), this functionsends an \c ABORT command to the server. When the server repliesthat the command is aborted, the commandFinished() signal with the\c error argument set to \c true is emitted for the command. Dueto timing issues, it is possible that the command had alreadyfinished before the abort request reached the server, in whichcase, the commandFinished() signal is emitted with the \c errorargument set to \c false.For all other commands that are affected by the abort(), nosignals are emitted.If you don't start further FTP commands directly after theabort(), there won't be any scheduled commands and the done()signal is emitted.\warning Some FTP servers, for example the BSD FTP daemon (version0.3), wrongly return a positive reply even when an abort hasoccurred. For these servers the commandFinished() signal has itserror flag set to \c false, even though the command did notcomplete successfully.\sa clearPendingCommands() */ void QFtp::abort() {if (d->pending.isEmpty())return;clearPendingCommands();d->pi.abort(); }/*!Returns the identifier of the FTP command that is being executedor 0 if there is no command being executed.\sa currentCommand() */ int QFtp::currentId() const {if (d->pending.isEmpty())return 0;return d->pending.first()->id; }/*!Returns the command type of the FTP command being executed or \cNone if there is no command being executed.\sa currentId() */ QFtp::Command QFtp::currentCommand() const {if (d->pending.isEmpty())return None;return d->pending.first()->command; }/*!Returns the QIODevice pointer that is used by the FTP command to read datafrom or store data to. If there is no current FTP command being executed orif the command does not use an IO device, this function returns 0.This function can be used to delete the QIODevice in the slot connected tothe commandFinished() signal.\sa get() put() */ QIODevice* QFtp::currentDevice() const {if (d->pending.isEmpty())return 0;QFtpCommand *c = d->pending.first();if (c->is_ba)return 0;return c->data.dev; }/*!Returns true if there are any commands scheduled that have not yetbeen executed; otherwise returns false.The command that is being executed is \e not considered as ascheduled command.\sa clearPendingCommands() currentId() currentCommand() */ bool QFtp::hasPendingCommands() const {return d->pending.count() > 1; }/*!Deletes all pending commands from the list of scheduled commands.This does not affect the command that is being executed. If youwant to stop this as well, use abort().\sa hasPendingCommands() abort() */ void QFtp::clearPendingCommands() {// delete all entires except the first onewhile (d->pending.count() > 1)delete d->pending.takeLast(); }/*!Returns the current state of the object. When the state changes,the stateChanged() signal is emitted.\sa State stateChanged() */ QFtp::State QFtp::state() const {return d->state; }/*!Returns the last error that occurred. This is useful to find outwhat went wrong when receiving a commandFinished() or a done()signal with the \c error argument set to \c true.If you start a new command, the error status is reset to \c NoError. */ QFtp::Error QFtp::error() const {return d->error; }/*!Returns a human-readable description of the last error thatoccurred. This is useful for presenting a error message to theuser when receiving a commandFinished() or a done() signal withthe \c error argument set to \c true.The error string is often (but not always) the reply from theserver, so it is not always possible to translate the string. Ifthe message comes from Qt, the string has already passed throughtr(). */ QString QFtp::errorString() const {return d->errorString; }/*! \internal */ void QFtpPrivate::_q_startNextCommand() {Q_Q(QFtp);if (pending.isEmpty())return;QFtpCommand *c = pending.first();error = QFtp::NoError;errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));if (q->bytesAvailable())q->readAll(); // clear the dataemit q->commandStarted(c->id);// Proxy support, replace the Login argument in place, then fall// through.if (c->command == QFtp::Login && !proxyHost.isEmpty()) {QString loginString = c->rawCmds.first().trimmed();loginString += QLatin1Char('@') + host;if (port && port != 21)loginString += QLatin1Char(':') + QString::number(port);loginString += QLatin1String("\r\n");c->rawCmds[0] = loginString;}if (c->command == QFtp::SetTransferMode) {_q_piFinished(QLatin1String("Transfer mode set"));} else if (c->command == QFtp::SetProxy) {proxyHost = c->rawCmds[0];proxyPort = c->rawCmds[1].toUInt();c->rawCmds.clear();_q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));} else if (c->command == QFtp::ConnectToHost) { #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the PIpi.setProperty("_q_networksession", q->property("_q_networksession")); #endifif (!proxyHost.isEmpty()) {host = c->rawCmds[0];port = c->rawCmds[1].toUInt();pi.connectToHost(proxyHost, proxyPort);} else {pi.connectToHost(c->rawCmds[0], c->rawCmds[1].toUInt());}} else {if (c->command == QFtp::Put) {if (c->is_ba) {pi.dtp.setData(c->data.ba);pi.dtp.setBytesTotal(c->data.ba->size());} else if (c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly))) {pi.dtp.setDevice(c->data.dev);if (c->data.dev->isSequential()) {pi.dtp.setBytesTotal(0);pi.dtp.connect(c->data.dev, SIGNAL(readyRead()), SLOT(dataReadyRead()));pi.dtp.connect(c->data.dev, SIGNAL(readChannelFinished()), SLOT(dataReadyRead()));} else {pi.dtp.setBytesTotal(c->data.dev->size());}}} else if (c->command == QFtp::Get) {if (!c->is_ba && c->data.dev) {pi.dtp.setDevice(c->data.dev);}} else if (c->command == QFtp::Close) {state = QFtp::Closing;emit q->stateChanged(state);}pi.sendCommands(c->rawCmds);} }/*! \internal */ void QFtpPrivate::_q_piFinished(const QString&) {if (pending.isEmpty())return;QFtpCommand *c = pending.first();if (c->command == QFtp::Close) {// The order of in which the slots are called is arbitrary, so// disconnect the SIGNAL-SIGNAL temporary to make sure that we// don't get the commandFinished() signal before the stateChanged()// signal.if (state != QFtp::Unconnected) {close_waitForStateChange = true;return;}}emit q_func()->commandFinished(c->id, false);pending.removeFirst();delete c;if (pending.isEmpty()) {emit q_func()->done(false);} else {_q_startNextCommand();} }/*! \internal */ void QFtpPrivate::_q_piError(int errorCode, const QString &text) {Q_Q(QFtp);if (pending.isEmpty()) {qWarning("QFtpPrivate::_q_piError was called without pending command!");return;}QFtpCommand *c = pending.first();// non-fatal errorsif (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {pi.dtp.setBytesTotal(0);return;} else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {return;}error = QFtp::Error(errorCode);switch (q->currentCommand()) {case QFtp::ConnectToHost:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1")).arg(text);break;case QFtp::Login:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1")).arg(text);break;case QFtp::List:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1")).arg(text);break;case QFtp::Cd:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1")).arg(text);break;case QFtp::Get:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1")).arg(text);break;case QFtp::Put:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1")).arg(text);break;case QFtp::Remove:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1")).arg(text);break;case QFtp::Mkdir:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1")).arg(text);break;case QFtp::Rmdir:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1")).arg(text);break;default:errorString = text;break;}pi.clearPendingCommands();q->clearPendingCommands();emit q->commandFinished(c->id, true);pending.removeFirst();delete c;if (pending.isEmpty())emit q->done(true);else_q_startNextCommand(); }/*! \internal */ void QFtpPrivate::_q_piConnectState(int connectState) {state = QFtp::State(connectState);emit q_func()->stateChanged(state);if (close_waitForStateChange) {close_waitForStateChange = false;_q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));} }/*! \internal */ void QFtpPrivate::_q_piFtpReply(int code, const QString &text) {if (q_func()->currentCommand() == QFtp::RawCommand) {pi.rawCommand = true;emit q_func()->rawCommandReply(code, text);} }/*!Destructor. */ QFtp::~QFtp() {abort();close(); }QT_END_NAMESPACE#include "qftp.moc"#include "moc_qftp.cpp"#endif // QT_NO_FTP

    qurlinfo.cpp

    /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/#include "qurlinfo.h"#include "qurl.h" #include "qdir.h" #include <limits.h>QT_BEGIN_NAMESPACEclass QUrlInfoPrivate { public:QUrlInfoPrivate() :permissions(0),size(0),isDir(false),isFile(true),isSymLink(false),isWritable(true),isReadable(true),isExecutable(false){}QString name;int permissions;QString owner;QString group;qint64 size;QDateTime lastModified;QDateTime lastRead;bool isDir;bool isFile;bool isSymLink;bool isWritable;bool isReadable;bool isExecutable; };/*!\class QUrlInfo\brief The QUrlInfo class stores information about URLs.\ingroup io\ingroup network\inmodule QtNetworkThe information about a URL that can be retrieved includes name(),permissions(), owner(), group(), size(), lastModified(),lastRead(), isDir(), isFile(), isSymLink(), isWritable(),isReadable() and isExecutable().You can create your own QUrlInfo objects passing in all therelevant information in the constructor, and you can modify aQUrlInfo; for each getter mentioned above there is an equivalentsetter. Note that setting values does not affect the underlyingresource that the QUrlInfo provides information about; for exampleif you call setWritable(true) on a read-only resource the onlything changed is the QUrlInfo object, not the resource.\sa QUrl, {FTP Example} *//*!\enum QUrlInfo::PermissionSpecThis enum is used by the permissions() function to report thepermissions of a file.\value ReadOwner The file is readable by the owner of the file.\value WriteOwner The file is writable by the owner of the file.\value ExeOwner The file is executable by the owner of the file.\value ReadGroup The file is readable by the group.\value WriteGroup The file is writable by the group.\value ExeGroup The file is executable by the group.\value ReadOther The file is readable by anyone.\value WriteOther The file is writable by anyone.\value ExeOther The file is executable by anyone. *//*!Constructs an invalid QUrlInfo object with default values.\sa isValid() */QUrlInfo::QUrlInfo() {d = 0; }/*!Copy constructor, copies \a ui to this URL info object. */QUrlInfo::QUrlInfo(const QUrlInfo &ui) {if (ui.d) {d = new QUrlInfoPrivate;*d = *ui.d;} else {d = 0;} }/*!Constructs a QUrlInfo object by specifying all the URL'sinformation.The information that is passed is the \a name, file \apermissions, \a owner and \a group and the file's \a size. Alsopassed is the \a lastModified date/time and the \a lastReaddate/time. Flags are also passed, specifically, \a isDir, \aisFile, \a isSymLink, \a isWritable, \a isReadable and \aisExecutable. */QUrlInfo::QUrlInfo(const QString &name, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable) {d = new QUrlInfoPrivate;d->name = name;d->permissions = permissions;d->owner = owner;d->group = group;d->size = size;d->lastModified = lastModified;d->lastRead = lastRead;d->isDir = isDir;d->isFile = isFile;d->isSymLink = isSymLink;d->isWritable = isWritable;d->isReadable = isReadable;d->isExecutable = isExecutable; }/*!Constructs a QUrlInfo object by specifying all the URL'sinformation.The information that is passed is the \a url, file \apermissions, \a owner and \a group and the file's \a size. Alsopassed is the \a lastModified date/time and the \a lastReaddate/time. Flags are also passed, specifically, \a isDir, \aisFile, \a isSymLink, \a isWritable, \a isReadable and \aisExecutable. */QUrlInfo::QUrlInfo(const QUrl &url, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable) {d = new QUrlInfoPrivate;d->name = QFileInfo(url.path()).fileName();d->permissions = permissions;d->owner = owner;d->group = group;d->size = size;d->lastModified = lastModified;d->lastRead = lastRead;d->isDir = isDir;d->isFile = isFile;d->isSymLink = isSymLink;d->isWritable = isWritable;d->isReadable = isReadable;d->isExecutable = isExecutable; }/*!Sets the name of the URL to \a name. The name is the full text,for example, "http://qt.nokia.com/doc/qurlinfo.html".If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setName(const QString &name) {if (!d)d = new QUrlInfoPrivate;d->name = name; }/*!If \a b is true then the URL is set to be a directory; if \a b isfalse then the URL is set not to be a directory (which normallymeans it is a file). (Note that a URL can refer to both a file anda directory even though most file systems do not support this.)If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setDir(bool b) {if (!d)d = new QUrlInfoPrivate;d->isDir = b; }/*!If \a b is true then the URL is set to be a file; if \b is falsethen the URL is set not to be a file (which normally means it is adirectory). (Note that a URL can refer to both a file and adirectory even though most file systems do not support this.)If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setFile(bool b) {if (!d)d = new QUrlInfoPrivate;d->isFile = b; }/*!Specifies that the URL refers to a symbolic link if \a b is trueand that it does not if \a b is false.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setSymLink(bool b) {if (!d)d = new QUrlInfoPrivate;d->isSymLink = b; }/*!Specifies that the URL is writable if \a b is true and notwritable if \a b is false.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setWritable(bool b) {if (!d)d = new QUrlInfoPrivate;d->isWritable = b; }/*!Specifies that the URL is readable if \a b is true and notreadable if \a b is false.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setReadable(bool b) {if (!d)d = new QUrlInfoPrivate;d->isReadable = b; }/*!Specifies that the owner of the URL is called \a s.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setOwner(const QString &s) {if (!d)d = new QUrlInfoPrivate;d->owner = s; }/*!Specifies that the owning group of the URL is called \a s.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setGroup(const QString &s) {if (!d)d = new QUrlInfoPrivate;d->group = s; }/*!Specifies the \a size of the URL.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setSize(qint64 size) {if (!d)d = new QUrlInfoPrivate;d->size = size; }/*!Specifies that the URL has access permissions \a p.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setPermissions(int p) {if (!d)d = new QUrlInfoPrivate;d->permissions = p; }/*!Specifies that the object the URL refers to was last modified at\a dt.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setLastModified(const QDateTime &dt) {if (!d)d = new QUrlInfoPrivate;d->lastModified = dt; }/*!\since 4.4Specifies that the object the URL refers to was last read at\a dt.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setLastRead(const QDateTime &dt) {if (!d)d = new QUrlInfoPrivate;d->lastRead = dt; }/*!Destroys the URL info object. */QUrlInfo::~QUrlInfo() {delete d; }/*!Assigns the values of \a ui to this QUrlInfo object. */QUrlInfo &QUrlInfo::operator=(const QUrlInfo &ui) {if (ui.d) {if (!d)d= new QUrlInfoPrivate;*d = *ui.d;} else {delete d;d = 0;}return *this; }/*!Returns the file name of the URL.\sa isValid() */QString QUrlInfo::name() const {if (!d)return QString();return d->name; }/*!Returns the permissions of the URL. You can use the \c PermissionSpec flagsto test for certain permissions.\sa isValid() */int QUrlInfo::permissions() const {if (!d)return 0;return d->permissions; }/*!Returns the owner of the URL.\sa isValid() */QString QUrlInfo::owner() const {if (!d)return QString();return d->owner; }/*!Returns the group of the URL.\sa isValid() */QString QUrlInfo::group() const {if (!d)return QString();return d->group; }/*!Returns the size of the URL.\sa isValid() */qint64 QUrlInfo::size() const {if (!d)return 0;return d->size; }/*!Returns the last modification date of the URL.\sa isValid() */QDateTime QUrlInfo::lastModified() const {if (!d)return QDateTime();return d->lastModified; }/*!Returns the date when the URL was last read.\sa isValid() */QDateTime QUrlInfo::lastRead() const {if (!d)return QDateTime();return d->lastRead; }/*!Returns true if the URL is a directory; otherwise returns false.\sa isValid() */bool QUrlInfo::isDir() const {if (!d)return false;return d->isDir; }/*!Returns true if the URL is a file; otherwise returns false.\sa isValid() */bool QUrlInfo::isFile() const {if (!d)return false;return d->isFile; }/*!Returns true if the URL is a symbolic link; otherwise returns false.\sa isValid() */bool QUrlInfo::isSymLink() const {if (!d)return false;return d->isSymLink; }/*!Returns true if the URL is writable; otherwise returns false.\sa isValid() */bool QUrlInfo::isWritable() const {if (!d)return false;return d->isWritable; }/*!Returns true if the URL is readable; otherwise returns false.\sa isValid() */bool QUrlInfo::isReadable() const {if (!d)return false;return d->isReadable; }/*!Returns true if the URL is executable; otherwise returns false.\sa isValid() */bool QUrlInfo::isExecutable() const {if (!d)return false;return d->isExecutable; }/*!Returns true if \a i1 is greater than \a i2; otherwise returnsfalse. The objects are compared by the value, which is specifiedby \a sortBy. This must be one of QDir::Name, QDir::Time orQDir::Size. */bool QUrlInfo::greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy) {switch (sortBy) {case QDir::Name:return i1.name() > i2.name();case QDir::Time:return i1.lastModified() > i2.lastModified();case QDir::Size:return i1.size() > i2.size();default:return false;} }/*!Returns true if \a i1 is less than \a i2; otherwise returns false.The objects are compared by the value, which is specified by \asortBy. This must be one of QDir::Name, QDir::Time or QDir::Size. */bool QUrlInfo::lessThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy) {return !greaterThan(i1, i2, sortBy); }/*!Returns true if \a i1 equals to \a i2; otherwise returns false.The objects are compared by the value, which is specified by \asortBy. This must be one of QDir::Name, QDir::Time or QDir::Size. */bool QUrlInfo::equal(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy) {switch (sortBy) {case QDir::Name:return i1.name() == i2.name();case QDir::Time:return i1.lastModified() == i2.lastModified();case QDir::Size:return i1.size() == i2.size();default:return false;} }/*!Returns true if this QUrlInfo is equal to \a other; otherwisereturns false.\sa lessThan(), equal() */bool QUrlInfo::operator==(const QUrlInfo &other) const {if (!d)return other.d == 0;if (!other.d)return false;return (d->name == other.d->name &&d->permissions == other.d->permissions &&d->owner == other.d->owner &&d->group == other.d->group &&d->size == other.d->size &&d->lastModified == other.d->lastModified &&d->lastRead == other.d->lastRead &&d->isDir == other.d->isDir &&d->isFile == other.d->isFile &&d->isSymLink == other.d->isSymLink &&d->isWritable == other.d->isWritable &&d->isReadable == other.d->isReadable &&d->isExecutable == other.d->isExecutable); }/*!\fn bool QUrlInfo::operator!=(const QUrlInfo &other) const\since 4.2Returns true if this QUrlInfo is not equal to \a other; otherwisereturns false.\sa lessThan(), equal() *//*!Returns true if the URL info is valid; otherwise returns false.Valid means that the QUrlInfo contains real information.You should always check if the URL info is valid before relying onthe values. */ bool QUrlInfo::isValid() const {return d != 0; }QT_END_NAMESPACE

    參考文獻:霍亞飛. Qt Creator快速入門[M](第三版). 北京航空航天大學出版社, 2017.

    資料:鏈接:https://pan.baidu.com/s/1eN2RKoWAC6mAcEXsWeBB_w 密碼:2koh


    總結

    以上是生活随笔為你收集整理的Qt网络编程例子的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    国产精品乱码一区二三区 | 九九热国产视频 | 人人插人人射 | 在线观看第一页 | 天天狠狠| 青春草免费在线视频 | 91最新网址在线观看 | 国产又黄又硬又爽 | 欧美日韩1区 | 中文字幕色播 | 91在线观看高清 | 国产精品18久久久久久首页狼 | 不卡视频一区二区三区 | 亚洲91中文字幕无线码三区 | 在线不卡的av | 天天添夜夜操 | 国产手机视频在线 | 国产一区二区三区视频在线 | 综合网五月天 | 久久国产一区二区三区 | 亚洲成人麻豆 | 一区二区三区在线电影 | 色婷婷综合久久久 | 日韩av一区二区在线 | 在线看片一区 | 国产精品成人av电影 | 中字幕视频在线永久在线观看免费 | 91理论电影 | 欧美福利网址 | 成人久久影院 | 久久精品久久久精品美女 | 在线观看免费91 | 久久免费精品视频 | 欧美日韩天堂 | 99久热在线精品视频观看 | 少妇视频在线播放 | 国产三级香港三韩国三级 | 九九热免费视频在线观看 | 久久久久久久国产精品 | www麻豆视频 | 国产 成人 久久 | 黄色免费在线看 | 婷婷国产v亚洲v欧美久久 | 色婷婷激情电影 | 91在线看视频免费 | 欧美精品少妇xxxxx喷水 | 高清av免费一区中文字幕 | 国产精品手机视频 | 青青草在久久免费久久免费 | 国产成人精品一区在线 | 久久伦理电影 | 欧美激情视频在线免费观看 | 精品国产一区二区三区久久久蜜臀 | 亚洲欧美激情插 | 国产精品资源在线 | 免费成人在线网站 | 亚洲精品欧美精品 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | www五月婷婷| 91中文在线观看 | 狠狠干狠狠久久 | 国产又粗又长的视频 | 日日躁天天躁 | 18国产精品白浆在线观看免费 | 中文字幕免费国产精品 | adn—256中文在线观看 | 一区在线观看 | 日韩精品一区电影 | 成人免费视频网 | 中文字幕频道 | 天天摸天天操天天爽 | 婷婷伊人综合亚洲综合网 | 又污又黄的网站 | 国产黄在线免费观看 | 黄网站色欧美视频 | 久久精品99国产国产 | 亚洲欧美成aⅴ人在线观看 四虎在线观看 | 99爱精品在线 | 日韩精品三区四区 | 岛国av在线不卡 | 国产精品一区二区 91 | 日本性生活免费看 | 一区中文字幕 | 亚洲乱码中文字幕综合 | 久久久久免费电影 | 久久久久国产视频 | 99久久久久免费精品国产 | 91夫妻自拍 | 麻豆va一区二区三区久久浪 | 最新国产精品亚洲 | 在线观看成人小视频 | 亚洲综合一区二区精品导航 | 国产丝袜 | 亚洲成人av在线播放 | 亚洲精品国产精品久久99热 | 久色网 | 国产午夜三级 | 999视频在线播放 | 人人看人人 | 亚洲午夜精品久久久久久久久久久久 | 久久午夜精品视频 | 久久久久久久久久久福利 | 欧美男同视频网站 | av在线看网站 | 不卡在线一区 | 人人爽人人 | 骄小bbw搡bbbb揉bbbb| 亚洲国产精品500在线观看 | 狠狠狠狠狠狠操 | 最新三级在线 | zzijzzij亚洲日本少妇熟睡 | 波多野结衣视频一区二区三区 | av一本久道久久波多野结衣 | 久草在线视频网站 | 99久久久久久久久久 | 久久午夜精品影院一区 | 精品在线一区二区 | www.狠狠干| 久久国语露脸国产精品电影 | 在线播放国产精品 | 一区二区三区精品在线视频 | 欧美成人aa | 日韩在线小视频 | 911久久香蕉国产线看观看 | 久久 地址 | 国产精品久久久久久久久久久免费看 | 久久视频精品 | 久草在线这里只有精品 | 久久久久久高潮国产精品视 | 国产精品中文字幕在线 | 国产午夜精品在线 | 国产免费视频在线 | 色网站在线看 | 欧美一区二区伦理片 | 久久久免费看片 | 91精品国产乱码 | 国产精品免费久久久久久久久久中文 | 天天躁天天躁天天躁婷 | 日韩av一区二区在线 | 欧美久久久久久久久中文字幕 | 91探花国产综合在线精品 | 在线观看亚洲专区 | 色婷婷视频在线 | 久久精品麻豆 | 麻豆av电影 | 精品日韩在线一区 | 人人玩人人添人人 | 久草视频看看 | 久久视频热| 日本精品一区二区三区在线播放视频 | 久久久精品欧美一区二区免费 | 91精品国产99久久久久久久 | 在线观看视频免费大全 | 免费看黄色91 | 超碰国产在线播放 | 99国产精品视频免费观看一公开 | 欧美日韩精品久久久 | 国产亚洲人成网站在线观看 | 干干日日| 国产在线探花 | 久久久福利视频 | 国产夫妻性生活自拍 | 天天色成人网 | 国产欧美精品xxxx另类 | 亚洲专区路线二 | 五月天天色 | 91精品国产91 | 久久久亚洲精华液 | 二区三区在线视频 | 欧美久久久久久久久久久久 | 国产精品18久久久久久首页狼 | 成人片在线播放 | 狠狠干 狠狠操 | 欧美性脚交 | 日日弄天天弄美女bbbb | 色综合色综合色综合 | 狠狠操狠狠干天天操 | 最新成人av| 激情xxxx| 日韩一区在线免费观看 | 欧美精品v国产精品 | 中文字幕一区二区三区乱码不卡 | 亚洲五月花 | 国产一区二区午夜 | 91最新视频在线观看 | www色com | 国产精品久久久久亚洲影视 | 成人免费观看视频网站 | 在线观看视频在线 | 国产视频日韩视频欧美视频 | 国色天香在线 | www.eeuss影院av撸 | 91最新在线| 精品一区在线看 | 97精品国产一二三产区 | 国产视频中文字幕在线观看 | 久久综合狠狠综合久久激情 | 成人avav| 久久综合偷偷噜噜噜色 | 99久久久国产免费 | 超碰大片 | 亚洲91网站 | 国产午夜在线 | 日本精品视频在线观看 | 一本大道久久精品懂色aⅴ 五月婷社区 | 96亚洲精品久久久蜜桃 | 亚洲成 人精品 | 人人爽久久涩噜噜噜网站 | 亚洲成aⅴ人片久久青草影院 | 欧美a免费 | 亚洲人成网站精品片在线观看 | 高清精品久久 | 国产精品第三页 | 中文字幕91在线 | 江苏妇搡bbbb搡bbbb | 欧美成人影音 | 在线免费观看成人 | 日日干夜夜爱 | 欧美另类激情 | 国产这里只有精品 | 国产精品久久久影视 | 免费看黄20分钟 | 国产成人免费在线观看 | 国产精品视频免费看 | 亚洲国产福利视频 | 三级黄色三级 | 射久久久| 狠狠狠狠狠狠天天爱 | 91亚洲免费 | 久久免费的视频 | 免费日韩 精品中文字幕视频在线 | 日韩电影在线一区 | 天天综合导航 | 97国产在线播放 | 国产精品久久久久久久久久尿 | 免费黄色小网站 | 亚洲综合激情 | 色婷婷综合久久久 | 婷婷久久婷婷 | 精品国产伦一区二区三区观看方式 | 国产精品对白一区二区三区 | 久久99在线观看 | 日日插日日干 | 久草在线一免费新视频 | 精品国产精品一区二区夜夜嗨 | av中文天堂在线 | 香蕉视频在线观看免费 | av国产网站 | 亚洲夜夜网| 中文字幕亚洲精品在线观看 | 美女精品国产 | 亚洲欧美日韩在线一区二区 | 波多野结衣视频一区二区三区 | 在线观看的a站 | 色就色,综合激情 | 99久久这里有精品 | 激情五月婷婷激情 | av成人免费在线观看 | 夜夜操天天摸 | 久久99久久99精品中文字幕 | 91网在线观看 | 精品免费视频123区 午夜久久成人 | 亚洲国产精品资源 | 综合天天色 | 国产精品久久久久久久久婷婷 | 亚洲精欧美一区二区精品 | 亚洲成av人影院 | 午夜精品电影一区二区在线 | 一级久久精品 | 特级a毛片 | 97人人艹 | 9999免费视频| 天天干天天天天 | 国产日韩视频在线 | 狠狠干狠狠插 | 久久影视精品 | 日韩精品一区二区三区丰满 | 91精品国产入口 | 日日夜夜狠狠干 | 天天爱天天干天天爽 | 久久成人国产精品入口 | 国产精品永久免费 | 久久精品国产一区 | 日韩乱色精品一区二区 | 色综合五月天 | 狠狠色丁香久久婷婷综合_中 | 国产在线小视频 | av大片网站| 欧美一级电影免费观看 | 99久久夜色精品国产亚洲 | 免费欧美高清视频 | 精品久久久久久久久久久院品网 | 在线国产能看的 | 99久久久国产精品免费99 | 日韩mv欧美mv国产精品 | 久久激情五月婷婷 | 精品在线免费观看 | 这里有精品在线视频 | 久久av中文字幕片 | 欧美精品久久久久久久久久丰满 | 久久精品精品电影网 | 亚洲国产无 | 久久综合视频网 | 国产男女无遮挡猛进猛出在线观看 | 亚洲成人家庭影院 | 在线观看av黄色 | 欧美性生活大片 | 久久久国产精品成人免费 | 久久美女视频 | 免费在线观看毛片网站 | 最新动作电影 | 久久久精品国产一区二区 | 人人干干人人 | 日本公妇在线观看高清 | 国产精品网站一区二区三区 | 国产最新精品视频 | 免费视频久久久久 | 麻豆传媒一区二区 | 99精品一区二区三区 | 青草视频在线免费 | 久久久久久久久久久高潮一区二区 | 国产91学生粉嫩喷水 | 黄色软件在线观看视频 | 成人在线视频免费看 | 国产精品99久久久精品免费观看 | 午夜国产在线观看 | 国产黄免费看 | 中文免费 | 日韩成人邪恶影片 | 欧美性生活久久 | 欧美一级性视频 | 美女视频是黄的免费观看 | 中文字幕日韩高清 | 久久久亚洲麻豆日韩精品一区三区 | 日韩在线观看视频中文字幕 | 日韩视频免费播放 | 一本色道久久综合亚洲二区三区 | 成人av资源 | 免费av网站在线看 | 日韩成人精品在线观看 | 成人全视频免费观看在线看 | 久久看片网站 | 日韩二区三区在线 | 国产亚洲日本 | 久久综合桃花 | 欧美成人黄色 | 黄视频色网站 | 美国av片在线观看 | 超碰人在线 | 国产理论免费 | 国产一区私人高清影院 | 亚洲精品资源 | 欧美成人亚洲成人 | 全黄网站| 久久狠狠干 | 五月婷婷综合色拍 | 欧美aaa一级| 日韩av高清 | 2023年中文无字幕文字 | 国产精品va在线观看入 | 国产精品99在线播放 | 91免费日韩 | 久久国产精品一区二区三区 | 一区二区三区高清在线观看 | 国产白浆视频 | 婷婷丁香视频 | 四虎影视成人永久免费观看视频 | 精品国产乱码久久久久久1区二区 | 伊人天天色 | 精品国产一区二区三区在线观看 | 青青视频一区 | av亚洲产国偷v产偷v自拍小说 | 在线免费观看黄色 | 久久草草影视免费网 | 国产日韩欧美在线观看视频 | 西西444www大胆高清视频 | 欧美精彩视频在线观看 | 香蕉视频亚洲 | 九色91在线视频 | 国产精品99久久久 | 成人动漫一区二区 | 在线观看成人av | 国产精品久久久久久久久久久杏吧 | 成av人电影| 久久久久久久精 | 在线有码中文 | 亚洲精品国产精品国自产 | 97av视频在线观看 | 人人草人人草 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 91在线永久| 日韩久久久久久久久 | 二区三区在线 | 亚洲精品大全 | 91自拍成人 | 又色又爽又黄 | 亚洲一区二区天堂 | 成年人视频在线免费 | 天天操夜夜操天天射 | 久久精品123 | 日韩特级片| 在线导航av| 久久综合网色—综合色88 | 免费男女羞羞的视频网站中文字幕 | 97超级碰碰碰视频在线观看 | www视频在线播放 | 国产无套视频 | 97视频免费看 | 久99久精品视频免费观看 | 欧美一级黄色网 | 日韩免费看 | 国产成人久久精品77777综合 | 国产精品精 | 久久久久久看片 | 国产精品久久久999 国产91九色视频 | 欧美日韩精品在线视频 | www.人人干 | 欧美日韩高清一区二区 国产亚洲免费看 | 日韩免费成人 | 色免费在线 | 91精品国产一区二区在线观看 | 久久精品香蕉 | 日本视频网 | 亚洲三级网 | 天天射天天干天天操 | 99久久精品国产欧美主题曲 | 伊人天天干 | a极黄色片 | 狠狠干五月天 | 99精品在线免费观看 | 国产色资源 | 麻豆91在线看 | 激情电影影院 | 精品欧美一区二区精品久久 | 亚洲日韩精品欧美一区二区 | 亚洲免费在线播放视频 | 丝袜制服天堂 | 综合天堂av久久久久久久 | 香蕉蜜桃视频 | 中文字幕久久亚洲 | 日韩av二区 | 久久久久欧美精品 | 中文在线字幕免费观看 | 97精品国产一二三产区 | 欧美亚洲成人免费 | 丰满少妇麻豆av | 日日日爽爽爽 | 人人添人人澡 | 亚洲春色综合另类校园电影 | 91在线看视频免费 | 亚洲精品视频在线观看网站 | 国产成人精品综合久久久 | 亚洲精品国偷拍自产在线观看 | 中文字幕国产亚洲 | 国产精品色婷婷 | 在线网址你懂得 | 69精品在线| 伊人久在线 | 久久综合给合久久狠狠色 | 国产精品久久久久久久久久久免费看 | 国产精品每日更新 | 在线观看韩国av | 婷婷在线色 | 国产一区网址 | 婷婷五月色综合 | 一区三区视频 | av成人在线网站 | 国产成人精品av | 国产高清在线免费 | 天天色天天射天天干 | 国产一区二区久久久 | 最近最新中文字幕 | 激情影音先锋 | 国产日韩三级 | 日韩国产精品毛片 | 日本精品久久久久久 | a视频免费在线观看 | 久草在线综合 | 成人免费观看大片 | 国产亚洲欧美在线视频 | 中文有码在线 | 国产日产欧美在线观看 | 丁香综合 | 日韩一区二区三区免费电影 | 97视频人人免费看 | 999久久| 婷婷成人综合 | 国产原创av在线 | 亚洲女人天堂成人av在线 | 蜜桃视频在线视频 | 高潮久久久 | 在线中文字幕电影 | 国产操在线 | 婷婷午夜天 | 一区二区三区在线免费播放 | 国产精品久久久久高潮 | 狠狠色网 | 免费亚洲黄色 | 免费aa大片 | 成人国产亚洲 | 精品国产综合区久久久久久 | 国产专区在线看 | 色网站在线观看 | 久一网站 | 婷婷激情欧美 | 国产美女在线精品免费观看 | 亚洲综合黄色 | 久久免费播放视频 | 人人澡人 | 亚洲最新精品 | 国产在线不卡 | 国产精品va在线观看入 | 久久久成人精品 | 中文字幕你懂的 | 久久免费视频在线观看 | www日日夜夜 | 五月综合在线观看 | 久久综合九色综合97婷婷女人 | 日本精品久久久久中文字幕5 | av在线播放一区二区三区 | 五月综合色婷婷 | 免费a现在观看 | 亚洲欧美成人综合 | 久久精品视频18 | 国产日产精品一区二区三区四区的观看方式 | 亚洲一区二区高潮无套美女 | 亚洲日本韩国一区二区 | 91在线免费播放 | 91麻豆国产 | 天天拍天天色 | 久久成人免费 | 久久成人精品视频 | www.夜夜骑.com| 国产精品久久久999 国产91九色视频 | 不卡电影免费在线播放一区 | 久久999精品| 久久久高清 | 国产黄色精品 | 欧美aa一级 | 五月婷婷综合在线观看 | 国产精品久久久久久久久久了 | 色婷婷激情 | 亚洲欧美日本国产 | 久久久久久久久久久久影院 | 99热精品在线观看 | 激情五月综合 | 欧美精品一区二区免费 | 欧美狠狠色 | 在线一区av | 91成人免费在线 | 国产成人黄色 | 成人免费精品 | 久久婷婷精品 | 免费观看一区二区三区视频 | 国产免费不卡 | 日韩在线观看免费 | av最新资源 | 一区中文字幕电影 | 国产精品大片 | 日韩中文字幕电影 | 黄色大全视频 | 婷婷爱五月天 | 香蕉影视在线观看 | 丁香五月亚洲综合在线 | 免费观看www视频 | 四虎影视欧美 | 久久精品中文视频 | 精品久久免费看 | 麻豆成人在线观看 | www.777奇米 | 色五月激情五月 | 亚洲国产中文在线观看 | 欧美成人91| 亚洲精品久久久久中文字幕m男 | 中文字幕一区二区在线观看 | 69av视频在线 | 97人人超| 国产亚洲精品久久久久久 | 成人超碰97| 天天天在线综合网 | 色七七亚洲影院 | 久草在线观 | 亚洲3级 | 99久久夜色精品国产亚洲 | 成人免费在线视频 | 成人久久久久久久久 | 一区二区高清在线 | 狠狠干夜夜操 | 91看片淫黄大片在线播放 | 成人性生活大片 | www黄色av | 91在线看网站 | 又色又爽又黄高潮的免费视频 | 日韩在线观看视频网站 | 精品一区二区三区四区在线 | 亚洲精品欧美专区 | 精品国产aⅴ一区二区三区 在线直播av | 国产精品18久久久久久久 | 99久久国产免费看 | 视频精品一区二区三区 | 国产视频综合在线 | 婷婷丁香狠狠爱 | 97超碰人人在线 | 丁香婷婷在线 | 九九九视频精品 | 黄色免费在线视频 | 色婷婷久久久综合中文字幕 | 97国产精品 | 久久99国产精品自在自在app | 国产视频一区精品 | 欧美一区二区三区四区夜夜大片 | 色偷偷人人澡久久超碰69 | 久久国产精品99久久久久 | 国产精品尤物视频 | 国产黄色理论片 | 九九99 | 亚洲最大免费成人网 | 狠狠干,狠狠操 | 天天鲁一鲁摸一摸爽一爽 | 亚洲 欧美 国产 va在线影院 | 在线一二区 | 91久久爱热色涩涩 | 亚洲精品国 | 久久99精品久久久久久清纯直播 | 91精品欧美| 亚洲欧美国产精品va在线观看 | 国产精品久久99综合免费观看尤物 | 欧美孕妇与黑人孕交 | 韩日成人av| 久久在线免费 | 玖玖视频精品 | 国产欧美日韩精品一区二区免费 | 一级性生活片 | 久久久久麻豆v国产 | 国产在线观看你懂得 | 久久久久久不卡 | 天天摸天天操天天爽 | 伊人激情网 | 狠狠激情中文字幕 | 最近日本字幕mv免费观看在线 | 在线观看黄色的网站 | 91精品综合在线观看 | 亚洲久久视频 | 午夜精品av | 天天弄天天干 | 免费三级网 | 国产一卡久久电影永久 | 麻豆国产精品va在线观看不卡 | 久热免费在线观看 | bbbbb女女女女女bbbbb国产 | av电影在线观看 | 日韩在线视频在线观看 | 久久精品欧美一区 | 欧美成人h版 | 99热在线免费观看 | 在线观看日韩免费视频 | www.福利 | 中文字幕日韩伦理 | 国产五码一区 | 国产精品欧美久久久久天天影视 | 天天操天天能 | 日韩视频一区二区在线 | 波多野结衣视频一区二区三区 | 最近中文字幕mv | 欧美一区二区三区免费观看 | 国产污视频在线观看 | 夜色在线资源 | 99热9| 天天五月天色 | 手机看国产毛片 | 久久综合导航 | 久久久久国产一区二区三区 | 欧美日韩久久久 | 亚洲成a人片在线www | 黄色av免费电影 | 五月婷婷久草 | 久久毛片高清国产 | 91精品在线免费观看视频 | 激情 婷婷 | 摸阴视频| 不卡电影一区二区三区 | av电影不卡在线 | 精品视频亚洲 | 日韩在线中文字幕 | 国产一区免费在线观看 | 婷婷深爱激情 | 国内精品久久久久久 | 91大神免费视频 | 一本一本久久aa综合精品 | 日本在线观看中文字幕无线观看 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 亚洲国产精品电影 | 亚洲人久久| 欧美日韩三区二区 | 日韩精品免费在线 | av中文字幕网站 | 一区二区三区免费在线观看视频 | 超碰97人人射妻 | 日韩精品一卡 | 国产91在线观看 | 久久久久网址 | 国产福利小视频在线 | 亚洲欧美成人 | mm1313亚洲精品国产 | 五月婷婷在线视频观看 | 手机在线黄色网址 | 国产精品婷婷午夜在线观看 | 一本—道久久a久久精品蜜桃 | 欧美日韩久久不卡 | 日韩中文字幕在线不卡 | 日韩欧美xxx | 久久小视频 | 国产韩国精品一区二区三区 | 欧美福利视频 | 91在线视频免费 | 欧美一区二区在线看 | 免费观看完整版无人区 | 成人国产精品久久久春色 | 国产不卡在线看 | 欧美日韩免费看 | 日韩成人精品一区二区 | 亚洲国产中文字幕 | 女人魂免费观看 | 午夜av不卡| 欧美视屏一区二区 | 欧美综合在线视频 | 日韩a在线 | 99精品视频在线播放免费 | 国内精品视频一区二区三区八戒 | 国产精品99久久久久久久久久久久 | 色婷婷免费 | 色婷婷狠狠干 | 奇米影视999 | 日韩黄色软件 | 日韩电影一区二区在线 | 中文字幕亚洲精品日韩 | 久久国产精品精品国产色婷婷 | 中文字幕一区二区三区乱码在线 | 2020天天干夜夜爽 | 夜夜躁日日躁狠狠久久av | 手机av在线不卡 | 成人免费中文字幕 | 亚洲精品在线观看中文字幕 | 人人玩人人添人人澡超碰 | 久久国产精品久久精品国产演员表 | 91成版人在线观看入口 | 日韩免 | 国产精品久久精品国产 | 欧美日韩高清一区二区三区 | 国产精品一区二区三区在线免费观看 | 久草在线免费新视频 | 天天爽天天碰狠狠添 | 成人影视免费看 | 日产乱码一二三区别在线 | 精精国产xxxx视频在线播放 | 91视频麻豆视频 | 欧美精品免费一区二区 | 久久久久久毛片 | 91精品办公室少妇高潮对白 | 亚洲高清视频一区二区三区 | 欧美性生活小视频 | 久久99在线 | 免费下载高清毛片 | 91色一区二区三区 | 亚洲最大免费成人网 | 天天操天| 91成人精品国产刺激国语对白 | 成人av网站在线观看 | 欧美一区三区四区 | 国产精品第二页 | 91九色网址| 激情综合网婷婷 | 国内视频在线观看 | 国产精品视频观看 | 国产精品免费视频一区二区 | 免费久久网站 | 色成人亚洲网 | 国产香蕉久久精品综合网 | 婷婷草 | 激情久久网 | 探花视频在线观看 | 色永久免费视频 | 99国产精品久久久久老师 | 国产成人久久久77777 | 一区二区三区在线观看免费视频 | 91人人爽久久涩噜噜噜 | 日本狠狠色| 欧美韩国在线 | 91精品国产91p65 | www国产亚洲精品久久麻豆 | 中文一区在线观看 | 九月婷婷色 | 99热这里| 91精品免费看 | 日韩欧美一级二级 | 久久激情视频免费观看 | 最新国产精品亚洲 | 国产不卡一区二区视频 | 亚洲欧美日韩精品久久久 | 久久综合免费视频 | 久久在线看 | 国产精品大片免费观看 | 精品国自产在线观看 | 99精品黄色片免费大全 | 91视频中文字幕 | 69av在线播放 | 亚洲va欧美va人人爽 | 四虎www| 81精品国产乱码久久久久久 | 免费国产一区二区视频 | 欧美精品亚洲二区 | 成人国产精品免费观看 | 久久永久视频 | 免费看黄在线 | 日韩欧美视频二区 | 国产在线美女 | 国产在线日本 | 91色吧| 成人免费观看视频大全 | 亚洲欧美视频在线 | 国产成人黄色片 | 久久久福利视频 | 最近日本韩国中文字幕 | 日日射av| 精品久久久久久亚洲综合网站 | 欧美精品视 | 国产黄色特级片 | 久久99亚洲热视 | 一区二区三区www | 精品国产黄色片 | 午夜精品视频福利 | 又大又硬又黄又爽视频在线观看 | 九热在线 | 国产精品粉嫩 | 在线观看av小说 | 中文字幕高清在线播放 | 免费观看一级特黄欧美大片 | 亚洲第一区在线播放 | 午夜影院三级 | 亚洲91在线| 九九亚洲视频 | 91精品免费在线 | 99久久精品午夜一区二区小说 | 91视频88av | 在线免费观看视频你懂的 | 精品久久影院 | 在线观看亚洲电影 | 黄色小说在线免费观看 | 国产精品久久久久久久久久妇女 | 国产做aⅴ在线视频播放 | 亚洲视频综合在线 | 国产成人a亚洲精品 | 片黄色毛片黄色毛片 | 亚洲精品在线视频观看 | 成人av网址大全 | 91pony九色丨交换 | av线上免费看 | 高清一区二区三区 | 黄色视屏av | 99国产精品一区二区 | 国产91免费在线 | 色综合久久88色综合天天人守婷 | 五月天久久婷 | 天天干天天天 | 最新日韩在线观看 | 国内精品久久久久久 | 国产91精品久久久久 | 少妇高潮流白浆在线观看 | 久久午夜影视 | 黄免费在线观看 | 国产小视频在线观看免费 | 色综合久久久久 | 最新色站 | 亚洲天堂精品视频 | 观看免费av | 日日久视频 | 亚州精品在线视频 | 国产日产精品一区二区三区四区的观看方式 | av在线亚洲天堂 | 开心激情婷婷 | 黄色字幕网 | 国产精品99久久久久人中文网介绍 | 日本久久不卡视频 | 三上悠亚一区二区在线观看 | 亚洲天堂在线观看完整版 | 日韩在线视频看看 | 欧美日韩国产一区二区在线观看 | 一区二区三区在线不卡 | 亚洲成人黄色在线 | 99久高清在线观看视频99精品热在线观看视频 | 999久久| 人人爽人人做 | 激情综合网五月激情 | 欧美精品乱码99久久影院 | 国产一二区在线观看 | 91视频88av | 日韩视频图片 | 视频一区二区在线 | 日韩欧美在线高清 | 91av在线视频播放 | av东方在线 | 美女视频永久黄网站免费观看国产 | 新版资源中文在线观看 | 韩国在线一区二区 | 青草视频免费观看 | 国产成人精品在线观看 | 97在线观看免费高清 | 国产精品久久久久一区二区国产 | 亚洲视频免费在线观看 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 日韩电影在线观看一区二区 | 在线播放一区 | 九九九视频精品 | 91影视成人 | 国产成人一级电影 | a在线一区 | 高清av在线免费观看 | 丁香资源影视免费观看 | 天天插天天狠天天透 | 国产日产精品一区二区三区四区的观看方式 | a在线观看国产 | 黄色亚洲免费 | 日韩在线一区二区免费 | 久久一区二区三区日韩 | 五月天伊人 | 很污的网站 | 99久久9| 久草a在线| 欧美999| 久久精品一区二区三 | 精品视频网站 | 免费高清无人区完整版 | 久久精品电影院 | 日日干激情五月 | 欧美精品第一 | 婷婷色六月天 | 91久久精品日日躁夜夜躁国产 | 91精品网站在线观看 | 六月丁香在线观看 | 国产自在线 | 91精品视屏 | 91女神的呻吟细腰翘臀美女 | 久久婷婷开心 | 色免费在线 | 精品国产乱码久久久久久久 | 免费av在线网站 | 久久亚洲二区 | 黄av在线| av片在线看 | 午夜精品久久久久久久99婷婷 | 色综合天天狠天天透天天伊人 | 中文字幕在线国产精品 | 亚洲精品中文字幕在线观看 | 国产成人无码AⅤ片在线观 日韩av不卡在线 | 久久在视频 | 97精品国产一二三产区 | 97av影院 | 精品在线你懂的 | 91aaa在线观看| 日韩在线观看视频一区二区三区 | 99热这里只有精品免费 | 在线欧美中文字幕 | 欧美性高跟鞋xxxxhd | 91精品国自产拍天天拍 | 天天摸天天弄 | 成人精品99 | 精品国产一区二区三区久久久久久 | 国产盗摄精品一区二区 | 国产成人高清av | 日韩在线电影 | 亚洲国产69 | av超碰免费在线 | 在线v片免费观看视频 | www.亚洲黄| 午夜视频二区 | 亚洲激情在线观看 | 久久久久国产精品一区二区 | av色综合| 欧美一区二区三区在线视频观看 | 日韩中文在线字幕 | 手机看片1042| 欧美福利在线播放 | 亚洲欧洲精品在线 | 国产精品久久久久久久99 | 久久精品99国产国产精 | 久久黄色免费视频 | 视频在线观看一区 | 国产精品视频免费看 | 丁香九月激情综合 | 99热精品国产一区二区在线观看 | 黄色国产大片 | 久久国产亚洲视频 | 久久无码av一区二区三区电影网 | 免费日韩在线 | 午夜12点 | 亚洲国产精品成人女人久久 | 天天爱天天操天天爽 | 久久国产色 |