Qt 之 Http概述、Post、Get认知,同步和异步概念

概述

关于Http

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP是一个属于应用层的面向对象的协议,HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

主要特点

  1. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  2. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  3. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  4. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
  5. 支持B/S及C/S模式。

HTTP之请求消息Request

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:
请求行(request line)、请求头部(header)、空行和请求数据四个部分组成

POST请求例子,使用Charles抓取的request:
1 POST / HTTP1.1
2 Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive
3 //空行
4 name=Professional%20Ajax&publisher=Wiley
第一部分:请求行,第一行明了是post请求,以及http1.1版本。
第二部分:请求头部,第二行至第六行。
第三部分:空行,第七行的空行。
第四部分:请求数据,第八行。

HTTP之响应消息Response

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
Respose举例:

1 HTTP/1.1 200 OK
2 Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8
3 //空行
4 下面的html


      
      
            
      
 

第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok)。
第二部分:消息报头,用来说明客户端要使用的一些附加信息。第二行和第三行为消息报头,
Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8
第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。空行后面的html部分为响应正文。

Qt中QUrlQuery

QUrlQuery 类提供了一种方法来操纵 URL 查询中的 key-value 对。
QUrlQuery 用来解析 URL 中的查询字符串,像下面这样:
在这里插入图片描述
上述的查询字符串在 URL 中 被用来传输选项,通常解码为多个 key-value 对。其列表包含了的两个条目,键为“type”和 “color”。QUrlQuery 也适用于从查询的各个组成部分创建一个查询字符串,为了 在 QUrl::setQuery() 中使用。

解析一个查询字符串最常见的方式是在构造函数中始化它,通过传递一个查询字符串。否则,可以使用 setQuery() 函数来设置要解析的查询。该函数也可用于解析具有非标准分割符的查询,在设置它们之后使用 setQueryDelimiters() 函数。

编码的查询字符串可以再次使用 query() 获得,这需要所有的内部存储项,并使用分隔符编码字符串。

简单使用eg:

// 基本 URL
QString baseUrl = "http://www.zhihu.com/search";
QUrl url(baseUrl);
// key-value 对
QUrlQuery query;
query.addQueryItem("type", "content");
query.addQueryItem("name", "Qt");
url.setQuery(query);

Qt中Get方法

均为异步方式;
eg1:

 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
  connect(manager, &QNetworkAccessManager::finished,this, &MyClass::replyFinished);
  manager->get(QNetworkRequest(QUrl("http://qt-project.org")));

eg2:

QNetworkRequest request;
  request.setUrl(QUrl("http://qt-project.org"));
  request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

  QNetworkReply *reply = manager->get(request);
  connect(reply, &QIODevice::readyRead, this, &MyClass::slotReadyRead);
  connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),this, &MyClass::slotError);
  connect(reply, &QNetworkReply::sslErrors,this, &MyClass::slotSslErrors);

**同步方式举例:**函数实现是否能上外网

bool NetLiveWorker::IsWebOk()
{
    return IsHostOnline("https://www.baidu.com/", 2000);
}

bool NetLiveWorker::IsHostOnline(QString strHostName, int nTimeoutmSeconds)
{
    QNetworkRequest request(strHostName);
    request.setRawHeader("Content-Type", "charset='utf-8'");
    request.setRawHeader("Content-Type", "application/json");
    //QNetworkAccessManager* naManager = new QNetworkAccessManager;
    QNetworkAccessManager manager;
    QEventLoop eventloop;

    QTimer timer;
    timer.singleShot(nTimeoutmSeconds, &eventloop, SLOT(quit()));
    timer.start();

    QNetworkReply* reply = manager.get(request);
    QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
    Q_ASSERT(conRet);

    eventloop.exec(QEventLoop::ExcludeUserInputEvents); //开启事件循环

    if (!timer.isActive())
    {
        //超时,未知状态
        QObject::disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
        reply->abort();
        reply->deleteLater();

        return false;
    }

    if (reply->error() != QNetworkReply::NoError)
    {
        //qDebug() << "error = " <error();
        reply->abort();
        reply->deleteLater();
        return false;
    }

    bool bRes = reply->readAll().length() > 0;
    reply->abort();
    reply->deleteLater();

    return bRes;
}

Qt中Post方法

post方法提供了二个重载方法,每个方法针对有特定的场景

QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart)

eg1: 异步方式,同步方式同上

	QUrl url(m_strApiHead + "iot-server/api/pen/device/login");
    QUrlQuery postData;
    postData.addQueryItem("wifi_mac", pconf->getWifiMac());
    postData.addQueryItem("sn", pconf->getSN());
    postData.addQueryItem("sig",checksum);

    QNetworkAccessManager netManager;
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

    QNetworkReply *pNetworkResponse = netManager.post(request,postData.toString(QUrl::FullyEncoded).toUtf8());
    QObject::connect(pNetworkResponse, &QNetworkReply::finished, [=]{
        QByteArray res =  pNetworkResponse->readAll();
        qDebug() <<"sendApiLogin replay " << res;
        QJsonObject json_object = QJsonDocument::fromJson(res).object();
    });

eg2:QHttpMultiPart 发送文件


QUrl url( m_strApiHead + "iot-server/api/pen/device/picsearch");
QImage  img(sfPath);

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart textPart;
//stamp
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"stamp\""));
textPart.setBody(stamp.toLocal8Bit());
multiPart->append(textPart);

//width
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image_width\""));
textPart.setBody(QString("%1").arg(width).toLocal8Bit());
multiPart->append(textPart);

//height
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image_height\""));
textPart.setBody(QString("%1").arg(height).toLocal8Bit());
multiPart->append(textPart);

//
QFile *file = new QFile(sfPath);
if (file->open(QIODevice::ReadOnly))
{
    qDebug() << "sendApiSearchQuesResults open file " << sfPath << "success" ;
    QHttpPart filePart;
    filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
                       QString("form-data; name=\"file\"; filename=\"%1\"").arg(filename));
    filePart.setBodyDevice(file);
    file->setParent(multiPart); // file deleted on the destruction of multiPart

    multiPart->append(filePart);
}
else {
    qDebug() << sfPath << "ultiPart->append(filePart)  open failed";
}

multiPart->setBoundary("---wmm---");       //设置分隔符
QNetworkAccessManager netManager;
QNetworkRequest request(url);
QNetworkReply *pNetworkResponse = netManager.post(request,multiPart);
multiPart->setParent(pNetworkResponse);

QObject::connect(pNetworkResponse, &QNetworkReply::finished, [=]{
    file->close();
    QByteArray res =  pNetworkResponse->readAll();
    QTextCodec *tc = QTextCodec::codecForName("UTF8");
    QString str = tc->toUnicode(res);
    //qDebug() <<"sendApiSearchQuesResults replay " << str;
    QJsonObject json_object = QJsonDocument::fromJson(res).object();
    pNetworkResponse->deleteLater();
});

总结

qt中http提供了很多方便的类来实现post、get方法
其中主要有
1 QNetworkAccessManager
2 QUrlQuery
3 QNetworkRequest
4 QNetworkReply
后续会提供这些类的详细用法,和应用场景

QNetworkAccessManager用法: https://editor.csdn.net/md/?articleId=114499807
QUrlQuery 详解:link.
Qt之 Post方法上传图片到服务器两种方式Base64流和File :https://editor.csdn.net/md/?articleId=114794119.
Qt之 四种常见的 POST 提交数据方式: https://editor.csdn.net/md/?articleId=114795085.

你可能感兴趣的:(《Qt,项目实战经历全记录》,qt,http,post,get)