Browse Source

Add examples/qt

hewei.it 4 years ago
parent
commit
563db4c473

+ 53 - 0
examples/qt/client/HttpClientPage.cpp

@@ -0,0 +1,53 @@
+#include "HttpClientPage.h"
+
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+HttpClientPage::HttpClientPage(QWidget *parent) : QWidget(parent)
+{
+    initUI();
+    initConnect();
+}
+
+HttpClientPage::~HttpClientPage()
+{
+}
+
+void HttpClientPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // method
+    hbox->addWidget(new QLabel("method:"));
+    method = new QComboBox;
+    method->addItems({ "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD" });
+    method->setCurrentText("POST");
+    hbox->addWidget(method);
+
+    // url
+    hbox->addWidget(new QLabel("url:"));
+    urlEdt = new QLineEdit("http://127.0.0.1:8080/echo");
+    hbox->addWidget(urlEdt);
+
+    setLayout(hbox);
+}
+
+void HttpClientPage::initConnect()
+{
+}
+
+int HttpClientPage::send(const QString& msg)
+{
+    requests::Request req(new HttpRequest);
+    req->SetMethod(method->currentText().toStdString().c_str());
+    req->SetUrl(urlEdt->text().toStdString().c_str());
+    req->SetBody(msg.toStdString());
+    return requests::async(req, [](const requests::Response& resp) {
+        if (resp == nullptr) {
+            g_mainwnd->postMessage("request failed!");
+        } else {
+            g_mainwnd->postMessage(QString("received http response:\n") + QString::fromStdString(resp->Dump(true, true)));
+        }
+    });
+}

+ 30 - 0
examples/qt/client/HttpClientPage.h

@@ -0,0 +1,30 @@
+#ifndef HTTP_CLIENT_PAGE_H
+#define HTTP_CLIENT_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QComboBox>
+
+#include "hv/requests.h"
+
+class HttpClientPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit HttpClientPage(QWidget *parent = nullptr);
+    ~HttpClientPage();
+
+    int send(const QString& msg);
+
+protected:
+    void initUI();
+    void initConnect();
+
+private:
+    QComboBox *method;
+    QLineEdit *urlEdt;
+};
+
+#endif // HTTP_CLIENT_PAGE_H

+ 142 - 0
examples/qt/client/TcpClientPage.cpp

@@ -0,0 +1,142 @@
+#include "TcpClientPage.h"
+#include "customevent.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+TcpClientPage::TcpClientPage(QWidget *parent) : QWidget(parent)
+{
+    client = nullptr;
+    initUI();
+    initConnect();
+}
+
+TcpClientPage::~TcpClientPage()
+{
+    close();
+}
+
+void TcpClientPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // host
+    hbox->addWidget(new QLabel("host:"));
+    hostEdt = new QLineEdit("127.0.0.1");
+    hbox->addWidget(hostEdt);
+
+    // port
+    hbox->addWidget(new QLabel("port:"));
+    portEdt = new QLineEdit("1234");
+    hbox->addWidget(portEdt);
+
+    // connect
+    connectBtn = new QPushButton("connect");
+    hbox->addWidget(connectBtn);
+
+    // close
+    closeBtn = new QPushButton("close");
+    closeBtn->setEnabled(false);
+    hbox->addWidget(closeBtn);
+
+    setLayout(hbox);
+}
+
+void TcpClientPage::initConnect()
+{
+    QObject::connect(connectBtn, &QPushButton::clicked, [this]() {
+        std::string host = hostEdt->text().toStdString();
+        int port = portEdt->text().toInt();
+
+        if (connect(port, host.c_str())) {
+            connectBtn->setEnabled(false);
+            closeBtn->setEnabled(true);
+            g_mainwnd->appendMessage(QString::asprintf("TCP client connecting to %s:%d ...", host.c_str(), port));
+        } else {
+            g_mainwnd->appendMessage(QString::asprintf("TCP client connect failed!"));
+        }
+    });
+
+    QObject::connect(closeBtn, &QPushButton::clicked, [this]() {
+        close();
+        connectBtn->setEnabled(true);
+        closeBtn->setEnabled(false);
+        g_mainwnd->appendMessage("TCP client closing ...");
+    });
+}
+
+void TcpClientPage::customEvent(QEvent* e)
+{
+    switch(e->type())
+    {
+    case qEventRecvMsg:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            g_mainwnd->appendMessage(event->message);
+        }
+        e->accept();
+        break;
+    case qEventConnected:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            connectBtn->setEnabled(false);
+            closeBtn->setEnabled(true);
+            g_mainwnd->appendMessage(event->message);
+        }
+        e->accept();
+        break;
+    case qEventDisconnected:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            connectBtn->setEnabled(true);
+            closeBtn->setEnabled(false);
+            g_mainwnd->appendMessage(event->message);
+        }
+        e->accept();
+    break;
+    default:
+        break;
+    }
+}
+
+bool TcpClientPage::connect(int port, const char *host)
+{
+    client = new hv::TcpClient;
+    int connfd = client->createsocket(port, host);
+    if (connfd < 0) {
+        return false;
+    }
+    client->onConnection = [this](const hv::SocketChannelPtr& channel) {
+        QStringEvent* event;
+        if (channel->isConnected()) {
+            event = new QStringEvent(QString::asprintf("TCP client connected! connfd=%d", channel->fd()), qEventConnected);
+        } else {
+            event = new QStringEvent(QString::asprintf("TCP client disconnected! connfd=%d", channel->fd()), qEventDisconnected);
+        }
+        QApplication::postEvent(this, event);
+    };
+    client->onMessage = [](const hv::SocketChannelPtr& channel, hv::Buffer* buf) {
+        g_mainwnd->postMessage(QString::asprintf("< %.*s", (int)buf->size(), (char*)buf->data()));
+    };
+    client->start();
+    return true;
+}
+
+void TcpClientPage::close()
+{
+    if (client) {
+        client->stop(true);
+    }
+    SAFE_DELETE(client);
+}
+
+int TcpClientPage::send(const QString& msg)
+{
+    if (client == nullptr || !client->isConnected()) {
+        g_mainwnd->postMessage("Please connect first!");
+        return -1;
+    }
+    return client->send(msg.toStdString());
+}

