Qt中的网络客户端

目录

HttpClient

http报文相关

HttpClient发送报文格式

x-www-form-urlencoded:

multipart/form-data

raw

binary

QUrl

QNetworkAccessManager

Http-Get

Http-Post

http-post:form-data

ftp-up

ftp-down

QDesktopServices


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行为

外部程序运行效果

相关推荐
Beginner_bml13 分钟前
TCP协议的三次握手与四次挥手的过程
网络·网络协议·tcp/ip
GEEKVIP1 小时前
选择 PDF 编辑器时要考虑什么?如何选择适用于 Windows 10 的 PDF 编辑器
网络·windows·ios·智能手机·pdf·编辑器·iphone
双燕难凭远信4 小时前
VMware桥接模式无法连接网络
网络·桥接模式
Dylanioucn4 小时前
【网络】web1.0 2.0 3.0各自出现背景/技术原理/演化发展过程,以及Web 3.0 对传统互联网的影响
网络·网络协议·安全
Bartender_Jill9 小时前
[ROS2]解决PyQt5和sip的各种报错问题 stderr: qt_gui_cpp
开发语言·python·qt·机器人·数据可视化
南棱笑笑生10 小时前
20241005给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Android12时使用iperf3测网速
linux·运维·网络
DC_BLOG10 小时前
MPLS解决BGP路由黑洞问题
运维·服务器·网络·ip
知新_ROL10 小时前
QT 中如何保存matlab 能打开的.mat数据矩阵!
qt·matlab·矩阵