ℰ悟透Qt—Http网络编程

news2024/11/26 0:25:21

目录

    • 概述
    • 实践理论
    • QNetworkAccessManager进行Http编程的基本步骤
    • 代码实战(重点片段)

概述

网络访问 API 建立在一个 QNetworkAccessManager 对象之上,该对象保存了发送请求所需的公共配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号和可用于监视网络操作进度的回复信号。整个 Qt 应用程序只需要一个 QNetworkAccessManager 实例。由于 QNetworkAccessManager 基于 QObject,因此只能从它所属的线程中使用。

创建了 QNetworkAccessManager 对象后,应用程序可以使用它来通过网络发送请求。提供了一组标准函数,这些函数接受请求和可选数据,并返回一个 QNetworkReply 对象。返回的对象用于获取响应对应请求返回的任何数据。

一旦你创建了QNetworkAccessManager对象,就可以使用它提供的一组标准方法来发送网络请求,而每个方法都会返回一个QNetworkReply对象,这个对象代表了与你发送的请求相应的反馈,通过它,你既可以读取网络应答的元数据,又可以读取实体数据。QNetworkAccessManager的get()post()head()put()deleteResourse()sendCustomRequest()等方法可用于发送网络请求,httpftphttps都是小菜一碟,只要你构造一个QNetworkRequest对象传递给它们就行了。

如果你仔细研究QNetworkAccessManager的接口及其枚举类型Operation,还有QNetworkRequest的接口及其KnownHeadersAttribute等枚举类型,可能会发现,其实,它好像就是专门为http编程设计的,毕竟,http协议在网络世界里广泛使用。不过喜欢ftp的朋友也别灰心,QNetworkAccessManager是万能的,它也支持下载、上传等常见的ftp操作。

实践理论

我们使用QNetworkAccessManager进行网络编程,与4个类打交道比较多一些,这里先介绍一下,为后面的代码解释做做准备。

  1. QNetworkAccessManager是中心,它提供了发送请求的各种API以及网络配置相关的方法。
  2. QNetworkRequest代表一个网络请求,包括URL、HTTP访问用的UserAgent、用户名、密码等都通过它设置。
  3. QNetworkReply是QNetworkAccessManager根据你提交的QNetworkRequest构造的一个应答对象,一旦请求发出,你就可以通过它获知网络操作过程中的各种状态。
  4. QUrl是构造请求(QNetworkRequest)时常用的类,它可以编解码URL,提取URL中的主机、路径、文件、用户名、密码等信息。

QNetworkAccessManager进行Http编程的基本步骤

  1. 根据URL生成QUrl对象,然后根据QUrl创建一个网络请求QNetworkRequest,必要时可设置一些header。
  2. 调用QNetworkAccessManager的get()、post()等方法中的一个。
  3. 使用QNetworkAccessManager返回的QNetworkReply实例来跟踪应答的各种状态反馈。

如果要上传数据到服务器,则需要提供一个QIODevice指针或者QByteArraypost()方法。通常情况下你可以使用QFile打开指定的文件然后传给post()方法来上传本地文件。而后者则允许你直接将裸数据上传到服务器。

需要特别说明的是:

  1. 开发者必须自己选择合适的时机释放QNetworkAccessManager返回的QNetworkReply对象。
  2. 请不要在响应QNetworkReply对象的信号时直接删除QNetworkReply对象,应该使用其deleteLater()方法延迟销毁它。
  3. 对于重定向消息,可以通过QNetworkReply::header(QNetworkRequest::LocationHeader)获取定向到的新地址,然后针对新地址重走长征路。

代码实战(重点片段)

首先要在qmake或cmake构建中添加相关配置:

Header: #include <QNetworkAccessManager>
CMake: find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(mytarget PRIVATE Qt6::Network)
qmake: QT += network

QNetworkAccessManager m_nam;
QNetworkReply * m_reply;
QNetworkRequest * m_request;
QString m_strFilePath;
QFile * m_file;
*************************************************
Widget::Widget(QWidget* parent): QWidget(parent), m_nam(this), m_reply(0), m_request(0), m_file(0) {
    setupGUI();
}

Widget::~Widget() {
    if (m_file) {
        m_file->close();
        delete m_file;
    }
    if (m_reply) {
        m_reply->disconnect(this);
        m_reply->abort();
        delete m_reply;
    }
}