+ 36 - 0
examples/qt/client/TcpClientPage.h

@@ -0,0 +1,36 @@
+#ifndef TCP_CLIENT_PAGE_H
+#define TCP_CLIENT_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "hv/TcpClient.h"
+
+class TcpClientPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit TcpClientPage(QWidget *parent = nullptr);
+    ~TcpClientPage();
+
+    bool connect(int port, const char* host = "127.0.0.1");
+    void close();
+    int send(const QString& msg);
+
+protected:
+    void initUI();
+    void initConnect();
+    virtual void customEvent(QEvent* e);
+
+private:
+    QLineEdit *hostEdt;
+    QLineEdit *portEdt;
+    QPushButton *connectBtn;
+    QPushButton *closeBtn;
+
+    hv::TcpClient *client;
+};
+
+#endif // TCP_CLIENT_PAGE_H

+ 94 - 0
examples/qt/client/UdpClientPage.cpp

@@ -0,0 +1,94 @@
+#include "UdpClientPage.h"
+
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+UdpClientPage::UdpClientPage(QWidget *parent) : QWidget(parent)
+{
+    client = nullptr;
+    initUI();
+    initConnect();
+}
+
+UdpClientPage::~UdpClientPage()
+{
+    stop();
+}
+
+void UdpClientPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // host
+    hbox->addWidget(new QLabel("host:"));
+    hostEdt = new QLineEdit("127.0.0.1");
+    hbox->addWidget(hostEdt);
+
+    // port
+    hbox->addWidget(new QLabel("port:"));
+    portEdt = new QLineEdit("1234");
+    hbox->addWidget(portEdt);
+
+    // start
+    startBtn = new QPushButton("start");
+    hbox->addWidget(startBtn);
+
+    // stop
+    stopBtn = new QPushButton("stop");
+    stopBtn->setEnabled(false);
+    hbox->addWidget(stopBtn);
+
+    setLayout(hbox);
+}
+
+void UdpClientPage::initConnect()
+{
+    connect(startBtn, &QPushButton::clicked, [this]() {
+        std::string host = hostEdt->text().toStdString();
+        int port = portEdt->text().toInt();
+
+        if (start(port, host.c_str())) {
+            startBtn->setEnabled(false);
+            stopBtn->setEnabled(true);
+            g_mainwnd->appendMessage(QString::asprintf("UDP client sendto %s:%d ...", host.c_str(), port));
+        } else {
+            g_mainwnd->appendMessage(QString::asprintf("UDP client start failed!"));
+        }
+    });
+
+    connect(stopBtn, &QPushButton::clicked, [this]() {
+        stop();
+        startBtn->setEnabled(true);
+        stopBtn->setEnabled(false);
+        g_mainwnd->appendMessage("UDP client stopped!");
+    });
+}
+
+bool UdpClientPage::start(int port, const char* host)
+{
+    client = new hv::UdpClient;
+    int sockfd = client->createsocket(port, host);
+    if (sockfd < 0) {
+        return false;
+    }
+    client->onMessage = [](const hv::SocketChannelPtr& channel, hv::Buffer* buf) {
+        g_mainwnd->postMessage(QString::asprintf("< %.*s", (int)buf->size(), (char*)buf->data()));
+    };
+    client->start();
+    return true;
+}
+
+void UdpClientPage::stop()
+{
+    SAFE_DELETE(client);
+}
+
+int UdpClientPage::send(const QString& msg)
+{
+    if (client == nullptr) {
+        g_mainwnd->postMessage("Please start first!");
+        return -1;
+    }
+    return client->sendto(msg.toStdString());
+}

+ 35 - 0
examples/qt/client/UdpClientPage.h

