使用QT搭建简单的httpserver调试工具【贴源码】

设计思路

qt自身不带httpserver相关的类,因此,需要我们自己来封装。
http相较于tcp,多了一些包头信息,已经get/post的请求方式,需要给出及时的应答,需要附带包头信息。
我们可以用tcpserver来封装httpserver,做一些简单的通讯打印。

实际效果图

在这里插入图片描述
不足之处
1、客户端数量没有做调试和优化,并不准确。
2、对于中文消息体未做任何处理。

源代码

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(HttpServer VERSION 0.1 LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

#QT相关部分
#set(CMAKE_PREFIX_PATH "D:/install_APP/qt5.15/5.15.2/msvc2019/lib/cmake")

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)

set(PROJECT_SOURCES
        main.cpp
        mymainwindow.cpp
        httpserver.cpp
        httpserver.h
        common.h
        mymainwindow.h
        mymainwindow.ui
)

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(HttpServer
        MANUAL_FINALIZATION
        ${PROJECT_SOURCES}
    )
# Define target properties for Android with Qt 6 as:
#    set_property(TARGET HttpServer APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
#                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
    if(ANDROID)
        add_library(HttpServer SHARED
            ${PROJECT_SOURCES}
        )
# Define properties for Android with Qt 5 after find_package() calls as:
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
    else()
        add_executable(HttpServer
            ${PROJECT_SOURCES}
        )
    endif()
endif()

target_link_libraries(HttpServer PRIVATE
    Qt${QT_VERSION_MAJOR}::Widgets
    Qt${QT_VERSION_MAJOR}::Core
    Qt${QT_VERSION_MAJOR}::Gui
    Qt${QT_VERSION_MAJOR}::Network
   # Qt${QT_VERSION_MAJOR}::Xml
)

set_target_properties(HttpServer PROPERTIES
    MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(HttpServer)
endif()

common.h

#ifndef COMMON_H
#define COMMON_H

#define MSG_CLIENT_IN     1   // 有客户端连接
#define MSG_CLIENT_OUT    2   // 有客户端断开
#define MSG_CLIENT_MSG    3   // 客户端发送来的消息

#endif // COMMON_H

httpserver.h

#ifndef HTTPSERVER_H
#define HTTPSERVER_H

#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include "common.h"

class httpserver : public QObject
{
    Q_OBJECT
public:
    //explicit httpserver(QObject *parent = nullptr);
    httpserver(quint16 port);
    ~httpserver();

    bool GetState(QString &Info);
    bool RespondClient(QTcpSocket* Sock, const QByteArray &data);
    QString GetMsgText(const QByteArray &data, QByteArray &Content);

signals:
    void SendMsg(int flag, QByteArray &data, QString Url = "");

public slots:
    void DealDisconnection();
    void NewConnection();
    void ReadData();
    void RecvNewPort(quint16 Port);
    void OnConnectionDisc(QAbstractSocket::SocketError error);

private:
    bool m_blistened;
    quint16 m_Port;
    QByteArray m_MsgData;
    QString m_Info;
    QTcpServer* m_pServer;
    QList<QTcpSocket*> m_SockClient;
};

#endif // HTTPSERVER_H

mymainwindow.h

#ifndef MYMAINWINDOW_H
#define MYMAINWINDOW_H

#include <QMainWindow>
#include <QDateTime>
#include <QFile>
#include "httpserver.h"
#include "common.h"
#include <QUrl>

QT_BEGIN_NAMESPACE
namespace Ui { class MyMainWindow; }
QT_END_NAMESPACE

class MyMainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MyMainWindow(QWidget *parent = nullptr);
    ~MyMainWindow();

signals:
    void SendNewPort(quint16 Port);   // 发送新的端口号

public slots:
    void RecvMsg(int flag, QByteArray &data, QString url);

private slots:
    void on_pb_Listen_clicked();

private:
    Ui::MyMainWindow *ui;

    int m_ClientNum;
    quint16 m_Port;
    httpserver* m_pHttpServer;
};
#endif // MYMAINWINDOW_H

httpserver.cpp

#include "httpserver.h"