static const char* g_ext_to_content_type[] = {
      ".001", "application/x-001"
    , ".301", "application/x-301"
    , ".323", "text/h323"
    , ".906", "application/x-906"
    , ".907", "drawing/907"
    , ".a11", "application/x-a11"
    , ".acp", "audio/x-mei-aac"
    , ".ai", "application/postscript"
    , ".aif", "audio/aiff"
    , ".aifc", "audio/aiff"
    , ".aiff", "audio/aiff"
    , ".anv", "application/x-anv"
    , ".asa", "text/asa"
    , ".asf", "video/x-ms-asf"
    , ".asp", "text/asp"
    , ".asx", "video/x-ms-asf"
    , ".au", "audio/basic"
    , ".avi", "video/avi"
    , ".awf", "application/vnd.adobe.workflow"
    , ".biz", "text/xml"
    , ".bmp", "application/x-bmp"
    , ".bot", "application/x-bot"
    , ".c4t", "application/x-c4t"
    , ".c90", "application/x-c90"
    , ".cal", "application/x-cals"
    , ".cat", "application/vnd.ms-pki.seccat"
    , ".cdf", "application/x-netcdf"
    , ".cdr", "application/x-cdr"
    , ".cel", "application/x-cel"
    , ".cer", "application/x-x509-ca-cert"
    , ".cg4", "application/x-g4"
    , ".cgm", "application/x-cgm"
    , ".cit", "application/x-cit"
    , ".class", "java/*"
    , ".cml", "text/xml"
    , ".cmp", "application/x-cmp"
    , ".cmx", "application/x-cmx"
    , ".cot", "application/x-cot"
    , ".conf", "text/plain"
    , ".crl", "application/pkix-crl"
    , ".crt", "application/x-x509-ca-cert"
    , ".csi", "application/x-csi"
    , ".css", "text/css"
    , ".cut", "application/x-cut"
    , ".dbf", "application/x-dbf"
    , ".dbm", "application/x-dbm"
    , ".dbx", "application/x-dbx"
    , ".dcd", "text/xml"
    , ".dcx", "application/x-dcx"
    , ".der", "application/x-x509-ca-cert"
    , ".dgn", "application/x-dgn"
    , ".dib", "application/x-dib"
    , ".dll", "application/x-msdownload"
    , ".doc", "application/msword"
    , ".dot", "application/msword"
    , ".drw", "application/x-drw"
    , ".dtd", "text/xml"
    , ".dwf", "Model/vnd.dwf"
    , ".dwf", "application/x-dwf"
    , ".dwg", "application/x-dwg"
    , ".dxb", "application/x-dxb"
    , ".dxf", "application/x-dxf"
    , ".edn", "application/vnd.adobe.edn"
    , ".emf", "application/x-emf"
    , ".eml", "message/rfc822"
    , ".ent", "text/xml"
    , ".epi", "application/x-epi"
    , ".eps", "application/x-ps"
    , ".eps", "application/postscript"
    , ".etd", "application/x-ebx"
    , ".exe", "application/x-msdownload"
    , ".fax", "image/fax"
    , ".fdf", "application/vnd.fdf"
    , ".fif", "application/fractals"
    , ".fo", "text/xml"
    , ".frm", "application/x-frm"
    , ".g4", "application/x-g4"
    , ".gbr", "application/x-gbr"
    , ".gcd", "application/x-gcd"
    , ".gif", "image/gif"
    , ".gl2", "application/x-gl2"
    , ".gp4", "application/x-gp4"
    , ".hgl", "application/x-hgl"
    , ".hmr", "application/x-hmr"
    , ".hpg", "application/x-hpgl"
    , ".hpl", "application/x-hpl"
    , ".hqx", "application/mac-binhex40"
    , ".hrf", "application/x-hrf"
    , ".hta", "application/hta"
    , ".htc", "text/x-component"
    , ".htm", "text/html"
    , ".html", "text/html"
    , ".htt", "text/webviewhtml"
    , ".htx", "text/html"
    , ".icb", "application/x-icb"
    , ".ico", "image/x-icon"
    , ".ico", "application/x-ico"
    , ".iff", "application/x-iff"
    , ".ig4", "application/x-g4"
    , ".igs", "application/x-igs"
    , ".iii", "application/x-iphone"
    , ".img", "application/x-img"
    , ".ini", "text/plain"
    , ".ins", "application/x-internet-signup"
    , ".isp", "application/x-internet-signup"
    , ".IVF", "video/x-ivf"
    , ".java", "java/*"
    , ".jfif", "image/jpeg"
    , ".jpe", "image/jpeg"
    , ".jpe", "application/x-jpe"
    , ".jpeg", "image/jpeg"
    , ".jpg", "image/jpeg"
    , ".jpg", "application/x-jpg"
    , ".js", "application/x-javascript"
    , ".jsp", "text/html"
    , ".la1", "audio/x-liquid-file"
    , ".lar", "application/x-laplayer-reg"
    , ".latex", "application/x-latex"
    , ".lavs", "audio/x-liquid-secure"
    , ".lbm", "application/x-lbm"
    , ".lmsff", "audio/x-la-lms"
    , ".log", "text/plain"
    , ".ls", "application/x-javascript"
    , ".ltr", "application/x-ltr"
    , ".m1v", "video/x-mpeg"
    , ".m2v", "video/x-mpeg"
    , ".m3u", "audio/mpegurl"
    , ".m4e", "video/mpeg4"
    , ".mac", "application/x-mac"
    , ".man", "application/x-troff-man"
    , ".math", "text/xml"
    , ".mdb", "application/msaccess"
    , ".mdb", "application/x-mdb"
    , ".mfp", "application/x-shockwave-flash"
    , ".mht", "message/rfc822"
    , ".mhtml", "message/rfc822"
    , ".mi", "application/x-mi"
    , ".mid", "audio/mid"
    , ".midi", "audio/mid"
    , ".mil", "application/x-mil"
    , ".mml", "text/xml"
    , ".mnd", "audio/x-musicnet-download"
    , ".mns", "audio/x-musicnet-stream"
    , ".mocha", "application/x-javascript"
    , ".movie", "video/x-sgi-movie"
    , ".mp1", "audio/mp1"
    , ".mp2", "audio/mp2"
    , ".mp2v", "video/mpeg"
    , ".mp3", "audio/mp3"
    , ".mp4", "video/mpeg4"
    , ".mpa", "video/x-mpg"
    , ".mpd", "application/vnd.ms-project"
    , ".mpe", "video/x-mpeg"
    , ".mpeg", "video/mpg"
    , ".mpg", "video/mpg"
    , ".mpga", "audio/rn-mpeg"
    , ".mpp", "application/vnd.ms-project"
    , ".mps", "video/x-mpeg"
    , ".mpt", "application/vnd.ms-project"
    , ".mpv", "video/mpg"
    , ".mpv2", "video/mpeg"
    , ".mpw", "application/vnd.ms-project"
    , ".mpx", "application/vnd.ms-project"
    , ".mtx", "text/xml"
    , ".mxp", "application/x-mmxp"
    , ".net", "image/pnetvue"
    , ".nrf", "application/x-nrf"
    , ".nws", "message/rfc822"
    , ".odc", "text/x-ms-odc"
    , ".out", "application/x-out"
    , ".p10", "application/pkcs10"
    , ".p12", "application/x-pkcs12"
    , ".p7b", "application/x-pkcs7-certificates"
    , ".p7c", "application/pkcs7-mime"
    , ".p7m", "application/pkcs7-mime"
    , ".p7r", "application/x-pkcs7-certreqresp"
    , ".p7s", "application/pkcs7-signature"
    , ".pc5", "application/x-pc5"
    , ".pci", "application/x-pci"
    , ".pcl", "application/x-pcl"
    , ".pcx", "application/x-pcx"
    , ".pdf", "application/pdf"
    , ".pdf", "application/pdf"
    , ".pdx", "application/vnd.adobe.pdx"
    , ".pfx", "application/x-pkcs12"
    , ".pgl", "application/x-pgl"
    , ".pic", "application/x-pic"
    , ".pko", "application/vnd.ms-pki.pko"
    , ".pl", "application/x-perl"
    , ".plg", "text/html"
    , ".pls", "audio/scpls"
    , ".plt", "application/x-plt"
    , ".png", "image/png"
    , ".png", "application/x-png"
    , ".pot", "application/vnd.ms-powerpoint"
    , ".ppa", "application/vnd.ms-powerpoint"
    , ".ppm", "application/x-ppm"
    , ".pps", "application/vnd.ms-powerpoint"
    , ".ppt", "application/vnd.ms-powerpoint"
    , ".ppt", "application/x-ppt"
    , ".pr", "application/x-pr"
    , ".prf", "application/pics-rules"
    , ".prn", "application/x-prn"
    , ".prt", "application/x-prt"
    , ".ps", "application/x-ps"
    , ".ps", "application/postscript"
    , ".ptn", "application/x-ptn"
    , ".pwz", "application/vnd.ms-powerpoint"
    , ".r3t", "text/vnd.rn-realtext3d"
    , ".ra", "audio/vnd.rn-realaudio"
    , ".ram", "audio/x-pn-realaudio"
    , ".ras", "application/x-ras"
    , ".rat", "application/rat-file"
    , ".rdf", "text/xml"
    , ".rec", "application/vnd.rn-recording"
    , ".red", "application/x-red"
    , ".rgb", "application/x-rgb"
    , ".rjs", "application/vnd.rn-realsystem-rjs"
    , ".rjt", "application/vnd.rn-realsystem-rjt"
    , ".rlc", "application/x-rlc"
    , ".rle", "application/x-rle"
    , ".rm", "application/vnd.rn-realmedia"
    , ".rmf", "application/vnd.adobe.rmf"
    , ".rmi", "audio/mid"
    , ".rmj", "application/vnd.rn-realsystem-rmj"
    , ".rmm", "audio/x-pn-realaudio"
    , ".rmp", "application/vnd.rn-rn_music_package"
    , ".rms", "application/vnd.rn-realmedia-secure"
    , ".rmvb", "application/vnd.rn-realmedia-vbr"
    , ".rmx", "application/vnd.rn-realsystem-rmx"
    , ".rnx", "application/vnd.rn-realplayer"
    , ".rp", "image/vnd.rn-realpix"
    , ".rpm", "audio/x-pn-realaudio-plugin"
    , ".rsml", "application/vnd.rn-rsml"
    , ".rt", "text/vnd.rn-realtext"
    , ".rtf", "application/msword"
    , ".rtf", "application/x-rtf"
    , ".rv", "video/vnd.rn-realvideo"
    , ".sam", "application/x-sam"
    , ".sat", "application/x-sat"
    , ".sdp", "application/sdp"
    , ".sdw", "application/x-sdw"
    , ".sit", "application/x-stuffit"
    , ".slb", "application/x-slb"
    , ".sld", "application/x-sld"
    , ".slk", "drawing/x-slk"
    , ".smi", "application/smil"
    , ".smil", "application/smil"
    , ".smk", "application/x-smk"
    , ".snd", "audio/basic"
    , ".sol", "text/plain"
    , ".sor", "text/plain"
    , ".spc", "application/x-pkcs7-certificates"
    , ".spl", "application/futuresplash"
    , ".spp", "text/xml"
    , ".ssm", "application/streamingmedia"
    , ".sst", "application/vnd.ms-pki.certstore"
    , ".stl", "application/vnd.ms-pki.stl"
    , ".stm", "text/html"
    , ".sty", "application/x-sty"
    , ".svg", "text/xml"
    , ".swf", "application/x-shockwave-flash"
    , ".tdf", "application/x-tdf"
    , ".tg4", "application/x-tg4"
    , ".tga", "application/x-tga"
    , ".tif", "image/tiff"
    , ".tif", "application/x-tif"
    , ".tiff", "image/tiff"
    , ".tld", "text/xml"
    , ".top", "drawing/x-top"
    , ".torrent", "application/x-bittorrent"
    , ".tsd", "text/xml"
    , ".txt", "text/plain"
    , ".uin", "application/x-icq"
    , ".uls", "text/iuls"
    , ".vcf", "text/x-vcard"
    , ".vda", "application/x-vda"
    , ".vdx", "application/vnd.visio"
    , ".vml", "text/xml"
    , ".vpg", "application/x-vpeg005"
    , ".vsd", "application/vnd.visio"
    , ".vsd", "application/x-vsd"
    , ".vss", "application/vnd.visio"
    , ".vst", "application/vnd.visio"
    , ".vst", "application/x-vst"
    , ".vsw", "application/vnd.visio"
    , ".vsx", "application/vnd.visio"
    , ".vtx", "application/vnd.visio"
    , ".vxml", "text/xml"
    , ".wav", "audio/wav"
    , ".wax", "audio/x-ms-wax"
    , ".wb1", "application/x-wb1"
    , ".wb2", "application/x-wb2"
    , ".wb3", "application/x-wb3"
    , ".wbmp", "image/vnd.wap.wbmp"
    , ".wiz", "application/msword"
    , ".wk3", "application/x-wk3"
    , ".wk4", "application/x-wk4"
    , ".wkq", "application/x-wkq"
    , ".wks", "application/x-wks"
    , ".wm", "video/x-ms-wm"
    , ".wma", "audio/x-ms-wma"
    , ".wmd", "application/x-ms-wmd"
    , ".wmf", "application/x-wmf"
    , ".wml", "text/vnd.wap.wml"
    , ".wmv", "video/x-ms-wmv"
    , ".wmx", "video/x-ms-wmx"
    , ".wmz", "application/x-ms-wmz"
    , ".wp6", "application/x-wp6"
    , ".wpd", "application/x-wpd"
    , ".wpg", "application/x-wpg"
    , ".wpl", "application/vnd.ms-wpl"
    , ".wq1", "application/x-wq1"
    , ".wr1", "application/x-wr1"
    , ".wri", "application/x-wri"
    , ".wrk", "application/x-wrk"
    , ".ws", "application/x-ws"
    , ".ws2", "application/x-ws"
    , ".wsc", "text/scriptlet"
    , ".wsdl", "text/xml"
    , ".wvx", "video/x-ms-wvx"
    , ".xdp", "application/vnd.adobe.xdp"
    , ".xdr", "text/xml"
    , ".xfd", "application/vnd.adobe.xfd"
    , ".xfdf", "application/vnd.adobe.xfdf"
    , ".xhtml", "text/html"
    , ".xls", "application/vnd.ms-excel"
    , ".xls", "application/x-xls"
    , ".xlw", "application/x-xlw"
    , ".xml", "text/xml"
    , ".xpl", "audio/scpls"
    , ".xq", "text/xml"
    , ".xql", "text/xml"
    , ".xquery", "text/xml"
    , ".xsd", "text/xml"
    , ".xsl", "text/xml"
    , ".xslt", "text/xml"
    , ".xwd", "application/x-xwd"
    , ".x_b", "application/x-x_b"
    , ".x_t", "application/x-x_t"
};