@@ -0,0 +1,35 @@
+#ifndef UDP_CLIENT_PAGE_H
+#define UDP_CLIENT_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "hv/UdpClient.h"
+
+class UdpClientPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit UdpClientPage(QWidget *parent = nullptr);
+    ~UdpClientPage();
+
+    bool start(int port, const char* host = "127.0.0.1");
+    void stop();
+    int send(const QString& msg);
+
+protected:
+    void initUI();
+    void initConnect();
+
+private:
+    QLineEdit *hostEdt;
+    QLineEdit *portEdt;
+    QPushButton *startBtn;
+    QPushButton *stopBtn;
+
+    hv::UdpClient *client;
+};
+
+#endif // UDP_CLIENT_PAGE_H

+ 127 - 0
examples/qt/client/WebSocketClientPage.cpp

@@ -0,0 +1,127 @@
+#include "WebSocketClientPage.h"
+#include "customevent.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+WebSocketClientPage::WebSocketClientPage(QWidget *parent) : QWidget(parent)
+{
+    client = nullptr;
+    initUI();
+    initConnect();
+}
+
+WebSocketClientPage::~WebSocketClientPage()
+{
+    close();
+}
+
+void WebSocketClientPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // url
+    hbox->addWidget(new QLabel("url:"));
+    urlEdt = new QLineEdit("ws://127.0.0.1:8080/echo");
+    hbox->addWidget(urlEdt);
+
+    // open
+    openBtn = new QPushButton("open");
+    hbox->addWidget(openBtn);
+
+    // close
+    closeBtn = new QPushButton("close");
+    closeBtn->setEnabled(false);
+    hbox->addWidget(closeBtn);
+
+    setLayout(hbox);
+}
+
+void WebSocketClientPage::initConnect()
+{
+    connect(openBtn, &QPushButton::clicked, [this]() {
+        std::string url = urlEdt->text().toStdString();
+
+        if (open(url.c_str())) {
+            openBtn->setEnabled(false);
+            closeBtn->setEnabled(true);
+            g_mainwnd->appendMessage(QString::asprintf("WS client openning to %s ...", url.c_str()));
+        } else {
+            g_mainwnd->appendMessage(QString::asprintf("WS client open failed!"));
+        }
+    });
+
+    connect(closeBtn, &QPushButton::clicked, [this]() {
+        close();
+        openBtn->setEnabled(true);
+        closeBtn->setEnabled(false);
+        g_mainwnd->appendMessage("WS client closing ...");
+    });
+}
+
+void WebSocketClientPage::customEvent(QEvent* e)
+{
+    switch(e->type())
+    {
+    case qEventRecvMsg:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            g_mainwnd->appendMessage(event->message);
+        }
+        e->accept();
+        break;
+    case qEventOpened:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            openBtn->setEnabled(false);
+            closeBtn->setEnabled(true);
+            g_mainwnd->appendMessage(event->message);
+        }
+        e->accept();
+        break;
+    case qEventClosed:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            openBtn->setEnabled(true);
+            closeBtn->setEnabled(false);
+            g_mainwnd->appendMessage(event->message);
+        }
+        e->accept();
+    break;
+    default:
+        break;
+    }
+}
+
+bool WebSocketClientPage::open(const char* url)
+{
+    client = new hv::WebSocketClient;
+    client->onopen = [this]() {
+        QStringEvent* event = new QStringEvent("WS client opened!", qEventOpened);
+        QApplication::postEvent(this, event);
+    };
+    client->onclose = [this]() {
+        QStringEvent* event = new QStringEvent("WS client closed!", qEventClosed);
+        QApplication::postEvent(this, event);
+    };
+    client->onmessage = [](const std::string& msg) {
+        g_mainwnd->postMessage(QString("< ") + QString::fromStdString(msg));
+    };
+    return client->open(url) == 0;
+}
+
+void WebSocketClientPage::close()
+{
+    SAFE_DELETE(client);
+}
+
+int WebSocketClientPage::send(const QString& msg)
+{
+    if (client == nullptr || !client->isConnected()) {
+        g_mainwnd->postMessage("Please open first!");
+        return -1;
+    }
+    return client->send(msg.toStdString());
+}

+ 35 - 0
examples/qt/client/WebSocketClientPage.h

@@ -0,0 +1,35 @@
+#ifndef WEB_SOCKET_CLIENT_PAGE_H
+#define WEB_SOCKET_CLIENT_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "hv/WebSocketClient.h"
+
+class WebSocketClientPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit WebSocketClientPage(QWidget *parent = nullptr);
+    ~WebSocketClientPage();
+
+    bool open(const char* url);
+    void close();
+    int send(const QString& msg);
+
+protected:
+    void initUI();
+    void initConnect();
+    virtual void customEvent(QEvent* e);
+
+private:
+    QLineEdit *urlEdt;
+    QPushButton *openBtn;
+    QPushButton *closeBtn;
+
+    hv::WebSocketClient *client;
+};
+
+#endif // WEB_SOCKET_CLIENT_PAGE_H

