一个Http请求包括
请求行 请求头 空行 请求体
下面是示例: 1,2,3,4分别代表上面的四个部分,我只是做了一些解析,具体可以结合代码
1. post / HTTP/1.1
2.GET请求头包括Host(主机名),user-agent(客户端标识符),connection:keep-alived长连接方式
2.如果是Post请求,还包含Content-Length Content-Type字段,这些字段QT中发送的时候都可2.以自己设定,也可以使用默认的,使用setheader设置预定义的这些字段,使用setRawHeader可以任意设定k-val;
3.空行
4.json数据/html数据/文本数据
QT客户端使用HTTP请求发送
下面代码在构造的时候向服务器发送了HTTP请求,服务器只需要准备一个套接字读取数据即可,
这样就知道了服务器收到哪些数据,以及后续应该这么解析这个HTTP请求。
下面的代码中,使用setHeader和setRawHeader可以设置请求的一些参数,不设定的话默认也会生成一些参数,比如Host,,user-agent,connection这些字段。可以自己更改代码然后查看服务器的变化。
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include<QJsonDocument>
#include<QJsonObject>
#include<QUrl>
#include<QDebug>
class HttpClient : public QObject
{
Q_OBJECT
public:
HttpClient(QObject *parent = nullptr) : QObject(parent) {
// 创建 QNetworkAccessManager 实例
manager = new QNetworkAccessManager(this);
// 连接信号槽,处理请求完成的响应
connect(manager, &QNetworkAccessManager::finished, this, &HttpClient::onFinished);
// 构造 HTTP 请求
QUrl url("http://192.168.31.245:8080"); // 示例 URL
QNetworkRequest request(url);
// request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
//request.setHeader(QNetworkRequest::ContentLengthHeader,"13");
request.setRawHeader("User-Agent", "MyApp/1.0");
request.setRawHeader("HH--YY--PP", "i am fun");
// 构建 JSON 数据
QJsonObject json;
json["title"] = "foo";
json["body"] = "bar";
json["userId"] = 1;
QJsonDocument jsonDoc(json);
QByteArray jsonData = jsonDoc.toJson();
// 发送 POST 请求
manager->post(request, jsonData);
//manager->get(request);
}
private slots:
// 处理服务器的响应
void onFinished(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) {
// 读取服务器响应的数据
QByteArray responseData = reply->readAll();
qDebug() << "Response data:" << responseData;
} else {
// 处理请求中的错误
qDebug() << "Error:" << reply->errorString();
}
// 释放资源
reply->deleteLater();
}
private:
QNetworkAccessManager *manager; // HTTP 管理器
};
服务器代码
下面的代码在本地ubantu 虚拟机运行,这里只需要实现read数据就可以,因为这里主要验证的是服务器收到的数据有哪些?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h> // 包含网络地址结构和函数
#include <sys/socket.h> // socket 相关函数
#define PORT 8080 // 定义服务器的监听端口
#define BUFFER_SIZE 5555// 定义缓冲区大小
int main() {
int server_fd, new_socket; // 服务器的 socket 文件描述符和新连接的 socket
struct sockaddr_in address; // 存储服务器地址
int addrlen = sizeof(address); // 地址长度
char buffer[BUFFER_SIZE] = {0}; // 用于接收消息的缓冲区
const char *message = "Hello from server"; // 服务器的响应消息
// 创建 socket 文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 服务器地址结构配置
address.sin_family = AF_INET; // IPv4
address.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用的网络接口
address.sin_port = htons(PORT); // 转换端口为网络字节序
// 绑定 socket 到指定端口和地址
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 监听端口,最大等待队列长度为 3
if (listen(server_fd, 3) < 0) {
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server is listening on port %d\n", PORT);
// 接受来自客户端的连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 从客户端接收数据
int valread = read(new_socket, buffer, BUFFER_SIZE);
printf("Message from client:\n%s", buffer);
// 关闭 socket
close(new_socket);
close(server_fd);
return 0;
}
收到的数据如下
request.setRawHeader("User-Agent", "MyApp/1.0");
request.setRawHeader("HH--YY--PP", "i am fun");
上面的代码可以任意设定请求头的字段,更加的灵活。
将QT注释的代码取消注释,即可更新ConTent-type字段,Lenth字段没设定的话可以自动填充,一般不用自己设定,只需要设定ConTent-type来告诉服务器请求头是什么类型的数据即可。
总结
最后服务器收到这些数据,根据请求行的URI,这里是/路径下,如果是get请求,根据后面的参数即可找到对应的文件,比如是一个html网页的静态页面,直接返回这个静态的HTML页面就可以。如果是Post请求,那么就读取请求体的数据来做后续的业务处理,比如客户端的登录,解析出用户名,密码(可以自己加密)等去查询数据库,最后封装响应返回即可。