static const char* get_content_type(char* filename) {
    int i = 0;
    int size = sizeof(g_ext_to_content_type) / sizeof(g_ext_to_content_type[0]);
    char* ext = filename;
    char* slash = strrchr(filename, '/');
    if (slash)
        slash++;
    else
        slash = filename;
    ext = strrchr(filename, '.');
    if (!ext)
        return "application/octet-stream";
    for (; i < size; i += 2) {
        if (!strcmp(ext, g_ext_to_content_type[i]))
            return g_ext_to_content_type[i + 1];
    }
    return "application/octet-stream";
}

QString Widget::requestString(QNetworkRequest* req) {
    QString joinedHeader;
    QVariant variant = req->header(QNetworkRequest::ContentTypeHeader);
    if (variant.isValid()) {
        joinedHeader += "content-type: ";
        joinedHeader += variant.toString();
        joinedHeader += "\n";
    }
    variant = req->header(QNetworkRequest::UserAgentHeader);
    if (variant.isValid()) {
        joinedHeader += "user-agent: ";
        joinedHeader += variant.toString();
        joinedHeader += "\n";
    }
    QList<QByteArray> headerList = req->rawHeaderList();
    int count = headerList.size();
    QByteArray value;
    for (int i = 0; i < count; i++) {
        const QByteArray& tag = headerList.at(i);
        value = req->rawHeader(tag);
        joinedHeader += QString("%1: %2\n").arg(tag.data()).arg(value.data());
    }
    return joinedHeader;
}

