目录
HttpClient
http报文相关
URL是为了 统一的命名网络中的一个资源(URL不是单单为了HTTP协议而定义的,而是网络上的所有的协议都可以使用) ;因此URL有以下特点:
-
URL是可移植的。(所有的网络协议都可以使用URL)
-
URL的完整性。(不能丢失数据,比如URL中包含二进制数据时,如何处理)
-
URL的可阅读性。(希望人能阅读)
因为一些历史的原因URL设计者使用US-ASCII字符集表示URL。(原因比如ASCII比较简单;所有的系统都支持ASCII) 。
http报文
http报文是面向文本的,报文中每一个字段都是一些ASCII码串,各个字段的长度是不确定的 。http有两类报文:请求报文 响应报文 。
++使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号("?")代表URL的结尾与请求参数的开始,传递参数长度受限制,例如: /index.jsp?id=100&op=bind 。++
1.请求行
请求行由请求方法字段、URL字段和HTT协议版本字段组成;空格隔开eg:GET /index.html HTTP/1.1
HTTP请求的方法:GET POST HEAD PUT DELETE OTIONS TRACE CONNECT
GET:一般客户端从server读取信息;请求参数和值附加在URL后面,?隔开。传递参数长度受限。eg:/etccharging?id=erdfwsaxnjdcdcd&secret=qw2dccbhcv;
POST:可以传递更多的信息给服务器,将参数封装在HTTP请求数据中,可以传递大量数据,可用来传文件。
2.消息头部
请求头部以键值对组成,一行一对,分号隔开,主要包含通知Server关于Client的请求信息;如:
User-Agent:产生请求的浏览浏览器类型;
Accet:Client端可识别的内容列表
Host:请求的主机名。
3.空行
最后一个请求头之后是一个空行、发送回车符和换行符通知服务器请求头结束,是完整的Http报文必须格式(据此可以设计解析报文)。
4.请求正文
请求数据不在GET方法中使用,一般POST中主要使用,通常设置与此相关头Content-Type 和Content-Length
HttpClient发送报文格式
multipart/form-data、x-www-form-urlencoded、raw、binary
x-www-form-urlencoded:
1,它是post的默认格式,使用URLencode转码方法。包括将name、value中的空格替换为加号;将非ascii字符做百分号编码;将input的name、value用'='连接,不同的input之间用'&'连接 。
2、百分号编码: 比如汉字'武'吧,他的utf8编码在十六进制下是0xE69CA6,占3个字节 ,把它转成字符串'E69CA6',变成了六个字节,每两个字节前加上百分号前缀,得到字符串"%E6%9C%A6",变成九个ascii字符,占九个字节。把这九个字节 拼接到数据包里,这样就可以传输"非ascii字符的 utf8编码的 十六进制表示的 字符串的 百分号形式"。
3,同样使用URLencode转码,这种post格式跟get的区别在于,get把转换、拼接完的字符串用'?'直接与表单的action连接作为URL使用,所以请求体里没有数据;而post把转换、拼接后的字符串放在了请求体里 不会在浏览器的地址栏显示,稍微安全一些。
multipart/form-data
1、对于一段utf8编码的字节,用application/x-www-form-urlencoded传输其中的ascii字符没有问题,但对于非ascii字符传输效率就很低了(汉字'武'从三字节变成了九字节),因此在传很长的字节(如文件)时应用multipart/form-data格式。smtp等协议也使用或借鉴了此格式。
2、此格式表面上发送一段一句话和一个文件,请求体如下
同时请求头里规定了*++Content-Type: multipart/form-data++;*
++boundary=----WebKitFormBoundarymNhhHqUh0p0gfFa8++
可见请求体里不同的input之间用一段叫boundary的字符串分割,每个input都有了自己一个小header,其后空行接着是数据。
multipart/form-data将表单中的每个input转为了一个由boundary分割的小格式,没有转码,直接将utf8字节拼接到请求体中(然后逐字节转码ASCII),在本地有多少字节实际就发送多少字节,极大提高了效率,适合传输长字节 。
raw
可以上传任意格式的文本,可以上传text、json、xml、html等。
binary
相当于Content-Type:application/octet-stream,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件。
QUrl
对字符串进行 URL 格式化编码 ;
cpp
[static] QByteArray QUrl::toPercentEncoding(const QString &input, const QByteArray &exclude = QByteArray(), const QByteArray &include = QByteArray())
Returns an encoded copy of input . input is first converted to UTF-8 , and all ASCII-characters that are not in the unreserved group are percent encoded . To prevent characters from being percent encoded pass them to exclude . To force characters to be percent encoded pass them to include.
Unreserved is defined as: ALPHA / DIGIT / "-" / "." / "_" / "~"
cpp
QByteArray ba = QUrl::toPercentEncoding("{a fishy string?}", "{}", "s");
qDebug(ba.constData());
// prints "{a fi%73hy %73tring%3F}"
遵循这些原则,以避免在处理 URL 和字符串时,出现错误的字符转换:当从一个 QByteArray 或一个char * 创建包含 URL 的 QString 时,记得要用 QString::fromUtf8()。
URL格式
scheme
scheme 指定使用的传输协议,它由 URL 起始部分的一个或多个 ASCII 字符表示。scheme 只能包含 ASCII 字符,对输入不做转换或解码,必须以 ASCII 字母开始。
scheme 严格兼容 RFC 3986:scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
协议 | 描述 |
---|---|
file | 资源是本地计算机上的文件。格式:file:///,注意后边应是三个斜杠。 |
ftp | 通过 FTP 访问资源。格式:FTP:// |
gopher | 通过 Gopher 协议访问该资源。 |
http | 通过 HTTP 访问该资源。格式:HTTP:// |
https | 通过安全的 HTTPS 访问该资源。格式:HTTPS:// |
mailto | 资源为电子邮件地址,通过 SMTP 访问。格式:mailto: |
MMS | 通过支持MMS(流媒体)协议的播放该资源(代表软件:Windows Media Player)。格式:MMS:// |
ed2k | 通过 支持ed2k(专用下载链接)协议的P2P软件访问该资源(代表软件:电驴)。格式:ed2k:// |
Flashget | 通过 支持Flashget:(专用下载链接)协议的P2P软件访问该资源(代表软件:快车)。格式: Flashget:// |
thunder | 通过 支持thunder(专用下载链接)协议的 P2P 软件访问该资源(代表软件:迅雷)。格式: thunder:// |
news | 通过 NNTP 访问该资源。 |
下图显示了一个 URL,其 scheme 是 ftp
:
要设置 scheme,使用以下方式:
QUrl url;url.setScheme("ftp");12
Authority
URL 的 authority 由用户信息、主机名和端口组成。所有这些元素都是可选的,即使 authority 为空,也是有效的。
格式:username:password@hostname:port
用户信息和主机由'@'
分割,主机和端口由 ':'
分割 。如果用户信息为空,'@'
必须被省略。尽管端口为空时,允许使用 ':'
。
host:指存放资源的服务器的域名系统(DNS)主机名或 IP 地址。
port:整数,可选,省略时使用方案的默认端口,各种传输协议都有默认的端口号,如 HTTP 的默认端口为80。如果输入时省略,则使用默认端口号。有时候出于安全或其他考虑,可以在服务器上对端口进行重定义,即采用非标准端口号,此时,URL 中就不能省略端口号这一项。
user info
user info 指用户信息,是 URL 中 authority 可选的一部分。
用户信息包括:用户名和一个可选的密码,由 ':'
分割。 如果密码为空,':'
必须被省略。
path
由零或多个 /
隔开的字符串,一般用来表示主机上的一个目录或文件地址。在 authority 之后,query 之前。
对于没有层级的 schemes,路径将是 scheme 后的所有部分,像下面这样:
query
query 指查询字符串,可选,用于给动态网页(例如:使用 CGI、ISAPI、PHP/JSP/ASP/ASP、.NET 等技术制作的网页)传递参数,可有多个参数,用 &
隔开,每个参数的名和值用 =
隔开。
fragment
fragment 指定网络资源中的片断 。是 URL 的最后一部分,由'#'
后面跟的字符串表示。通常指的是用于 HTTP 页面上的某个链接或点。
例如:一个网页中有多个名词解释,可使用 fragment 直接定位到某一名词解释。
fragment 有时也被称为 URL"引用"。
传递一个 QString()(null 字符串)将取消 fragment 的设置。传递一个参数 QString("")(空而非 null 字符串)将 fragment 设置为一个空字符串(和原始 URL 一样,只有一个 "#"
)。
QNetworkAccessManager
Http-Get
cpp
QString QHttpClient::Get(const QString&url, const QMap<QString, QString>&queryItemMap, const QMap<QString, QString>&headerMap)
{
QUrl GetUrl(url);
QNetworkRequest requestInfo;
if (queryItemMap.isEmpty())
{
QUrlQuery query;//添加问好后面的key = value参数
QMap<QString, QString>::const_iterator iter = queryItemMap.constBegin();
while (iter != queryItemMap.constEnd()) {
query.addQueryItem(iter.key(), iter.value());
++iter;
}
GetUrl.setQuery(query);
}
requestInfo.setUrl(GetUrl);
if (!headerMap.isEmpty())
{
QMap<QString, QString>::const_iterator iter = headerMap.constBegin();
while (iter != headerMap.constEnd()) {
requestInfo.setRawHeader(iter.key().toUtf8(), iter.value().toUtf8());
++iter;
}
}
QNetworkAccessManager NetWorkManager;
QTimer timer;
timer.setInterval(3000);
timer.setSingleShot(true);
QEventLoop eventLoop;
QNetworkReply *reply = NetWorkManager.get(requestInfo);
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
//错误处理
QString replyStr;
if (reply->error() == QNetworkReply::NoError)
{
replyStr = reply->readAll();
}
else
{
reply->deleteLater();
qDebug() << reply->errorString();
}
return replyStr;
}
Http-Post
cpp
QString QHttpClient::Post(const QString&url, const QString&Content, const QMap<QString, QString>&headerMap)
{
QUrl PostUrl(url);
if (!PostUrl.isValid())
{
return "";
}
QNetworkRequest requestInfo(PostUrl);
if (!headerMap.isEmpty())
{
QMap<QString, QString>::const_iterator iter = headerMap.constBegin();
while (iter != headerMap.constEnd()) {
requestInfo.setRawHeader(iter.key().toUtf8(), iter.value().toUtf8());
++iter;
}
}
requestInfo.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkAccessManager NetWorkManager;
QTimer timer;
timer.setInterval(3000);
timer.setSingleShot(true);
QEventLoop eventLoop;
QNetworkReply *reply = NetWorkManager.post(requestInfo, Content.toUtf8());
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
QString replyStr;
if (reply->error() == QNetworkReply::NoError)
{
replyStr = reply->readAll();
}
else
{
reply->deleteLater();
}
qDebug() << reply;
return replyStr;
}
http-post:form-data
cpp
QFile file(picpath);
if (!file.open(QIODevice::ReadOnly))
{
cLogger(LOG_MODULE_NAME)->error("file open {} failed", picpath);
return;
}
QByteArray imageData = file.readAll();
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\";filename=\"" + file.fileName() + "\""));
imagePart.setBody(imageData);
multiPart->append(imagePart);
QUrl qurl(url);
QNetworkRequest request(qurl);
QNetworkAccessManager NetManager;
QTimer timer;
timer.setInterval(m_sendTimeOut);
timer.setSingleShot(true);
QEventLoop eventLoop;
QNetworkReply *reply = NetManager.post(request, multiPart);
multiPart->setParent(reply);
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
if (reply->error() == QNetworkReply::NoError)
{
strReply = reply->readAll();
cLogger(LOG_MODULE_NAME)->info("reply: {}", strReply);
}
else
{
cLogger(LOG_MODULE_NAME)->error("Request connection failed! {}", reply->errorString());
}
reply->deleteLater();
ftp-up
cpp
bool QHttpClient::UploadFtp(const QString&host, const int port, const QString&username, const QString&password, const QByteArray&Content, const QString&uploadFilepath, bool bVertrify)
{
QUrl ftpUrl;
ftpUrl.setScheme("ftp");
ftpUrl.setHost(host);
ftpUrl.setPort(port);
if (bVertrify)
{
ftpUrl.setUserName(username);//匿名则不需要
ftpUrl.setPassword(password);//匿名则不需要
}
ftpUrl.setPath(uploadFilepath);
QNetworkRequest requestInfo;
requestInfo.setUrl(ftpUrl);
QNetworkAccessManager ftpWorkManager;
QTimer timer;
timer.setInterval(3000);
timer.setSingleShot(true);
QEventLoop eventLoop;
QNetworkReply *reply = ftpWorkManager.put(requestInfo, Content);
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
if (reply->error() == QNetworkReply::NoError)
{
return true;
}
else
{
reply->deleteLater();
return false;
}
}
ftp-down
cpp
bool QHttpClient::DownLoadFtp(const QString&host, const int port, const QString&username, const QString&password, QString&getContentStr, const QString&uploadFilepath, bool bVertrify)
{
QUrl ftpUrl;
ftpUrl.setScheme("ftp");
ftpUrl.setHost(host);
ftpUrl.setPort(port);
if (bVertrify)
{
ftpUrl.setUserName(username);//匿名则不需要
ftpUrl.setPassword(password);//匿名则不需要
}
ftpUrl.setPath(uploadFilepath);
QNetworkRequest requestInfo;
requestInfo.setUrl(ftpUrl);
QNetworkAccessManager ftpWorkManager;
QTimer timer;
timer.setInterval(3000);
timer.setSingleShot(true);
QEventLoop eventLoop;
QNetworkReply *reply = ftpWorkManager.get(requestInfo);
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
if (reply->error() == QNetworkReply::NoError)
{
getContentStr = reply->readAll();
return true;
}
else
{
reply->deleteLater();
return false;
}
}
QDesktopServices
主要用于与桌面环境交互,启用外部程序打开任意URL地址。Qt会根据传进来的url决定具体的操作。可以应用在软件打开本地的帮助文档、生成的excel数据表快捷打开等等,也可以打开在线的帮助引导网页等。
|--------------------|-------|-------------------------------------|
| Constant | Value | Description |
| QUrl::TolerantMode | 0 | QUrl试图纠正一些常见的url错误,便于解析不完全符合标准的url。 |
| QUrl::StrictMode | 1 | 只接受有效的url,可以验证url的格式。 |
自定义openUrl行为
外部程序运行效果