云计算百科
云计算领域专业知识百科平台

QT制作处理HTTP get请求的服务器

本人在学习QT时,想用QT制作一个可以处理HTTP请求的服务器,那么这个小项目该怎么实现呢,

首先,我们需要在项目的pro文件中添加 network,因为其中包含了许多网络编程的头文件。如图所示:

导入之后·,我们就要思考,要想处理HTTP请求,首先要让浏览器与服务器建立TCP连接,只有建立连接后,二者才能收发数据。因此首先,我们先建立TCP连接,当我们在linux中建立TCP连接时,我们通常的步骤是

1.创建用于监听的文件描述符

2.将该文件描述符与本机ip和预设端口(例如10000)进行绑定

3.监听该文件描述符,当有新连接时,创建用于通信的文件描述符。

这时我们的TCP连接就算完成了,在此之后就可以进行通信了。

但是对于QT来说,我们并不需要做这么多操作,我们只需要调用QTcpServer类中的listen函数就可以实现对指定IP的端口的监听,其参数为 (IP,端口),以本地为服务器为例(当然这种方法只有在同一局域网中别人才可以访问问到,要想实现所有人都可以访问到的服务器,可以将程序在云电脑上运行,这样就可以通过公网IP所有人都可以访问到)

QTcpServer::listen(QHostAddress::Any, 10000);//本机ip,端口号为10000

同时,当有新的客户端发送连接请求时,QTcpserver会自动的调用incomingConnection函数,传递的参数(socketDescriptor)是接受的连接的本机套接字描述符。基本实现创建一个 QTcpSocket,

要想得到用于通信的套接字,可以用以下方式得到:

QTcpSocket* socket=new QTcpSocket;
socket->setSocketDescriptor(socketDescriptor);

那么得到用于通信的文件描述符后,TCP连接就建立成功了,接下来我们要实现HTTP请求的处理和HTTP响应的发送。

要想处理HTTP请求(这里我们只讨论get请求),我们首先要知道HTTP请求到底是在请求什么,也就是对方发过来的一堆数据到底想表达什么,我们就要读懂HTTP请求数据的格式,下图是我在服务器得到的请求数据,每一行以\\r\\n结尾:

第一行表示请求行,其数据分别表示     请求数据的方法  请求的路径  http协议版本

第二行至空行之前表示请求头 ,每一行都有请求的属性 

对于get请求,http请求报文以空行结尾(即\\r\\n)。

知道了HTTP的请求格式,我们就能从请求中获取到我们想要的数据,首先,我们要知道请求使用的是get还是post方法来请求数据,其次我们需要知道对方是想要获取的数据是什么,至于其他的数据,我们没有必要保存。

其代码实现如下:

QByteArray &buffer = buffers[socket];
buffer.append(socket->readAll()); // 读取数据到缓存

// 检查是否接收到完整的HTTP头部(以\\r\\n\\r\\n结尾)
int endOfHeader = buffer.indexOf("\\r\\n\\r\\n");
if (endOfHeader == -1) return; // 数据不完整,等待后续数据

QString requestHeader = buffer.left(endOfHeader); // 提取头部
buffer.clear(); // 清空缓存

QTextStream stream(&requestHeader, QIODevice::ReadOnly);
QString requestLine = stream.readLine(); // 解析请求行
QStringList parts = requestLine.split(' ');
if (parts.size() < 3) {
sendResponse(socket, 400, "Bad Request", "text/html", "<h1>400 Bad Request</h1>");
return;
}

QString method = parts[0].toUpper();
QString path = parts[1];

知道了对方想要的是什么,我们就要判断我们是否有对应的文件或目录,如果没有我们就返回一个404 NotFound网页,如果有,我们就读取对应的文件然后发送给客户端。那么我们发送也有格式要求,要不然浏览器也不知道我们发过来的是什么东西,

对应的格式就是:

状态行:HTTP/1.1 200 OK   (其中的200表示状态码)

响应头: Content-Type:      (用来表述响应数据的一些属性,其中内容类型是必须写入其中的)

空行:\\r\\n

响应体:(真正响应想要传递给浏览器的数据)

其代码实现如下:

QByteArray response;
response.append(QString("HTTP/1.1 %1 %2\\r\\n").arg(statusCode).arg(statusText).toUtf8());
response.append(QString("Content-Type: %1\\r\\n").arg(contentType).toUtf8());
response.append(QString("Content-Length: %1\\r\\n").arg(content.toUtf8().size()).toUtf8());
response.append("Connection: close\\r\\n"); // 发送后关闭连接
response.append("\\r\\n"); // 头部结束
response.append(content.toUtf8()); // 添加内容
socket->write(response); // 发送响应

至此,HTTP数据的收发均已实现。

最后附上完整的代码:

头文件:

class HttpServer : public QTcpServer
{
Q_OBJECT
public:
explicit HttpServer(QObject *parent = nullptr);
void startServer();
virtual void incomingConnection(qintptr socketDescriptor);

protected slots:
// void handleNewConnection();
void readRequest();
void sendResponse(QTcpSocket *socket, int statusCode, const QString &statusText, const QString &contentType, const QString &content);
private:
QMap<QTcpSocket*, QByteArray> buffers; // 用于缓存每个socket的数据
};

cpp文件:

HttpServer::HttpServer(QObject *parent) : QTcpServer(parent)
{
}

void HttpServer::startServer()
{
if (!this->listen(QHostAddress::Any, 10000)) {
qDebug() << "Could not start server";
} else {
qDebug() << "Listening on port 10000…";
}
}

void HttpServer::incomingConnection(qintptr socketDescriptor)
{

QTcpSocket* socket=new QTcpSocket;
socket->setSocketDescriptor(socketDescriptor);
connect(socket, &QTcpSocket::readyRead, this, &HttpServer::readRequest);
connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
connect(socket, &QTcpSocket::disconnected, this, [this, socket]() {
buffers.remove(socket); // 清理缓存
});
buffers.insert(socket, QByteArray()); // 初始化缓存
qDebug() << "New connection from" << socket->peerAddress().toString();
}

void HttpServer::readRequest()
{
qDebug()<<"准备解析请求";
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) return;

QByteArray &buffer = buffers[socket];
buffer.append(socket->readAll()); // 读取数据到缓存

// 检查是否接收到完整的HTTP头部(以\\r\\n\\r\\n结尾)
int endOfHeader = buffer.indexOf("\\r\\n\\r\\n");
if (endOfHeader == -1) return; // 数据不完整,等待后续数据

QString requestHeader = buffer.left(endOfHeader); // 提取头部
buffer.clear(); // 清空缓存(本例中处理完即关闭连接)

QTextStream stream(&requestHeader, QIODevice::ReadOnly);
QString requestLine = stream.readLine(); // 解析请求行
QStringList parts = requestLine.split(' ');
if (parts.size() < 3) {
sendResponse(socket, 400, "Bad Request", "text/html", "<h1>400 Bad Request</h1>");
return;
}

QString method = parts[0].toUpper();
QString path = parts[1];

// 仅处理GET请求
if (method != "GET") {
sendResponse(socket, 501, "Not Implemented", "text/html", "<h1>501 Not Implemented</h1>");
return;
}

// 根据路径生成响应内容
QString content;
QString contentType = "text/html";
int statusCode = 200;
QString statusText = "OK";

if (path == "/") {
content = "tem: 23";
} else if (path == "/test") {
content = "<h1>Test Page</h1><p>This is a test response.</p>";
} else if (path.startsWith("/api/data")) {
contentType = "application/json";
content = "{\\"message\\": \\"Hello, World!\\"}";
} else {
statusCode = 404;
statusText = "Not Found";
content = "<h1>404 Not Found</h1>";
}
sendResponse(socket, statusCode, statusText, contentType, content);
}

void HttpServer::sendResponse(QTcpSocket *socket, int statusCode, const QString &statusText, const QString &contentType, const QString &content)
{
QByteArray response;
response.append(QString("HTTP/1.1 %1 %2\\r\\n").arg(statusCode).arg(statusText).toUtf8());
response.append(QString("Content-Type: %1\\r\\n").arg(contentType).toUtf8());
response.append(QString("Content-Length: %1\\r\\n").arg(content.toUtf8().size()).toUtf8());
response.append("Connection: close\\r\\n"); // 发送后关闭连接
response.append("\\r\\n"); // 头部结束
response.append(content.toUtf8()); // 添加内容

socket->write(response); // 发送响应
socket->disconnectFromHost(); // 断开连接
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » QT制作处理HTTP get请求的服务器
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!