QString Widget::responseString(QNetworkReply* reply) {
    QString joinedHeader;
    QVariant variant = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    if (variant.isValid())
        joinedHeader = QString("STATUS: %1\n").arg(variant.toInt());
    QList<QByteArray> headerList = reply->rawHeaderList();
    int count = headerList.size();
    QByteArray value;
    for (int i = 0; i < count; i++) {
        const QByteArray& tag = headerList.at(i);
        value = reply->rawHeader(tag);
        joinedHeader += QString("%1: %2\n").arg(tag.data()).arg(value.data());
    }
    return joinedHeader;
}

void Widget::onAddHttpHeaderValue() {
    if (!m_request)
        m_request = new QNetworkRequest();
    QString header = m_headerKeyEdit->text();
    if (header.compare("user-agent", Qt::CaseInsensitive) == 0)
        m_request->setHeader(QNetworkRequest::UserAgentHeader, m_headerValueEdit->text());
    m_request->setRawHeader(header, m_headerValueEdit->text());
    m_stateText->setText(requestString(m_request));
}

void Widget::onStart() {
    if (!m_request)
        m_request = new QNetworkRequest();
    QString strURL = m_urlEdit->text();
    QUrl qurl(strURL);
    QString httpMethod = m_methodCombo->currentText();
    m_request->setUrl(qurl);
    if (httpMethod == "GET") {
        QString errorString;
        if (!openFile(true, errorString)) {
            QMessageBox::warning(this, "Save File Error", errorString, QMessageBox::Ok);
            resetState();
            return;
        }
        m_reply = m_nam.get(*m_request);
    } else if (httpMethod == "POST") {
        QString errorString;
        if (!openFile(false, errorString)) {
            QMessageBox::warning(this, "Upload File Error", errorString, QMessageBox::Ok);
            resetState();
            return;
        }
        m_reply = m_nam.post(*m_request, m_file);
        m_request->setHeader(QNetworkRequest::ContentTypeHeader, get_content_type(m_strFilePath.data()));
    } else if (httpMethod == "HEAD")
        m_reply = m_nam.head(*m_request);
      else if (httpMethod == "DELETE")
        m_reply = m_nam.deleteResource(*m_request);
      else {
        QMessageBox::information(this, "information", "Please select a Method!", QMessageBox::Ok);
        resetState();
        return;
    }
    connect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
    connect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
            this, SLOT(onDownloadProgress(qint64, qint64)));
    connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)),
            this, SLOT(onUploadProgress(qint64, qint64)));
    connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(onMetaDataChanged()));
    connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onFinished()));
    m_stateText->setText(requestString(m_request));
    m_stateText->append("\n===========response==========\n");
}