+ 49 - 0
examples/qt/client/client.pro

@@ -0,0 +1,49 @@
+QT       += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+    main.cpp \
+    mainwindow.cpp \
+    TcpClientPage.cpp \
+    UdpClientPage.cpp \
+    HttpClientPage.cpp \
+    WebSocketClientPage.cpp
+
+HEADERS += \
+    customevent.h \
+    mainwindow.h \
+    TcpClientPage.h \
+    UdpClientPage.h \
+    HttpClientPage.h \
+    WebSocketClientPage.h
+
+win32 {
+    # INCLUDEPATH += C:\libhv\include
+    # LIBS += -LC:\libhv\lib -lhv
+
+    INCLUDEPATH += ../../../build/mingw64/include
+    LIBS += -L../../../build/mingw64/lib -lhv
+    LIBS += -lws2_32
+}
+
+unix {
+    # INCLUDEPATH += /usr/local/include
+    # LIBS += -L/usr/local/lib -lhv
+
+    INCLUDEPATH += ../../../include
+    LIBS += -L../../../lib -lhv
+}

+ 22 - 0
examples/qt/client/customevent.h

@@ -0,0 +1,22 @@
+#ifndef CUSTOM_EVENT_H
+#define CUSTOM_EVENT_H
+
+#include <QEvent>
+#include <QString>
+
+const static QEvent::Type qEventRecvMsg         = (QEvent::Type)(QEvent::User + 1);
+const static QEvent::Type qEventConnected       = (QEvent::Type)(QEvent::User + 2);
+const static QEvent::Type qEventDisconnected    = (QEvent::Type)(QEvent::User + 3);
+const static QEvent::Type qEventOpened          = (QEvent::Type)(QEvent::User + 4);
+const static QEvent::Type qEventClosed          = (QEvent::Type)(QEvent::User + 5);
+
+class QStringEvent : public QEvent {
+public:
+    QStringEvent(const QString& msg = "", QEvent::Type type = qEventRecvMsg) : QEvent(type)
+    {
+        message = msg;
+    }
+    QString message;
+};
+
+#endif // CUSTOM_EVENT_H

+ 10 - 0
examples/qt/client/main.cpp

@@ -0,0 +1,10 @@
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    g_mainwnd->show();
+    return a.exec();
+}

+ 141 - 0
examples/qt/client/mainwindow.cpp

@@ -0,0 +1,141 @@
+#include "mainwindow.h"
+#include "customevent.h"
+
+#include <QApplication>
+#include <QDateTime>
+
+#include <QBoxLayout>
+
+#include "TcpClientPage.h"
+#include "UdpClientPage.h"
+#include "HttpClientPage.h"
+#include "WebSocketClientPage.h"
+
+SINGLETON_IMPL(MainWindow)
+
+MainWindow::MainWindow(QWidget *parent)
+    : QMainWindow(parent)
+{
+    initUI();
+    initConnect();
+}
+
+MainWindow::~MainWindow()
+{
+}
+
+void MainWindow::initUI()
+{
+    initMenu();
+
+    setFixedSize(640, 480);
+
+    QVBoxLayout* vbox = new QVBoxLayout();
+
+    tab = new QTabWidget;
+    tab->addTab(new TcpClientPage, "TCP");
+    tab->addTab(new UdpClientPage, "UDP");
+    tab->addTab(new HttpClientPage, "HTTP");
+    tab->addTab(new WebSocketClientPage, "WebSocket");
+    vbox->addWidget(tab);
+
+    QHBoxLayout* hbox = new QHBoxLayout();
+    sendmsg = new QTextEdit("hello");
+    sendmsg->setReadOnly(false);
+    hbox->addWidget(sendmsg);
+    sendbtn = new QPushButton("send");
+    hbox->addWidget(sendbtn);
+    vbox->addLayout(hbox);
+
+    recvmsg = new QTextEdit();
+    recvmsg->setReadOnly(true);
+    vbox->addWidget(recvmsg);
+
+    center = new QWidget;
+    center->setLayout(vbox);
+
+    setCentralWidget(center);
+}
+
+void MainWindow::initMenu()
+{
+
+}
+
+void MainWindow::initConnect()
+{
+    connect(sendbtn, &QPushButton::clicked, [this]() {
+        QWidget* page = tab->currentWidget();
+        QString msg = sendmsg->toPlainText();
+        switch (tab->currentIndex()) {
+        case 0:
+            {
+                TcpClientPage *client = dynamic_cast<TcpClientPage*>(page);
+                client->send(msg);
+            }
+            break;
+        case 1:
+            {
+                UdpClientPage *client = dynamic_cast<UdpClientPage*>(page);
+                client->send(msg);
+            }
+            break;
+        case 2:
+            {
+                HttpClientPage *client = dynamic_cast<HttpClientPage*>(page);
+                client->send(msg);
+            }
+            break;
+        case 3:
+            {
+                WebSocketClientPage *client = dynamic_cast<WebSocketClientPage*>(page);
+                client->send(msg);
+            }
+            break;
+        default:
+            break;
+        }
+    });
+}
+
+void MainWindow::postMessage(const QString &msg)
+{
+    QStringEvent* event = new QStringEvent(msg);
+    QApplication::postEvent(this, event);
+}
+
+void MainWindow::appendMessage(const QString& msg)
+{
+    QString text = recvmsg->toPlainText();
+    text += QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss.zzz] ");
+    text += msg;
+    if (text.back() != '\n') {
+        text += "\n";
+    }
+    showMessage(text);
+}
+
+void MainWindow::showMessage(const QString& msg)
+{
+    recvmsg->setText(msg);
+    QTextCursor cursor = recvmsg->textCursor();
+    cursor.movePosition(QTextCursor::End);
+    recvmsg->setTextCursor(cursor);
+    recvmsg->repaint();
+}
+
+void MainWindow::customEvent(QEvent* e)
+{
+    switch(e->type())
+    {
+    case qEventRecvMsg:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            appendMessage(event->message);
+        }
+        e->accept();
+        break;
+    default:
+        break;
+    }
+}