httpserver::httpserver(quint16 port)
{
    m_Info = "";
    m_SockClient.clear();
    m_MsgData.clear();
    m_pServer = new QTcpServer(this);
    if(false == m_pServer->listen(QHostAddress::Any, port))
    {
        m_Info = m_pServer->errorString();
        m_pServer->close();
        m_blistened = false;
    }
    else
    {
        m_blistened = true;
        connect(m_pServer, &QTcpServer::newConnection, this, &httpserver::NewConnection);
        connect(m_pServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(OnConnectionDisc(QAbstractSocket::SocketError)));
    }
}

httpserver::~httpserver()
{
    for(int i = 0; i < m_SockClient.size(); i ++)
    {
        m_SockClient[i]->deleteLater();
    }

    if(nullptr != m_pServer)
    {
        m_pServer->close();
        delete m_pServer;
        m_pServer = nullptr;
    }
}

void httpserver::OnConnectionDisc(QAbstractSocket::SocketError error)
{
    qDebug() << "Http server error:" << error;
}

/*===============================================
 * 函数:GetState
 * 功能:获取http服务器的状态
 * 参数:Info         消息提示
 * 返回:true         监听成功
 *      false        监听失败
===============================================*/
bool httpserver::GetState(QString &Info)
{
   Info = m_Info;
   return m_blistened;
}

/*===============================================
 * 函数:RecvNewPort
 * 功能:获取新的端口号
 * 参数:Port         端口号
 * 返回:
===============================================*/
void httpserver::RecvNewPort(quint16 Port)
{
    m_Port = Port;
    if(m_pServer->isListening())
    {
        m_pServer->close();
        m_pServer->listen(QHostAddress::Any, m_Port);
    }
}

/*===============================================
 * 函数:NewConnection
 * 功能:处理新发来的http客户端申请,并回应
 * 参数:
 * 返回:
===============================================*/
void httpserver::NewConnection()
{
    if(m_pServer->hasPendingConnections())
    {
        QTcpSocket* Sock = m_pServer->nextPendingConnection();
        RespondClient(Sock, "");
        m_SockClient<<Sock;

        emit SendMsg(MSG_CLIENT_IN, m_MsgData);
        connect(Sock, &QTcpSocket::disconnected, this, &httpserver::DealDisconnection);
        connect(Sock, &QTcpSocket::readyRead, this, &httpserver::ReadData);
    }
}

/*===============================================
 * 函数:DealDisconnection
 * 功能:处理客户端断开问题
 * 参数:
 * 返回:
===============================================*/
void httpserver::DealDisconnection()
{
    for(int i = 0; i < m_SockClient.size(); )
    {
        if(QAbstractSocket::ConnectedState != m_SockClient[i]->state())
        {
            m_SockClient.removeAt(i);

            emit SendMsg(MSG_CLIENT_OUT, m_MsgData);
            continue;
        }
        i ++;
    }
}

/*===============================================
 * 函数:RespondClient
 * 功能:回应(发送)客户端消息
 * 参数:Sock          客户端
 *      data          数据
 * 返回:true          发送成功
 *      false         发送失败
===============================================*/
bool httpserver::RespondClient(QTcpSocket* Sock, const QByteArray &data)
{
    if(nullptr == Sock)
    {
        return false;
    }

    char length[32];
    memset(length, 0 ,sizeof(length));
    sprintf(length, "Content-Length:%d\r\n", data.size());
    Sock->write("HTTP/1.1 200 OK\r\n");
    Sock->write("Access-Control-Allow-Origin: *\r\n");
    Sock->write(length);
    Sock->write("Content-Type: application/octet-stream\r\n");
    Sock->write("Keep-Alive: timeout=5, max=5\r\n\r\n");
    if(0 != data.size())
    {
        Sock->write(data.data(), data.size());
    }

    return true;
}