void Widget::onMetaDataChanged() {
    m_stateText->setText(responseString(m_reply));
}

void Widget::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
    if (bytesTotal > 0) {
        int value = (bytesReceived * 100) / bytesTotal;
        m_progress->setValue(value);
    }
}

void Widget::onUploadProgress(qint64 bytesSent, qint64 bytesTotal) {
    if (bytesTotal > 0) {
        int value = (bytesSent * 100) / bytesTotal;
        m_progress->setValue(value);
    }
}

void Widget::onReadyRead() {
    QByteArray data = m_reply->readAll();
    m_stateText->append(QString("read %1 bytes\n").arg(data.length()));
    if (m_file) {
        m_file->write(data);
    }
}

void Widget::onFinished() {
    QString strState = m_reply->error() == QNetworkReply::NoError ? "success" : m_reply->errorString();
    m_stateText->append(QString("\nhttp request done - %1\n").arg(strState));
    m_stateText->append("\n-------\n");
    resetState();
}

void Widget::resetState() {
    if (m_file) {
        m_file->close();
        delete m_file;
        m_file = 0;
    }
    if (m_request) {
        delete m_request;
        m_request = 0;
    }
    if (m_reply) {
        m_reply->disconnect(this);
        m_reply->deleteLater();
        m_reply = 0;
    }
}