+ 46 - 0
examples/qt/client/mainwindow.h

@@ -0,0 +1,46 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QTextEdit>
+#include <QPushButton>
+
+#include <QTabWidget>
+#include <QListWidget>
+
+#include "hv/singleton.h"
+
+class MainWindow : public QMainWindow
+{
+    SINGLETON_DECL(MainWindow)
+    Q_OBJECT
+
+public:
+    MainWindow(QWidget *parent = nullptr);
+    ~MainWindow();
+
+    void initUI();
+    void initMenu();
+    void initConnect();
+
+    void postMessage(const QString& msg);
+    void appendMessage(const QString& msg);
+    void showMessage(const QString& msg);
+
+protected:
+    virtual void customEvent(QEvent* e);
+
+private:
+    QWidget *center;
+    QTabWidget *tab;
+    QTextEdit *sendmsg;
+    QPushButton *sendbtn;
+    QTextEdit *recvmsg;
+};
+
+#define g_mainwnd MainWindow::instance()
+
+#endif // MAINWINDOW_H

+ 115 - 0
examples/qt/server/HttpServerPage.cpp

@@ -0,0 +1,115 @@
+#include "HttpServerPage.h"
+
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+HttpServerPage::HttpServerPage(QWidget *parent) : QWidget(parent)
+{
+    server = nullptr;
+    service = nullptr;
+    ws = nullptr;
+    initUI();
+    initConnect();
+}
+
+HttpServerPage::~HttpServerPage()
+{
+    stop();
+}
+
+void HttpServerPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // host
+    hbox->addWidget(new QLabel("host:"));
+    hostEdt = new QLineEdit("0.0.0.0");
+    hbox->addWidget(hostEdt);
+
+    // port
+    hbox->addWidget(new QLabel("port:"));
+    portEdt = new QLineEdit("8080");
+    hbox->addWidget(portEdt);
+
+    // start
+    startBtn = new QPushButton("start");
+    hbox->addWidget(startBtn);
+
+    // stop
+    stopBtn = new QPushButton("stop");
+    stopBtn->setEnabled(false);
+    hbox->addWidget(stopBtn);
+
+    setLayout(hbox);
+}
+
+void HttpServerPage::initConnect()
+{
+    connect(startBtn, &QPushButton::clicked, [this]() {
+        std::string host = hostEdt->text().toStdString();
+        int port = portEdt->text().toInt();
+
+        if (start(port, host.c_str())) {
+            startBtn->setEnabled(false);
+            stopBtn->setEnabled(true);
+            g_mainwnd->appendMessage(QString::asprintf("HTTP server running on %s:%d ...", host.c_str(), port));
+        } else {
+            g_mainwnd->appendMessage(QString::asprintf("HTTP server start failed!"));
+        }
+    });
+
+    connect(stopBtn, &QPushButton::clicked, [this]() {
+        stop();
+        startBtn->setEnabled(true);
+        stopBtn->setEnabled(false);
+        g_mainwnd->appendMessage("HTTP server stopped!");
+    });
+}
+
+bool HttpServerPage::start(int port, const char* host)
+{
+    service = new hv::HttpService;
+    service->document_root = ".";
+    service->home_page = "index.html";
+    service->preprocessor = [](HttpRequest* req, HttpResponse* resp) {
+        g_mainwnd->postMessage(QString("received http request:\n") + QString::fromStdString(req->Dump(true, true)));
+        return 0;
+    };
+    service->POST("/echo", [](const HttpContextPtr& ctx) {
+        // echo
+        return ctx->send(ctx->body(), ctx->type());
+    });
+    service->postprocessor = [](HttpRequest* req, HttpResponse* resp) {
+        g_mainwnd->postMessage(QString("send http response:\n") + QString::fromStdString(resp->Dump(true, true)));
+        return 0;
+    };
+
+    ws = new hv::WebSocketService;
+    ws->onopen = [](const WebSocketChannelPtr& channel, const std::string& url) {
+        g_mainwnd->postMessage(QString("ws onopen: ") + QString::fromStdString(url));
+    };
+    ws->onmessage = [](const WebSocketChannelPtr& channel, const std::string& msg) {
+        g_mainwnd->postMessage(QString("ws onmessage: ") + QString::fromStdString(msg));
+        // echo
+        channel->send(msg);
+    };
+    ws->onclose = [](const WebSocketChannelPtr& channel) {
+        g_mainwnd->postMessage("ws onclose");
+    };
+
+    server = new hv::WebSocketServer;
+    server->registerHttpService(service);
+    server->registerWebSocketService(ws);
+    server->setHost(host);
+    server->setPort(port);
+    server->setThreadNum(1);
+    return server->start() == 0;
+}
+
+void HttpServerPage::stop()
+{
+    SAFE_DELETE(server);
+    SAFE_DELETE(service);
+    SAFE_DELETE(ws);
+}