/*===============================================
 * 函数:ReadData
 * 功能:读取数据
 * 参数:
 * 返回:
===============================================*/
void httpserver::ReadData()
{
    QByteArray data;
    QByteArray Msg;
    data.clear();
    Msg.clear();
    QTcpSocket* Sock = nullptr;
    for(int i = 0; i < m_SockClient.size(); i ++)
    {
        data.clear();
        Sock = m_SockClient.at(i);
        data = Sock->readAll();
        if(data.length() != 0)
        {
            qDebug()<<data;
            QString url = Sock->peerAddress().toString() + ":" + QString::number(Sock->peerPort()) + "/" + GetMsgText(data, Msg);
            emit SendMsg(MSG_CLIENT_MSG, Msg, url);
            data.clear();
            RespondClient(Sock, data);
        }
    }
}

/*===============================================
 * 函数:GetMsgText
 * 功能:获取客户端发送的内容
 * 参数:data            原始数据
 *      Content         客户端发送的内容
 * 返回:客户端的url
===============================================*/
QString httpserver::GetMsgText(const QByteArray &data, QByteArray &Content)
{
    if(data.isEmpty())
    {
        return "";
    }
    QString strData = data;

    // 获取url
    int Start = strData.indexOf("/");
    int End = strData.indexOf("HTTP/");
    QString Url = strData.mid(Start + 1, End - Start - 2);

    // 获取长度
    int locat_length = strData.indexOf("Content-Length:");
    int Locat_Content = strData.indexOf("\r\n\r\n");
    locat_length += qstrlen("Content-Length:");
    QString len = strData.mid(locat_length, Locat_Content - locat_length);

    // 获取内容
    Locat_Content += qstrlen("\r\n\r\n");
    Content = strData.mid(Locat_Content, len.toInt()).toLatin1();

    return Url;
}

mymainwindow.cpp

#include "mymainwindow.h"
#include "./ui_mymainwindow.h"

MyMainWindow::MyMainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MyMainWindow)
{
    ui->setupUi(this);
    setWindowTitle("httpServer");
    m_pHttpServer = nullptr;
    m_ClientNum = 0;
    ui->Client_num->setText(QString::number(m_ClientNum));

    // httpServer
    m_Port = 8000;
    ui->Port->setText(QString::number(m_Port));
    m_pHttpServer = new httpserver(m_Port);
    QString strInfo = "";
    if(false == m_pHttpServer->GetState(strInfo))
    {
        ui->RecvMsg->setText(strInfo);
    }
    else
    {
        strInfo = "HttpServer start !!!";
        ui->RecvMsg->setText(strInfo);

    }
    connect(m_pHttpServer, &httpserver::SendMsg, this, &MyMainWindow::RecvMsg);
    connect(this, SIGNAL(SendNewPort(quint16)), m_pHttpServer, SLOT(RecvNewPort(quint16)));
}

MyMainWindow::~MyMainWindow()
{
    delete ui;
}

/*==========================================
 * 函数:on_pb_Listen_clicked
 * 功能:启动http服务器,开始监听
 * 参数:
 * 返回:
==========================================*/
void MyMainWindow::on_pb_Listen_clicked()
{
    quint16 Port = ui->Port->text().toUShort();
    qDebug()<<"port:"<<Port;
    if(Port <= 1024 || Port > 65534 || Port == m_Port)
    {
        return;
    }
    m_Port = Port;

    emit SendNewPort(Port);
}

/*==========================================
 * 函数:RecvMsg
 * 功能:接收消息提示
 * 参数:flag        标志位
 *      data        消息
 * 返回:
==========================================*/
void MyMainWindow::RecvMsg(int flag, QByteArray &data, QString url)
{
    switch(flag)
    {
    // 新加入客户端
    case MSG_CLIENT_IN:
        {
            m_ClientNum ++;
            ui->Client_num->setText(QString::number(m_ClientNum));
        }
        break;
    // 有客户端退出
    case MSG_CLIENT_OUT:
        {
            m_ClientNum --;
            ui->Client_num->setText(QString::number(m_ClientNum));
        }
        break;
    // 打印客户端发来的信息
    case MSG_CLIENT_MSG:
        {
            QString strInfo = url + ":\n" + data + "\n";
            ui->RecvMsg->append(strInfo);
        }
        break;
    default:
        {

        }
    }
}

main.cpp

#include "mymainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyMainWindow w;
    w.show();
    return a.exec();
}

mymainwindow.ui

在这里插入图片描述

测试工具

可以使用postman进行调试

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值