函数——strrchr()

const char* strrchr(const char* str, int character);
             char* strrchr(char* str, int character);
Locate last occurrence of character in string.
Returns a pointer to the last occurrence of character in the C string str.
The terminating null-character is considered part of the C string. Therefore, it can also be located to retrieve a pointer to the end of a string.
Parameters
str - C string.
character - Character to be located. It is passed as its int promotion, but it is internally converted back to char.
Return Value
A pointer to the last occurrence of character in str.
If the character is not found, the function returns a null pointer.

完整代码运行后,如下图所示:

➼ ➼ ➼ 【完整代码】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/702527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络】数据链路层之随机接入-CSMA/CD协议(总线局域网)

1.概念 2.信号碰撞&#xff08;冲突&#xff09; 3.解决方案 CSMA/CD 4.争用期&#xff08;端到端往返时延&#xff09; 5.最小帧长 6.最大帧长 7.指数退避算法 8.信道利用率 9.帧发送流程 10.帧接受流程 12.题目1 13.题目2 14.题目3 15 小结

无线蓝耳机哪些牌子好?十大真无线耳机排名品牌

随着TWS技术在应用层面的日益完善&#xff0c;真无线蓝牙耳机就越来越受欢迎了&#xff0c;完全摒弃了线材的束缚&#xff0c;做到了真正的无线耳机&#xff0c;这简直是无法忍受耳机线的强迫症的福音&#xff0c;而且现在不仅是佩戴时会格外的舒适&#xff0c;随着无线技术的不…