+ 38 - 0
examples/qt/server/HttpServerPage.h

@@ -0,0 +1,38 @@
+#ifndef HTTP_SERVER_PAGE_H
+#define HTTP_SERVER_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "hv/HttpServer.h"
+#include "hv/WebSocketServer.h"
+
+class HttpServerPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit HttpServerPage(QWidget *parent = nullptr);
+    ~HttpServerPage();
+
+    bool start(int port, const char* host = "0.0.0.0");
+    void stop();
+
+protected:
+    void initUI();
+    void initConnect();
+
+private:
+    QLineEdit *hostEdt;
+    QLineEdit *portEdt;
+    QPushButton *startBtn;
+    QPushButton *stopBtn;
+
+    // hv::HttpServer *server;
+    hv::WebSocketServer *server;
+    hv::HttpService *service;
+    hv::WebSocketService *ws;
+};
+
+#endif // HTTP_SERVER_PAGE_H

+ 96 - 0
examples/qt/server/TcpServerPage.cpp

@@ -0,0 +1,96 @@
+#include "TcpServerPage.h"
+
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+TcpServerPage::TcpServerPage(QWidget *parent) : QWidget(parent)
+{
+    server = nullptr;
+    initUI();
+    initConnect();
+}
+
+TcpServerPage::~TcpServerPage()
+{
+    stop();
+}
+
+void TcpServerPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // host
+    hbox->addWidget(new QLabel("host:"));
+    hostEdt = new QLineEdit("0.0.0.0");
+    hbox->addWidget(hostEdt);
+
+    // port
+    hbox->addWidget(new QLabel("port:"));
+    portEdt = new QLineEdit("1234");
+    hbox->addWidget(portEdt);
+
+    // start
+    startBtn = new QPushButton("start");
+    hbox->addWidget(startBtn);
+
+    // stop
+    stopBtn = new QPushButton("stop");
+    stopBtn->setEnabled(false);
+    hbox->addWidget(stopBtn);
+
+    setLayout(hbox);
+}
+
+void TcpServerPage::initConnect()
+{
+    connect(startBtn, &QPushButton::clicked, [this]() {
+        std::string host = hostEdt->text().toStdString();
+        int port = portEdt->text().toInt();
+
+        if (start(port, host.c_str())) {
+            startBtn->setEnabled(false);
+            stopBtn->setEnabled(true);
+            g_mainwnd->appendMessage(QString::asprintf("TCP server running on %s:%d ...", host.c_str(), port));
+        } else {
+            g_mainwnd->appendMessage(QString::asprintf("TCP server start failed!"));
+        }
+    });
+
+    connect(stopBtn, &QPushButton::clicked, [this]() {
+        stop();
+        startBtn->setEnabled(true);
+        stopBtn->setEnabled(false);
+        g_mainwnd->appendMessage("TCP server stopped!");
+    });
+}
+
+bool TcpServerPage::start(int port, const char* host)
+{
+    server = new hv::TcpServer;
+    int listenfd = server->createsocket(port, host);
+    if (listenfd < 0) {
+        return false;
+    }
+    server->setThreadNum(1);
+    server->onConnection = [](const hv::SocketChannelPtr& channel) {
+        std::string peeraddr = channel->peeraddr();
+        if (channel->isConnected()) {
+            g_mainwnd->postMessage(QString::asprintf("%s connected! connfd=%d", peeraddr.c_str(), channel->fd()));
+        } else {
+            g_mainwnd->postMessage(QString::asprintf("%s disconnected! connfd=%d", peeraddr.c_str(), channel->fd()));
+        }
+    };
+    server->onMessage = [](const hv::SocketChannelPtr& channel, hv::Buffer* buf) {
+        g_mainwnd->postMessage(QString::asprintf("< %.*s", (int)buf->size(), (char*)buf->data()));
+        // echo
+        channel->write(buf);
+    };
+    server->start();
+    return true;
+}
+
+void TcpServerPage::stop()
+{
+    SAFE_DELETE(server);
+}

