qt实现一个简单http服务器和客户端
一、功能简介
服务器:
登录功能、下载文件功能
客户端:
登录功能、下载文件功能、上传成绩功能
二、服务器代码
//HttpServer.h
#ifndef HTTPSERVER_H
#define HTTPSERVER_H#include <QMainWindow>
#include <QTcpSocket>
#include <QTcpServer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>
#include <QString>
#include <QCoreApplication>
#include <QDir>
class Server:public QTcpServer
{
//http服务器类Q_OBJECT
public:Server(QObject *parent = nullptr) : QTcpServer(parent) {}
signals:
//信号函数
//自定义信号函数,发送信号,将信息显示在界面类上的textedit中void login_success(QString str);void login_faild(QString str);void download_success(QString str);void download_faild(QString str);void upload_success(QString str);
protected:void incomingConnection(qintptr socketDescriptor) override {QTcpSocket *socket = new QTcpSocket(this);socket->setSocketDescriptor(socketDescriptor);connect(socket, &QTcpSocket::readyRead, this, [this, socket]() {handleRequest(socket);});}
private:void handleRequest(QTcpSocket *socket){QString request = socket->readAll();QStringList lines = request.split("\r\n");QString requestLine = lines[0];QStringList requestParts = requestLine.split(" ");QString method = requestParts[0];QString path = requestParts[1];if (method == "POST" && path == "/login") {handleLogin(socket, request);} else if (method == "GET" && path == "/download") {handleDownload(socket);} else if (method == "POST" && path == "/upload") {handleUpload(socket, request);} else {sendResponse(socket, 404, "Not Found");}}void handleLogin(QTcpSocket *socket, const QString &request){// 解析请求体QString body = request.split("\r\n\r\n").last();QJsonDocument doc = QJsonDocument::fromJson(body.toUtf8());QJsonObject json = doc.object();QString username = json["username"].toString();QString password = json["password"].toString();// 简单验证if (username == "admin" && password == "123456") {sendResponse(socket, 200, "Login Success");emit login_success(username+":"+"Login Success!");//ui->textEdit->append(username+":"+"Login Success!");} else {sendResponse(socket, 401, "Login Failed");emit login_faild(username+":"+"Login Failed!");//ui->textEdit->append(username+":"+"Login Failed!");}}void handleDownload(QTcpSocket *socket){// 获取应用程序的当前目录QString appDir = QCoreApplication::applicationDirPath();// 构建文件的完整路径QString filePath = QDir(appDir).filePath("test.txt");QFile file(filePath);if (file.open(QIODevice::ReadOnly)) {QByteArray data = file.readAll();sendResponse(socket, 200, data);file.close();emit upload_success("download Success!");} else {emit upload_success("download Failed!");sendResponse(socket, 404, "File Not Found");}}void handleUpload(QTcpSocket *socket, const QString &request){QString body = request.split("\r\n\r\n").last();QJsonDocument doc = QJsonDocument::fromJson(body.toUtf8());QJsonObject json = doc.object();QString chinese = json["chinese"].toString();QString math = json["math"].toString();QString english = json["english"].toString();// 简单处理,打印上传的数据emit upload_success("语文:"+chinese+";"+"数学:"+math+";"+"英语:"+english);qDebug() << "语文:" << chinese << "数学:" << math << "英语:" << english;sendResponse(socket, 200, "Upload Success");}void sendResponse(QTcpSocket *socket, int statusCode, const QByteArray &body){QString response = QString("HTTP/1.1 %1 OK\r\n""Content-Type: text/plain\r\n""Content-Length: %2\r\n""\r\n""%3").arg(statusCode).arg(body.size()).arg(QString(body));socket->write(response.toUtf8());socket->disconnectFromHost();}};QT_BEGIN_NAMESPACE
namespace Ui {
class HttpServer;
}
QT_END_NAMESPACEclass HttpServer : public QMainWindow
{
//http服务器界面类Q_OBJECTpublic:HttpServer(QWidget *parent = nullptr);~HttpServer();private:Ui::HttpServer *ui;Server* m_server;
};
#endif // HTTPSERVER_H
//HttpServer.cpp
#include "httpserver.h"
#include "ui_httpserver.h"HttpServer::HttpServer(QWidget *parent): QMainWindow(parent), ui(new Ui::HttpServer)
{ui->setupUi(this);m_server= new Server();m_server->listen(QHostAddress::Any,8080);connect(m_server,&Server::login_success,this,[=](QString str){ui->textEdit->append(str);});connect(m_server,&Server::login_faild,this,[=](QString str){ui->textEdit->append(str);});connect(m_server,&Server::download_success,this,[=](QString str){ui->textEdit->append(str);});connect(m_server,&Server::download_faild,this,[=](QString str){ui->textEdit->append(str);});connect(m_server,&Server::upload_success,this,[=](QString str){ui->textEdit->append(str);});
}
HttpServer::~HttpServer()
{delete ui;
}
三、客户端代码
//HttpClient.h
#ifndef HTTPCLIENT_H
#define HTTPCLIENT_H#include <QMainWindow>
#include<QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>
#include <QDebug>
#include <QPushButton>QT_BEGIN_NAMESPACE
namespace Ui {
class HttpClient;
}
QT_END_NAMESPACEclass HttpClient : public QMainWindow
{Q_OBJECTpublic:HttpClient(QWidget *parent = nullptr);~HttpClient();
private:Ui::HttpClient *ui;QNetworkAccessManager *manager;
private slots:void login( );void downloadFile( );void uploadScore( );
};
#endif // HTTPCLIENT_H
//HttpClient.cpp
#include "httpclient.h"
#include "ui_httpclient.h"HttpClient::HttpClient(QWidget *parent): QMainWindow(parent), ui(new Ui::HttpClient)
{ui->setupUi(this);manager=new QNetworkAccessManager;connect(ui->pb_login,&QPushButton::clicked,this,&HttpClient::login);connect(ui->pu_down,&QPushButton::clicked,this,&HttpClient::downloadFile);connect(ui->pb_upload,&QPushButton::clicked,this,&HttpClient::uploadScore);
}
void HttpClient::login()
{QUrl url("http://127.0.0.1:8080/login");QNetworkRequest request(url);request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");QJsonObject json;json["username"] = ui->li_name->text();//adminjson["password"] = ui->li_pswd->text();//123456QNetworkReply *reply = manager->post(request, QJsonDocument(json).toJson());QObject::connect(reply, &QNetworkReply::finished, [reply]() {if (reply->error() == QNetworkReply::NoError) {qDebug() << "Login Response:" << reply->readAll();} else {qDebug() << "Login Error:" << reply->errorString();}reply->deleteLater();});
}
void HttpClient::downloadFile()
{QUrl url("http://127.0.0.1:8080/download");QNetworkRequest request(url);QNetworkReply *reply = manager->get(request);QObject::connect(reply, &QNetworkReply::finished, [reply]() {if (reply->error() == QNetworkReply::NoError) {QByteArray data = reply->readAll();QFile file("D:\\Project\\qtcreator\\HttpClient\\downloaded.txt");if (file.open(QIODevice::WriteOnly)) {file.write(data);file.close();qDebug() << "File downloaded successfully!";}} else {qDebug() << "Download Error:" << reply->errorString();}reply->deleteLater();});
}
void HttpClient::uploadScore()
{QUrl url("http://127.0.0.1:8080/upload");QNetworkRequest request(url);request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");QJsonObject json;json["chinese"] = ui->li_chinese->text();json["math"] =ui->li_math->text();json["english"] = ui->li_english->text();QNetworkReply *reply = manager->post(request, QJsonDocument(json).toJson());QObject::connect(reply, &QNetworkReply::finished, [reply]() {if (reply->error() == QNetworkReply::NoError) {qDebug() << "Upload Response:" << reply->readAll();} else {qDebug() << "Upload Error:" << reply->errorString();}reply->deleteLater();});
}
HttpClient::~HttpClient()
{delete ui;
}
四、其他设置
1.不管是服务器还是客户端,都要在.pro文件中添加 network
QT += core gui network
2.保证在httpserver.exe目录下有text.txt文件
五、测试
分别运行服务器与客户端程序
1.登录功能
客户端输入用户名与密码,点击登录
2.下载功能
3.上传功能
六、追加python测试
1.下载库(pip install requests)
import requests# 服务器地址和端口
SERVER_URL = "http://localhost:8080"def test_login():"""测试用户登录接口"""url = f"{SERVER_URL}/login"data = {"username": "admin","password": "123456"}response = requests.post(url, json=data)# 验证响应if response.status_code == 200:print("Login Test: PASSED")else:print(f"Login Test: FAILED (Status Code: {response.status_code}, Response: {response.text})")def test_download():"""测试文件下载接口"""url = f"{SERVER_URL}/download"response = requests.get(url)# 验证响应if response.status_code == 200:print("Download Test: PASSED")print("File Content:")print(response.text) # 打印文件内容else:print(f"Download Test: FAILED (Status Code: {response.status_code}, Response: {response.text})")def test_upload():"""测试成绩上传接口"""url = f"{SERVER_URL}/upload"data = {"chinese": "80","math": "90","english": "79"}response = requests.post(url, json=data)# 验证响应if response.status_code == 200:print("Upload Test: PASSED")else:print(f"Upload Test: FAILED (Status Code: {response.status_code}, Response: {response.text})")def run_tests():"""运行所有测试"""print("Running Tests...")test_login()test_download()test_upload()print("All Tests Completed.")if __name__ == "__main__":run_tests()
3.结果