Redhawk: 为什么STA CHECK中switch cell覆盖率为0?

如题&#xff0c;做dynamic analysis时&#xff0c;不需要switch cell的timing window信息&#xff0c;但是做low power analysis需要做上电分析时switch cell的timing window就是必须的了&#xff0c;本文对switch cell control pin没有timing window (STA CHECK覆盖率为零)的…

ChatGPT:给教育创新带来风险与挑战

在教育界&#xff0c;当前对ChatGPT的关注固然有一部分原因是它所能带来的教育创新&#xff0c;但更多的原因是ChatGPT同时也在冲击着教师的角色定位&#xff0c;推动着人才培养目标的转型&#xff0c;逼迫着学生和教师走出舒适区。况且&#xff0c;ChatGPT还不断诱发教育中的技…

【算法之贪心算法IV】leetcode56. 合并区间

452. 用最少数量的箭引爆气球 力扣题目链接 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴…

cisp 记录(第一,二章节)

第一章信息安全保障 1.信息安全保障基础 1.1信息安全的概念 1.2信息安全属性 1.3信息安全视角 1.4信息安全发展阶段 1.5信息安全保障新领域 2安全保障体系框架 2.1基于时间的PDR和PPDR模型 2.2信息安全保障技术框架 2.3信息系统安全保障评估框架 2.4企业安全架构 1.1信息安全…

mysql基础4——单机多实例部署

文章目录 一、手动部署二、脚本部署 前提了解&#xff1a; 适用于服务器不够&#xff0c;但需要把业务区分开的情况下&#xff0c;可以部署此方式。比如多个业务平台使用的数据库不同&#xff0c;不能只创建一个数据库&#xff0c;这时候就需要部署多实例。实际情况中能避免此方…

BOSHIDA 检测DC电源模块的稳定性能的几个步骤

BOSHIDA 检测DC电源模块的稳定性能的几个步骤 检测DC电源模块的稳定性能需要进行以下几个步骤&#xff1a; 1. 确认测试设备&#xff1a;需要准备一台多功能电源和一台数字万用表&#xff0c;可以根据需要选用不同型号和规格的测试设备。 2.设置电源参数&#xff1a;根据DC电…

DEM获取地形和计算点是否在矩形面积内的算法

1准备一个DEM地形&#xff0c;打开GlobalMap,如下选择 选择否&#xff0c;生成边界 将边界导出为shp文件&#xff0c;打开QGIS&#xff0c;安装get wkt插件&#xff0c;选择对应坐标系&#xff0c;获取wkt,如图 , 附判断点是否在此坐标范围内代码 /// <summary>/// 判断点…

echarts 柱形图 Y轴数据多,鼠标滚动显示数据,不缩放

坐标轴数据太多&#xff0c;只能滚动显示&#xff0c;滚动的时候&#xff0c;不想缩放&#xff0c;单纯平移就好。 滚动后第二屏的截图 没滚动的&#xff0c;第一屏的截图 option {title: {// text: World Population,},tooltip: {trigger: axis,axisPointer: {type: shadow,}…