+ 34 - 0
examples/qt/server/TcpServerPage.h

@@ -0,0 +1,34 @@
+#ifndef TCP_SERVER_PAGE_H
+#define TCP_SERVER_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "hv/TcpServer.h"
+
+class TcpServerPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit TcpServerPage(QWidget *parent = nullptr);
+    ~TcpServerPage();
+
+    bool start(int port, const char* host = "0.0.0.0");
+    void stop();
+
+protected:
+    void initUI();
+    void initConnect();
+
+private:
+    QLineEdit *hostEdt;
+    QLineEdit *portEdt;
+    QPushButton *startBtn;
+    QPushButton *stopBtn;
+
+    hv::TcpServer *server;
+};
+
+#endif // TCP_SERVER_PAGE_H

+ 87 - 0
examples/qt/server/UdpServerPage.cpp

@@ -0,0 +1,87 @@
+#include "UdpServerPage.h"
+
+#include <QBoxLayout>
+
+#include "mainwindow.h"
+
+UdpServerPage::UdpServerPage(QWidget *parent) : QWidget(parent)
+{
+    server = nullptr;
+    initUI();
+    initConnect();
+}
+
+UdpServerPage::~UdpServerPage()
+{
+    stop();
+}
+
+void UdpServerPage::initUI()
+{
+    QHBoxLayout* hbox = new QHBoxLayout;
+
+    // host
+    hbox->addWidget(new QLabel("host:"));
+    hostEdt = new QLineEdit("0.0.0.0");
+    hbox->addWidget(hostEdt);
+
+    // port
+    hbox->addWidget(new QLabel("port:"));
+    portEdt = new QLineEdit("1234");
+    hbox->addWidget(portEdt);
+
+    // start
+    startBtn = new QPushButton("start");
+    hbox->addWidget(startBtn);
+
+    // stop
+    stopBtn = new QPushButton("stop");
+    stopBtn->setEnabled(false);
+    hbox->addWidget(stopBtn);
+
+    setLayout(hbox);
+}
+
+void UdpServerPage::initConnect()
+{
+    connect(startBtn, &QPushButton::clicked, [this]() {
+        std::string host = hostEdt->text().toStdString();
+        int port = portEdt->text().toInt();
+
+        if (start(port, host.c_str())) {
+            startBtn->setEnabled(false);
+            stopBtn->setEnabled(true);
+            g_mainwnd->appendMessage(QString::asprintf("UDP server running on %s:%d ...", host.c_str(), port));
+        } else {
+            g_mainwnd->appendMessage(QString::asprintf("UDP server start failed!"));
+        }
+    });
+
+    connect(stopBtn, &QPushButton::clicked, [this]() {
+        stop();
+        startBtn->setEnabled(true);
+        stopBtn->setEnabled(false);
+        g_mainwnd->appendMessage("UDP server stopped!");
+    });
+}
+
+bool UdpServerPage::start(int port, const char* host)
+{
+    server = new hv::UdpServer;
+    int bindfd = server->createsocket(port, host);
+    if (bindfd < 0) {
+        return false;
+    }
+    server->onMessage = [](const hv::SocketChannelPtr& channel, hv::Buffer* buf) {
+        g_mainwnd->postMessage(QString::asprintf("< %.*s", (int)buf->size(), (char*)buf->data()));
+        // echo
+        channel->write(buf);
+    };
+    server->start();
+    return true;
+}
+
+void UdpServerPage::stop()
+{
+    SAFE_DELETE(server);
+}

+ 34 - 0
examples/qt/server/UdpServerPage.h

@@ -0,0 +1,34 @@
+#ifndef UDP_SERVER_PAGE_H
+#define UDP_SERVER_PAGE_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "hv/UdpServer.h"
+
+class UdpServerPage : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit UdpServerPage(QWidget *parent = nullptr);
+    ~UdpServerPage();
+
+    bool start(int port, const char* host = "0.0.0.0");
+    void stop();
+
+protected:
+    void initUI();
+    void initConnect();
+
+private:
+    QLineEdit *hostEdt;
+    QLineEdit *portEdt;
+    QPushButton *startBtn;
+    QPushButton *stopBtn;
+
+    hv::UdpServer *server;
+};
+
+#endif // UDP_SERVER_PAGE_H

+ 18 - 0
examples/qt/server/customevent.h

@@ -0,0 +1,18 @@
+#ifndef CUSTOM_EVENT_H
+#define CUSTOM_EVENT_H
+
+#include <QEvent>
+#include <QString>
+
+const static QEvent::Type qEventRecvMsg = (QEvent::Type)(QEvent::User + 1);
+
+class QStringEvent : public QEvent {
+public:
+    QStringEvent(const QString& msg = "", QEvent::Type type = qEventRecvMsg) : QEvent(type)
+    {
+        message = msg;
+    }
+    QString message;
+};
+
+#endif // CUSTOM_EVENT_H