15 年开源路,从大厂搬砖到创业挖坑

上个月回蚂蚁做了一场有关开源的分享&#xff0c;让我讲讲离开公司自己做开源创业后的感想。 正好借着端午节的时间&#xff0c;也更完整地回顾一下自己职业生涯 15 年来和开源结缘的经历。 邂逅 Cappuccino 08 年参加工作后&#xff0c;第一个投入精力的开源项目是 Objective…

安装Home Assistant

文章目录 前言1. 安装Home Assistant2. 配置Home Assistant3. 安装cpolar内网穿透3.1 windows系统3.2 Linux系统3.3 macOS系统 4. 映射Home Assistant端口5. 公网访问Home Assistant6. 固定公网地址6.1 保留一个固定二级子域名6.2 配置固定二级子域名 转载自远程穿透的文章&…

网络安全 | 密码基础知识介绍

概述 密码介绍 安全问题 保密性&#xff1a;对发送的消息进行获取完整性&#xff1a;对发送的消息进行篡改身份伪造&#xff1a;对发送的主体身份进行篡改&#xff0c;a发的消息&#xff0c;篡改为b发的行为抵赖&#xff1a;对发送的消息进行否认&#xff0c;丧失行为的可追…

VulnHub打靶记录——easy_cloudantivirus

靶机下载地址&#xff1a;https://www.vulnhub.com/entry/boredhackerblog-cloud-av,453/ 将靶机设置为NAT模式并启动。 主机发现&信息收集 nmap扫描本地网段 nmap -sn 192.168.50.1/24136是kali&#xff0c;137就是我们的目标靶机。 接着收集靶场信息&#xff1a; n…

【 Android11 WiFi开发 二 】WiFi连接、断开

前言 上篇文章介绍了WiFi列表的获取与展示和WiFi状态改变的广播监听&#xff0c;本篇将介绍介绍对WiFi的操作&#xff0c;连接、忘记&#xff0c;查看已连接的WiFi信息等。 相关文章 1、【 Android11 WiFi开发 一 】WiFi列表获取与展示 说明 老规矩&#xff0c;先把WiFi连接和…

卷积基础知识总结

卷积是卷积神经网络中的核心模块&#xff0c;卷积的目的是提取输入图像的特征&#xff0c;如下图所示&#xff0c;卷积可以提取图像中的边缘特征信息。卷积也称为过滤器&#xff0c;即Filter 1 卷积的计算方法 卷积运算本质上就是在滤波器和输入数据的局部区域间做点积 注意…

MyBatis介绍与安装下载

目录 MyBatis 介绍 MyBatis 主要特点 MyBatis 下载 创建 Maven 工程 IDEA 连接数据库 导入JAR文件到项目 MyBatis 介绍 MyBatis是一种开源的Java持久化框架&#xff0c;用于将SQL数据库访问和映射任务与Java对象之间的映射分离。它提供了一种简单的方式来对数据库进行操…

消息中间件的首选之一:探索RocketMQ的优势和特性

​​​​​​​ 1、简介 RocketMQ 是一款开源的分布式消息队列系统&#xff0c;由阿里巴巴集团开发并开源。它是为了满足大规模分布式系统中的消息通信和异步解耦需求而设计的&#xff0c;具有高吞吐量、低延迟、可靠性强等特点。下面将详细介绍 RocketMQ 的架构、组件和关键特…

【网络进阶】Posix API与网络协议栈(三)

文章目录 1. 网络攻击和POSIX API与网络协议栈的关系1.1 网络攻击的基本概念和它们对协议栈的影响1.2 分布式拒绝服务&#xff08;DDoS&#xff09;攻击和网络协议栈1.3 地址解析协议&#xff08;ARP&#xff09;欺骗和POSIX API 2. 网络协议栈的理解和划分2.1 OSI七层模型2.2 …

AI 边缘计算控制器GEAC91

1 产品概览 产品概览 GEAC91 AI边缘计算控制器是一款基于 NVIDIA Jetson AGX Xavier处理 器、面向智能边缘计算应用场景的解决方案。 GEAC91控制器具有 控制器具有 GMSL2、 千兆网口CAN总线、 RS232、RS422、 USB3.0、USB2.0、SD卡等丰富的外设接口 &#xff0c;支持常见激光雷…