+ 10 - 0
examples/qt/server/main.cpp

@@ -0,0 +1,10 @@
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    g_mainwnd->show();
+    return a.exec();
+}

+ 99 - 0
examples/qt/server/mainwindow.cpp

@@ -0,0 +1,99 @@
+#include "mainwindow.h"
+#include "customevent.h"
+
+#include <QApplication>
+#include <QDateTime>
+
+#include <QBoxLayout>
+
+#include "TcpServerPage.h"
+#include "UdpServerPage.h"
+#include "HttpServerPage.h"
+
+SINGLETON_IMPL(MainWindow)
+
+MainWindow::MainWindow(QWidget *parent)
+    : QMainWindow(parent)
+{
+    initUI();
+    initConnect();
+}
+
+MainWindow::~MainWindow()
+{
+}
+
+void MainWindow::initUI()
+{
+    initMenu();
+
+    setFixedSize(640, 480);
+
+    QVBoxLayout* vbox = new QVBoxLayout();
+
+    tab = new QTabWidget;
+    tab->addTab(new TcpServerPage, "TCP");
+    tab->addTab(new UdpServerPage, "UDP");
+    tab->addTab(new HttpServerPage, "HTTP | WebSocket");
+    vbox->addWidget(tab);
+
+    recvmsg = new QTextEdit();
+    recvmsg->setReadOnly(true);
+    vbox->addWidget(recvmsg);
+
+    center = new QWidget;
+    center->setLayout(vbox);
+
+    setCentralWidget(center);
+}
+
+void MainWindow::initMenu()
+{
+
+}
+
+void MainWindow::initConnect()
+{
+}
+
+void MainWindow::postMessage(const QString &msg)
+{
+    QStringEvent* event = new QStringEvent(msg);
+    QApplication::postEvent(this, event);
+}
+
+void MainWindow::appendMessage(const QString& msg)
+{
+    QString text = recvmsg->toPlainText();
+    text += QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss.zzz] ");
+    text += msg;
+    if (text.back() != '\n') {
+        text += "\n";
+    }
+    showMessage(text);
+}
+
+void MainWindow::showMessage(const QString& msg)
+{
+    recvmsg->setText(msg);
+    QTextCursor cursor = recvmsg->textCursor();
+    cursor.movePosition(QTextCursor::End);
+    recvmsg->setTextCursor(cursor);
+    recvmsg->repaint();
+}
+
+void MainWindow::customEvent(QEvent* e)
+{
+    switch(e->type())
+    {
+    case qEventRecvMsg:
+        {
+            QStringEvent* event = dynamic_cast<QStringEvent*>(e);
+            appendMessage(event->message);
+        }
+        e->accept();
+        break;
+    default:
+        break;
+    }
+}

+ 44 - 0
examples/qt/server/mainwindow.h

@@ -0,0 +1,44 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QWidget>
+#include <QLabel>
+#include <QLineEdit>
+#include <QTextEdit>
+#include <QPushButton>
+
+#include <QTabWidget>
+#include <QListWidget>
+
+#include "hv/singleton.h"
+
+class MainWindow : public QMainWindow
+{
+    SINGLETON_DECL(MainWindow)
+    Q_OBJECT
+
+public:
+    MainWindow(QWidget *parent = nullptr);
+    ~MainWindow();
+
+    void initUI();
+    void initMenu();
+    void initConnect();
+
+    void postMessage(const QString& msg);
+    void appendMessage(const QString& msg);
+    void showMessage(const QString& msg);
+
+protected:
+    virtual void customEvent(QEvent* e);
+
+private:
+    QWidget *center;
+    QTabWidget *tab;
+    QTextEdit *recvmsg;
+};
+
+#define g_mainwnd MainWindow::instance()
+
+#endif // MAINWINDOW_H

+ 47 - 0
examples/qt/server/server.pro

@@ -0,0 +1,47 @@
+QT       += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+    main.cpp \
+    mainwindow.cpp \
+    TcpServerPage.cpp \
+    UdpServerPage.cpp \
+    HttpServerPage.cpp
+
+HEADERS += \
+    customevent.h \
+    mainwindow.h \
+    TcpServerPage.h \
+    UdpServerPage.h \
+    HttpServerPage.h
+
+win32 {
+    # INCLUDEPATH += C:\libhv\include
+    # LIBS += -LC:\libhv\lib -lhv
+
+    INCLUDEPATH += ../../../build/mingw64/include
+    LIBS += -L../../../build/mingw64/lib -lhv
+    LIBS += -lws2_32
+}
+
+unix {
+    # INCLUDEPATH += /usr/local/include
+    # LIBS += -L/usr/local/lib -lhv
+
+    INCLUDEPATH += ../../../include
+    LIBS += -L../../../lib -lhv
+}