From 8a7c4d2990e1d3d0c9f2702806347bb1dc20a7eb Mon Sep 17 00:00:00 2001
From: "daragan.andrey@demlabs.net" <daragan.andrey@demlabs.net>
Date: Fri, 3 May 2019 19:32:08 +0300
Subject: [PATCH] [+] Added DapRPCProtocol files.

---
 DapRPCProtocol/DapRPCProtocol.pri             |  25 ++
 DapRPCProtocol/DapRpcAbstractServer.cpp       |  29 ++
 DapRPCProtocol/DapRpcAbstractServer.h         |  31 ++
 DapRPCProtocol/DapRpcLocalServer.cpp          | 108 +++++
 DapRPCProtocol/DapRpcLocalServer.h            |  44 +++
 DapRPCProtocol/DapRpcMessage.cpp              | 373 ++++++++++++++++++
 DapRPCProtocol/DapRpcMessage.h                |  96 +++++
 DapRPCProtocol/DapRpcService.cpp              | 340 ++++++++++++++++
 DapRPCProtocol/DapRpcService.h                |  72 ++++
 DapRPCProtocol/DapRpcServiceProvider.cpp      |  93 +++++
 DapRPCProtocol/DapRpcServiceProvider.h        |  29 ++
 DapRPCProtocol/DapRpcServiceReply.cpp         |  31 ++
 DapRPCProtocol/DapRpcServiceReply.h           |  30 ++
 DapRPCProtocol/DapRpcServiceRequest.cpp       |  69 ++++
 DapRPCProtocol/DapRpcServiceRequest.h         |  31 ++
 DapRPCProtocol/DapRpcSocket.cpp               | 262 ++++++++++++
 DapRPCProtocol/DapRpcSocket.h                 |  74 ++++
 DapRPCProtocol/DapRpcTCPServer.cpp            | 110 ++++++
 DapRPCProtocol/DapRpcTCPServer.h              |  44 +++
 KelvinDashboardGUI/qml.qrc                    |   4 +-
 .../Resources/Icons/iconErrorNetwork.png      | Bin 0 -> 2548 bytes
 .../Resources/Icons/iconNetwork.png           | Bin 0 -> 2300 bytes
 22 files changed, 1893 insertions(+), 2 deletions(-)
 create mode 100644 DapRPCProtocol/DapRPCProtocol.pri
 create mode 100644 DapRPCProtocol/DapRpcAbstractServer.cpp
 create mode 100644 DapRPCProtocol/DapRpcAbstractServer.h
 create mode 100644 DapRPCProtocol/DapRpcLocalServer.cpp
 create mode 100644 DapRPCProtocol/DapRpcLocalServer.h
 create mode 100644 DapRPCProtocol/DapRpcMessage.cpp
 create mode 100644 DapRPCProtocol/DapRpcMessage.h
 create mode 100644 DapRPCProtocol/DapRpcService.cpp
 create mode 100644 DapRPCProtocol/DapRpcService.h
 create mode 100644 DapRPCProtocol/DapRpcServiceProvider.cpp
 create mode 100644 DapRPCProtocol/DapRpcServiceProvider.h
 create mode 100644 DapRPCProtocol/DapRpcServiceReply.cpp
 create mode 100644 DapRPCProtocol/DapRpcServiceReply.h
 create mode 100644 DapRPCProtocol/DapRpcServiceRequest.cpp
 create mode 100644 DapRPCProtocol/DapRpcServiceRequest.h
 create mode 100644 DapRPCProtocol/DapRpcSocket.cpp
 create mode 100644 DapRPCProtocol/DapRpcSocket.h
 create mode 100644 DapRPCProtocol/DapRpcTCPServer.cpp
 create mode 100644 DapRPCProtocol/DapRpcTCPServer.h
 create mode 100644 KelvinDashboardService/Resources/Icons/iconErrorNetwork.png
 create mode 100644 KelvinDashboardService/Resources/Icons/iconNetwork.png

diff --git a/DapRPCProtocol/DapRPCProtocol.pri b/DapRPCProtocol/DapRPCProtocol.pri
new file mode 100644
index 0000000..c6899ff
--- /dev/null
+++ b/DapRPCProtocol/DapRPCProtocol.pri
@@ -0,0 +1,25 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+    $$PWD/DapRpcAbstractServer.h \
+    $$PWD/DapRpcLocalServer.h \
+    $$PWD/DapRpcMessage.h \
+    $$PWD/DapRpcService.h \
+    $$PWD/DapRpcServiceProvider.h \
+    $$PWD/DapRpcServiceReply.h \
+    $$PWD/DapRpcServiceRequest.h \
+    $$PWD/DapRpcSocket.h \
+    $$PWD/DapRpcTCPServer.h
+
+SOURCES += \
+    $$PWD/DapRpcAbstractServer.cpp \
+    $$PWD/DapRpcLocalServer.cpp \
+    $$PWD/DapRpcMessage.cpp \
+    $$PWD/DapRpcService.cpp \
+    $$PWD/DapRpcServiceProvider.cpp \
+    $$PWD/DapRpcServiceReply.cpp \
+    $$PWD/DapRpcServiceRequest.cpp \
+    $$PWD/DapRpcSocket.cpp \
+    $$PWD/DapRpcTCPServer.cpp
+
+
diff --git a/DapRPCProtocol/DapRpcAbstractServer.cpp b/DapRPCProtocol/DapRpcAbstractServer.cpp
new file mode 100644
index 0000000..7a8882d
--- /dev/null
+++ b/DapRPCProtocol/DapRpcAbstractServer.cpp
@@ -0,0 +1,29 @@
+#include "DapRpcAbstractServer.h"
+
+DapRpcAbstractServer::DapRpcAbstractServer()
+{
+
+}
+
+DapRpcAbstractServer::~DapRpcAbstractServer()
+{
+
+}
+
+int DapRpcAbstractServer::connectedClientCount() const
+{
+    return m_clients.size();
+}
+
+void DapRpcAbstractServer::notifyConnectedClients(const DapRpcMessage &message)
+{
+    for (int i = 0; i < m_clients.size(); ++i)
+        m_clients[i]->sendMessage(message);
+}
+
+void DapRpcAbstractServer::notifyConnectedClients(const QString &method, const QJsonArray &params)
+{
+    DapRpcMessage notification =
+        DapRpcMessage::createNotification(method, params);
+    notifyConnectedClients(notification);
+}
diff --git a/DapRPCProtocol/DapRpcAbstractServer.h b/DapRPCProtocol/DapRpcAbstractServer.h
new file mode 100644
index 0000000..56fa07d
--- /dev/null
+++ b/DapRPCProtocol/DapRpcAbstractServer.h
@@ -0,0 +1,31 @@
+#ifndef DapRPCABSTRACTSERVER_H
+#define DapRPCABSTRACTSERVER_H
+
+#include <QList>
+#include <QHostAddress>
+
+#include "DapRpcSocket.h"
+#include "DapRpcMessage.h"
+#include "DapRpcServiceProvider.h"
+
+class DapRpcAbstractServer : public DapRpcServiceProvider
+{
+protected:
+    QList<DapRpcSocket*> m_clients;
+
+public:
+    DapRpcAbstractServer();
+
+    virtual ~DapRpcAbstractServer();
+    virtual int connectedClientCount() const;
+    virtual bool listen(const QString &asAddress = QString(), quint16 aPort = 0) = 0;
+// signals:
+    virtual void onClientConnected() = 0;
+    virtual void onClientDisconnected() = 0;
+
+// public slots:
+    virtual void notifyConnectedClients(const DapRpcMessage &message);
+    virtual void notifyConnectedClients(const QString &method, const QJsonArray &params);
+};
+
+#endif // DapRPCABSTRACTSERVER_H
diff --git a/DapRPCProtocol/DapRpcLocalServer.cpp b/DapRPCProtocol/DapRpcLocalServer.cpp
new file mode 100644
index 0000000..11a2e84
--- /dev/null
+++ b/DapRPCProtocol/DapRpcLocalServer.cpp
@@ -0,0 +1,108 @@
+#include "DapRpcLocalServer.h"
+
+DapRpcLocalServer::DapRpcLocalServer(QObject *apParent)
+    : QLocalServer(apParent)
+{
+    this->setSocketOptions(QLocalServer::WorldAccessOption);
+}
+
+DapRpcLocalServer::~DapRpcLocalServer()
+{
+    foreach (QLocalSocket *socket, m_socketLookup.keys()) {
+        socket->flush();
+        socket->deleteLater();
+    }
+    m_socketLookup.clear();
+
+    foreach (DapRpcSocket *client, m_clients)
+        client->deleteLater();
+    m_clients.clear();
+}
+
+bool DapRpcLocalServer::listen(const QString &asAddress, quint16 aPort)
+{
+    Q_UNUSED(aPort);
+
+    return QLocalServer::listen(asAddress);
+}
+
+bool DapRpcLocalServer::addService(DapRpcService *apService)
+{
+    if (!DapRpcServiceProvider::addService(apService))
+        return false;
+
+    connect(apService, SIGNAL(notifyConnectedClients(DapRpcMessage)),
+               this, SLOT(notifyConnectedClients(DapRpcMessage)));
+    connect(apService, SIGNAL(notifyConnectedClients(QString,QJsonArray)),
+               this, SLOT(notifyConnectedClients(QString,QJsonArray)));
+    return true;
+}
+
+bool DapRpcLocalServer::removeService(DapRpcService *apService)
+{
+    if (!DapRpcServiceProvider::removeService(apService))
+        return false;
+
+    disconnect(apService, SIGNAL(notifyConnectedClients(DapRpcMessage)),
+                  this, SLOT(notifyConnectedClients(DapRpcMessage)));
+    disconnect(apService, SIGNAL(notifyConnectedClients(QString,QJsonArray)),
+                  this, SLOT(notifyConnectedClients(QString,QJsonArray)));
+    return true;
+}
+
+void DapRpcLocalServer::clientDisconnected()
+{
+    QLocalSocket *localSocket = static_cast<QLocalSocket*>(sender());
+    if (!localSocket) {
+        qJsonRpcDebug() << "called with invalid socket";
+        return;
+    }
+    if (m_socketLookup.contains(localSocket)) {
+        DapRpcSocket *socket = m_socketLookup.take(localSocket);
+        m_clients.removeAll(socket);
+        socket->deleteLater();
+    }
+
+    localSocket->deleteLater();
+    emit onClientDisconnected();
+}
+
+void DapRpcLocalServer::messageProcessing(const DapRpcMessage &asMessage)
+{
+    DapRpcSocket *socket = static_cast<DapRpcSocket*>(sender());
+    if (!socket) {
+        qJsonRpcDebug() << "called without service socket";
+        return;
+    }
+
+    processMessage(socket, asMessage);
+}
+
+void DapRpcLocalServer::notifyConnectedClients(const DapRpcMessage &message)
+{
+    DapRpcAbstractServer::notifyConnectedClients(message);
+}
+
+void DapRpcLocalServer::notifyConnectedClients(const QString &method, const QJsonArray &params)
+{
+    DapRpcAbstractServer::notifyConnectedClients(method, params);
+}
+
+void DapRpcLocalServer::incomingConnection(quintptr aSocketDescriptor)
+{
+    QLocalSocket *localSocket = new QLocalSocket(this);
+    if (!localSocket->setSocketDescriptor(aSocketDescriptor)) {
+        qJsonRpcDebug() << "nextPendingConnection is null";
+        localSocket->deleteLater();
+        return;
+    }
+
+    QIODevice *device = qobject_cast<QIODevice*>(localSocket);
+    DapRpcSocket *socket = new DapRpcSocket(device, this);
+    connect(socket, SIGNAL(messageReceived(DapRpcMessage)),
+              this, SLOT(messageProcessing(DapRpcMessage)));
+    m_clients.append(socket);
+    connect(localSocket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
+    m_socketLookup.insert(localSocket, socket);
+    emit onClientConnected();
+}
diff --git a/DapRPCProtocol/DapRpcLocalServer.h b/DapRPCProtocol/DapRpcLocalServer.h
new file mode 100644
index 0000000..9d7a7e7
--- /dev/null
+++ b/DapRPCProtocol/DapRpcLocalServer.h
@@ -0,0 +1,44 @@
+#ifndef DapRPCLOCALSERVER_H
+#define DapRPCLOCALSERVER_H
+
+#include <QObject>
+#include <QLocalSocket>
+#include <QLocalServer>
+
+#include "DapRpcSocket.h"
+#include "DapRpcService.h"
+#include "DapRpcAbstractServer.h"
+
+class DapRpcLocalServer : public QLocalServer, public DapRpcAbstractServer
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(DapRpcLocalServer)
+
+    QHash<QLocalSocket*, DapRpcSocket*> m_socketLookup;
+
+protected:
+    virtual void incomingConnection(quintptr aSocketDescriptor);
+
+public:
+    explicit DapRpcLocalServer(QObject *apParent = nullptr);
+    virtual ~DapRpcLocalServer();
+
+    virtual bool listen(const QString &asAddress = QString(), quint16 aPort = 0);
+    bool addService(DapRpcService *apService);
+    bool removeService(DapRpcService *apService);
+
+signals:
+    void onClientConnected();
+    void onClientDisconnected();
+
+private slots:
+    void clientDisconnected();
+    void messageProcessing(const DapRpcMessage &asMessage);
+
+    // DapRpcAbstractServer interface
+public slots:
+    void notifyConnectedClients(const DapRpcMessage &message);
+    void notifyConnectedClients(const QString &method, const QJsonArray &params);
+};
+
+#endif // DapRPCLOCALSERVER_H
diff --git a/DapRPCProtocol/DapRpcMessage.cpp b/DapRPCProtocol/DapRpcMessage.cpp
new file mode 100644
index 0000000..d07901b
--- /dev/null
+++ b/DapRPCProtocol/DapRpcMessage.cpp
@@ -0,0 +1,373 @@
+#include "DapRpcMessage.h"
+
+class DapRpcMessagePrivate : public QSharedData
+{
+public:
+    DapRpcMessagePrivate();
+    ~DapRpcMessagePrivate();
+    DapRpcMessagePrivate(const DapRpcMessagePrivate &aDapRpcMessagePrivate);
+
+    void initializeWithObject(const QJsonObject &aMessage);
+    static DapRpcMessage createBasicRequest(const QString &asMethod, const QJsonArray &aParams);
+    static DapRpcMessage createBasicRequest(const QString &asMethod,
+                                              const QJsonObject &aNamedParameters);
+
+    DapRpcMessage::Type m_type;
+    QScopedPointer<QJsonObject> m_pObject;
+
+    static int uniqueRequestCounter;
+};
+
+int DapRpcMessagePrivate::uniqueRequestCounter = 0;
+
+DapRpcMessagePrivate::DapRpcMessagePrivate()
+    : m_type(DapRpcMessage::Invalid),
+      m_pObject(nullptr)
+{
+}
+
+DapRpcMessagePrivate::DapRpcMessagePrivate(const DapRpcMessagePrivate &aDapRpcMessagePrivate)
+    : QSharedData(aDapRpcMessagePrivate),
+      m_type(aDapRpcMessagePrivate.m_type),
+      m_pObject(aDapRpcMessagePrivate.m_pObject ? new QJsonObject(*aDapRpcMessagePrivate.m_pObject) : nullptr)
+{
+}
+
+void DapRpcMessagePrivate::initializeWithObject(const QJsonObject &aMessage)
+{
+    m_pObject.reset(new QJsonObject(aMessage));
+    if (aMessage.contains(QLatin1String("id"))) {
+        if (aMessage.contains(QLatin1String("result")) ||
+            aMessage.contains(QLatin1String("error"))) {
+            if (aMessage.contains(QLatin1String("error")) &&
+                !aMessage.value(QLatin1String("error")).isNull())
+                m_type = DapRpcMessage::Error;
+            else
+                m_type = DapRpcMessage::Response;
+        } else if (aMessage.contains(QLatin1String("method"))) {
+            m_type = DapRpcMessage::Request;
+        }
+    } else {
+        if (aMessage.contains(QLatin1String("method")))
+            m_type = DapRpcMessage::Notification;
+    }
+}
+
+DapRpcMessagePrivate::~DapRpcMessagePrivate()
+{
+}
+
+DapRpcMessage::DapRpcMessage()
+    : d(new DapRpcMessagePrivate)
+{
+    d->m_pObject.reset(new QJsonObject);
+}
+
+DapRpcMessage::DapRpcMessage(const DapRpcMessage &aDapRPCMessage)
+    : d(aDapRPCMessage.d)
+{
+}
+
+DapRpcMessage::~DapRpcMessage()
+{
+}
+
+DapRpcMessage &DapRpcMessage::operator=(const DapRpcMessage &aDapRPCMessage)
+{
+    d = aDapRPCMessage.d;
+    return *this;
+}
+
+bool DapRpcMessage::operator==(const DapRpcMessage &aDapRpcMessage) const
+{
+    if (aDapRpcMessage.d == d)
+        return true;
+
+    if (aDapRpcMessage.type() == type()) {
+        if (aDapRpcMessage.type() == DapRpcMessage::Error) {
+            return (aDapRpcMessage.errorCode() == errorCode() &&
+                    aDapRpcMessage.errorMessage() == errorMessage() &&
+                    aDapRpcMessage.errorData() == errorData());
+        } else {
+            if (aDapRpcMessage.type() == DapRpcMessage::Notification) {
+                return (aDapRpcMessage.method() == method() &&
+                        aDapRpcMessage.params() == params());
+            } else {
+                return (aDapRpcMessage.id() == id() &&
+                        aDapRpcMessage.method() == method() &&
+                        aDapRpcMessage.params() == params());
+            }
+        }
+    }
+
+    return false;
+}
+
+DapRpcMessage DapRpcMessage::fromJson(const QByteArray &aData)
+{
+    DapRpcMessage result;
+    QJsonParseError error;
+    QJsonDocument document = QJsonDocument::fromJson(aData, &error);
+    if (error.error != QJsonParseError::NoError) {
+        qJsonRpcDebug() << error.errorString();
+        return result;
+    }
+
+    if (!document.isObject()) {
+        qJsonRpcDebug() << "invalid message: " << aData;
+        return result;
+    }
+
+    result.d->initializeWithObject(document.object());
+    return result;
+}
+
+DapRpcMessage DapRpcMessage::fromObject(const QJsonObject &aObject)
+{
+    DapRpcMessage result;
+    result.d->initializeWithObject(aObject);
+    return result;
+}
+
+QJsonObject DapRpcMessage::toObject() const
+{
+    if (d->m_pObject)
+        return QJsonObject(*d->m_pObject);
+    return QJsonObject();
+}
+
+QByteArray DapRpcMessage::toJson() const
+{
+    if (d->m_pObject) {
+        QJsonDocument doc(*d->m_pObject);
+        return doc.toJson();
+    }
+
+    return QByteArray();
+}
+
+bool DapRpcMessage::isValid() const
+{
+    return d->m_type != DapRpcMessage::Invalid;
+}
+
+DapRpcMessage::Type DapRpcMessage::type() const
+{
+    return d->m_type;
+}
+
+DapRpcMessage DapRpcMessagePrivate::createBasicRequest(const QString &asMethod, const QJsonArray &aParams)
+{
+    DapRpcMessage request;
+    request.d->m_pObject->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
+    request.d->m_pObject->insert(QLatin1String("method"), asMethod);
+    if (!aParams.isEmpty())
+        request.d->m_pObject->insert(QLatin1String("params"), aParams);
+    return request;
+}
+
+DapRpcMessage DapRpcMessagePrivate::createBasicRequest(const QString &asMethod,
+                                                           const QJsonObject &aNamedParameters)
+{
+    DapRpcMessage request;
+    request.d->m_pObject->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
+    request.d->m_pObject->insert(QLatin1String("method"), asMethod);
+    if (!aNamedParameters.isEmpty())
+        request.d->m_pObject->insert(QLatin1String("params"), aNamedParameters);
+    return request;
+}
+
+DapRpcMessage DapRpcMessage::createRequest(const QString &asMethod, const QJsonArray &aParams)
+{
+    DapRpcMessage request = DapRpcMessagePrivate::createBasicRequest(asMethod, aParams);
+    request.d->m_type = DapRpcMessage::Request;
+    DapRpcMessagePrivate::uniqueRequestCounter++;
+    request.d->m_pObject->insert(QLatin1String("id"), DapRpcMessagePrivate::uniqueRequestCounter);
+    return request;
+}
+
+DapRpcMessage DapRpcMessage::createRequest(const QString &asMethod, const QJsonValue &aParam)
+{
+    QJsonArray params;
+    params.append(aParam);
+    return createRequest(asMethod, params);
+}
+
+DapRpcMessage DapRpcMessage::createRequest(const QString &asMethod,
+                                               const QJsonObject &aNamedParameters)
+{
+    DapRpcMessage request =
+        DapRpcMessagePrivate::createBasicRequest(asMethod, aNamedParameters);
+    request.d->m_type = DapRpcMessage::Request;
+    DapRpcMessagePrivate::uniqueRequestCounter++;
+    request.d->m_pObject->insert(QLatin1String("id"), DapRpcMessagePrivate::uniqueRequestCounter);
+    return request;
+}
+
+DapRpcMessage DapRpcMessage::createNotification(const QString &asMethod, const QJsonArray &aParams)
+{
+    DapRpcMessage notification = DapRpcMessagePrivate::createBasicRequest(asMethod, aParams);
+    notification.d->m_type = DapRpcMessage::Notification;
+    return notification;
+}
+
+DapRpcMessage DapRpcMessage::createNotification(const QString &asMethod, const QJsonValue &aParam)
+{
+    QJsonArray params;
+    params.append(aParam);
+    return createNotification(asMethod, params);
+}
+
+DapRpcMessage DapRpcMessage::createNotification(const QString &asMethod,
+                                                    const QJsonObject &aNamedParameters)
+{
+    DapRpcMessage notification =
+        DapRpcMessagePrivate::createBasicRequest(asMethod, aNamedParameters);
+    notification.d->m_type = DapRpcMessage::Notification;
+    return notification;
+}
+
+DapRpcMessage DapRpcMessage::createResponse(const QJsonValue &aResult) const
+{
+    DapRpcMessage response;
+    if (d->m_pObject->contains(QLatin1String("id"))) {
+        QJsonObject *object = response.d->m_pObject.data();
+        object->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
+        object->insert(QLatin1String("id"), d->m_pObject->value(QLatin1String("id")));
+        object->insert(QLatin1String("result"), aResult);
+        response.d->m_type = DapRpcMessage::Response;
+    }
+
+    return response;
+}
+
+DapRpcMessage DapRpcMessage::createErrorResponse(DapErrorCode aCode,
+                                                     const QString &asMessage,
+                                                     const QJsonValue &aData) const
+{
+    DapRpcMessage response;
+    QJsonObject error;
+    error.insert(QLatin1String("code"), aCode);
+    if (!asMessage.isEmpty())
+        error.insert(QLatin1String("message"), asMessage);
+    if (!aData.isUndefined())
+        error.insert(QLatin1String("data"), aData);
+
+    response.d->m_type = DapRpcMessage::Error;
+    QJsonObject *object = response.d->m_pObject.data();
+    object->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
+    if (d->m_pObject->contains(QLatin1String("id")))
+        object->insert(QLatin1String("id"), d->m_pObject->value(QLatin1String("id")));
+    else
+        object->insert(QLatin1String("id"), 0);
+    object->insert(QLatin1String("error"), error);
+    return response;
+}
+
+int DapRpcMessage::id() const
+{
+    if (d->m_type == DapRpcMessage::Notification || !d->m_pObject)
+        return -1;
+
+    const QJsonValue &value = d->m_pObject->value(QLatin1String("id"));
+    if (value.isString())
+        return value.toString().toInt();
+    return value.toInt();
+}
+
+QString DapRpcMessage::method() const
+{
+    if (d->m_type == DapRpcMessage::Response || !d->m_pObject)
+        return QString();
+
+    return d->m_pObject->value(QLatin1String("method")).toString();
+}
+
+QJsonValue DapRpcMessage::params() const
+{
+    if (d->m_type == DapRpcMessage::Response || d->m_type == DapRpcMessage::Error)
+        return QJsonValue(QJsonValue::Undefined);
+    if (!d->m_pObject)
+        return QJsonValue(QJsonValue::Undefined);
+
+    return d->m_pObject->value(QLatin1String("params"));
+}
+
+QJsonValue DapRpcMessage::result() const
+{
+    if (d->m_type != DapRpcMessage::Response || !d->m_pObject)
+        return QJsonValue(QJsonValue::Undefined);
+
+    return d->m_pObject->value(QLatin1String("result"));
+}
+
+int DapRpcMessage::errorCode() const
+{
+    if (d->m_type != DapRpcMessage::Error || !d->m_pObject)
+        return 0;
+
+    QJsonObject error =
+        d->m_pObject->value(QLatin1String("error")).toObject();
+    const QJsonValue &value = error.value(QLatin1String("code"));
+    if (value.isString())
+        return value.toString().toInt();
+    return value.toInt();
+}
+
+QString DapRpcMessage::errorMessage() const
+{
+    if (d->m_type != DapRpcMessage::Error || !d->m_pObject)
+        return QString();
+
+    QJsonObject error =
+        d->m_pObject->value(QLatin1String("error")).toObject();
+    return error.value(QLatin1String("message")).toString();
+}
+
+QJsonValue DapRpcMessage::errorData() const
+{
+    if (d->m_type != DapRpcMessage::Error || !d->m_pObject)
+        return QJsonValue(QJsonValue::Undefined);
+
+    QJsonObject error =
+        d->m_pObject->value(QLatin1String("error")).toObject();
+    return error.value(QLatin1String("data"));
+}
+
+static QDebug operator<<(QDebug dbg, DapRpcMessage::Type type)
+{
+    switch (type) {
+    case DapRpcMessage::Request:
+        return dbg << "DapRpcMessage::Request";
+    case DapRpcMessage::Response:
+        return dbg << "DapRpcMessage::Response";
+    case DapRpcMessage::Notification:
+        return dbg << "DapRpcMessage::Notification";
+    case DapRpcMessage::Error:
+        return dbg << "DapRpcMessage::Error";
+    default:
+        return dbg << "DapRpcMessage::Invalid";
+    }
+}
+
+QDebug operator<<(QDebug dbg, const DapRpcMessage &msg)
+{
+    dbg.nospace() << "DapRpcMessage(type=" << msg.type();
+    if (msg.type() != DapRpcMessage::Notification) {
+        dbg.nospace() << ", id=" << msg.id();
+    }
+
+    if (msg.type() == DapRpcMessage::Request ||
+        msg.type() == DapRpcMessage::Notification) {
+        dbg.nospace() << ", method=" << msg.method()
+                      << ", params=" << msg.params();
+    } else if (msg.type() == DapRpcMessage::Response) {
+        dbg.nospace() << ", result=" << msg.result();
+    } else if (msg.type() == DapRpcMessage::Error) {
+        dbg.nospace() << ", code=" << msg.errorCode()
+                      << ", message=" << msg.errorMessage()
+                      << ", data=" << msg.errorData();
+    }
+    dbg.nospace() << ")";
+    return dbg.space();
+}
diff --git a/DapRPCProtocol/DapRpcMessage.h b/DapRPCProtocol/DapRpcMessage.h
new file mode 100644
index 0000000..92b47aa
--- /dev/null
+++ b/DapRPCProtocol/DapRpcMessage.h
@@ -0,0 +1,96 @@
+#ifndef DapRPCMESSAGE_H
+#define DapRPCMESSAGE_H
+
+#include <QSharedDataPointer>
+#include <QMetaType>
+#include <QJsonDocument>
+#include <QJsonValue>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QDebug>
+
+#define qJsonRpcDebug if (qgetenv("QJSONRPC_DEBUG").isEmpty()); else qDebug
+
+enum DapErrorCode {
+    NoError         = 0,
+    ParseError      = -32700,           // Invalid JSON was received by the server.
+                                        // An error occurred on the server while parsing the JSON text.
+    InvalidRequest  = -32600,           // The JSON sent is not a valid Request object.
+    MethodNotFound  = -32601,           // The method does not exist / is not available.
+    InvalidParams   = -32602,           // Invalid method parameter(s).
+    InternalError   = -32603,           // Internal JSON-RPC error.
+    ServerErrorBase = -32000,           // Reserved for implementation-defined server-errors.
+    UserError       = -32099,           // Anything after this is user defined
+    TimeoutError    = -32100
+};
+Q_DECLARE_METATYPE(DapErrorCode)
+
+class DapRpcMessagePrivate;
+class DapRpcMessage
+{
+    friend class DapRpcMessagePrivate;
+    QSharedDataPointer<DapRpcMessagePrivate> d;
+
+public:
+    DapRpcMessage();
+    DapRpcMessage(const DapRpcMessage &aDapRPCMessage);
+    DapRpcMessage &operator=(const DapRpcMessage &aDapRPCMessage);
+    ~DapRpcMessage();
+
+    inline void swap(DapRpcMessage &aDapRPCMessage) { qSwap(d, aDapRPCMessage.d); }
+
+    enum Type {
+        Invalid,
+        Request,
+        Response,
+        Notification,
+        Error
+    };
+
+    static DapRpcMessage createRequest(const QString &asMethod,
+                                         const QJsonArray &aParams = QJsonArray());
+    static DapRpcMessage createRequest(const QString &asMethod, const QJsonValue &aParam);
+    static DapRpcMessage createRequest(const QString &asMethod, const QJsonObject &aNamedParameters);
+
+    static DapRpcMessage createNotification(const QString &asMethod,
+                                              const QJsonArray &aParams = QJsonArray());
+    static DapRpcMessage createNotification(const QString &asMethod, const QJsonValue &aParam);
+    static DapRpcMessage createNotification(const QString &asMethod,
+                                              const QJsonObject &aNamedParameters);
+
+    DapRpcMessage createResponse(const QJsonValue &aResult) const;
+    DapRpcMessage createErrorResponse(DapErrorCode aCode,
+                                        const QString &asMessage = QString(),
+                                        const QJsonValue &aData = QJsonValue()) const;
+
+    DapRpcMessage::Type type() const;
+    bool isValid() const;
+    int id() const;
+
+    // request
+    QString method() const;
+    QJsonValue params() const;
+
+    // response
+    QJsonValue result() const;
+
+    // error
+    int errorCode() const;
+    QString errorMessage() const;
+    QJsonValue errorData() const;
+
+    QJsonObject toObject() const;
+    static DapRpcMessage fromObject(const QJsonObject &aObject);
+
+    QByteArray toJson() const;
+    static DapRpcMessage fromJson(const QByteArray &aData);
+
+    bool operator==(const DapRpcMessage &aDapRpcMessage) const;
+    inline bool operator!=(const DapRpcMessage &aDapRpcMessage) const { return !(operator==(aDapRpcMessage)); }
+};
+
+QDebug operator<<(QDebug, const DapRpcMessage &);
+Q_DECLARE_METATYPE(DapRpcMessage)
+Q_DECLARE_SHARED(DapRpcMessage)
+
+#endif // DapRPCMESSAGE_H
diff --git a/DapRPCProtocol/DapRpcService.cpp b/DapRPCProtocol/DapRpcService.cpp
new file mode 100644
index 0000000..f52ce29
--- /dev/null
+++ b/DapRPCProtocol/DapRpcService.cpp
@@ -0,0 +1,340 @@
+#include "DapRpcService.h"
+#include "DapRpcSocket.h"
+
+ParameterInfo::ParameterInfo(const QString &asName, int aType, bool aOut)
+    : type(aType),
+      jsType(DapRpcService::convertVariantTypeToJSType(aType)),
+      name(asName),
+      out(aOut)
+{
+}
+
+MethodInfo::MethodInfo()
+    : returnType(QMetaType::Void),
+      valid(false),
+      hasOut(false)
+{
+}
+
+MethodInfo::MethodInfo(const QMetaMethod &aMethod)
+    : returnType(QMetaType::Void),
+      valid(true),
+      hasOut(false)
+{
+    returnType = aMethod.returnType();
+    if (returnType == QMetaType::UnknownType) {
+        qJsonRpcDebug() << "DapRpcService: can't bind method's return type"
+                      << QString(aMethod.name());
+        valid = false;
+        return;
+    }
+
+    parameters.reserve(aMethod.parameterCount());
+
+    const QList<QByteArray> &types = aMethod.parameterTypes();
+    const QList<QByteArray> &names = aMethod.parameterNames();
+    for (int i = 0; i < types.size(); ++i) {
+        QByteArray parameterType = types.at(i);
+        const QByteArray &parameterName = names.at(i);
+        bool out = parameterType.endsWith('&');
+
+        if (out) {
+            hasOut = true;
+            parameterType.resize(parameterType.size() - 1);
+        }
+
+        int type = QMetaType::type(parameterType);
+        if (type == 0) {
+            qJsonRpcDebug() << "DapRpcService: can't bind method's parameter"
+                          << QString(parameterType);
+            valid = false;
+            break;
+        }
+
+        parameters.append(ParameterInfo(parameterName, type, out));
+    }
+}
+
+void DapRpcService::setCurrentRequest(const DapRpcServiceRequest &aCurrentRequest)
+{
+    m_currentRequest = aCurrentRequest;
+}
+
+DapRpcService::DapRpcService(QObject *apParent)
+    : QObject(apParent)
+{
+}
+
+DapRpcService::~DapRpcService()
+{
+}
+
+DapRpcServiceRequest DapRpcService::currentRequest() const
+{
+    return m_currentRequest;
+}
+
+void DapRpcService::beginDelayedResponse()
+{
+    m_delayedResponse = true;
+}
+
+int DapRpcService::convertVariantTypeToJSType(int aType)
+{
+    switch (aType) {
+    case QMetaType::Int:
+    case QMetaType::UInt:
+    case QMetaType::Double:
+    case QMetaType::Long:
+    case QMetaType::LongLong:
+    case QMetaType::Short:
+    case QMetaType::Char:
+    case QMetaType::ULong:
+    case QMetaType::ULongLong:
+    case QMetaType::UShort:
+    case QMetaType::UChar:
+    case QMetaType::Float:
+        return QJsonValue::Double;    // all numeric types in js are doubles
+    case QMetaType::QVariantList:
+    case QMetaType::QStringList:
+        return QJsonValue::Array;
+    case QMetaType::QVariantMap:
+        return QJsonValue::Object;
+    case QMetaType::QString:
+        return QJsonValue::String;
+    case QMetaType::Bool:
+        return QJsonValue::Bool;
+    default:
+        break;
+    }
+
+    return QJsonValue::Undefined;
+}
+
+int DapRpcService::qjsonRpcMessageType = qRegisterMetaType<DapRpcMessage>("DapRpcMessage");
+void DapRpcService::cacheInvokableInfo()
+{
+    const QMetaObject *obj = metaObject();
+    int startIdx = staticMetaObject.methodCount(); // skip QObject slots
+    for (int idx = startIdx; idx < obj->methodCount(); ++idx) {
+        const QMetaMethod method = obj->method(idx);
+        if ((method.methodType() == QMetaMethod::Slot &&
+             method.access() == QMetaMethod::Public) ||
+             method.methodType() == QMetaMethod::Signal) {
+
+            QByteArray signature = method.methodSignature();
+            QByteArray methodName = method.name();
+
+            MethodInfo info(method);
+            if (!info.valid)
+                continue;
+
+            if (signature.contains("QVariant"))
+                m_invokableMethodHash[methodName].append(idx);
+            else
+                m_invokableMethodHash[methodName].prepend(idx);
+            m_methodInfoHash[idx] = info;
+        }
+    }
+}
+
+static bool jsParameterCompare(const QJsonArray &parameters,
+                               const MethodInfo &info)
+{
+    int j = 0;
+    for (int i = 0; i < info.parameters.size() && j < parameters.size(); ++i) {
+        int jsType = info.parameters.at(i).jsType;
+        if (jsType != QJsonValue::Undefined && jsType != parameters.at(j).type()) {
+            if (!info.parameters.at(i).out)
+                return false;
+        } else {
+            ++j;
+        }
+    }
+
+    return (j == parameters.size());
+}
+
+static  bool jsParameterCompare(const QJsonObject &parameters,
+                                const MethodInfo &info)
+{
+    for (int i = 0; i < info.parameters.size(); ++i) {
+        int jsType = info.parameters.at(i).jsType;
+        QJsonValue value = parameters.value(info.parameters.at(i).name);
+        if (value == QJsonValue::Undefined) {
+            if (!info.parameters.at(i).out)
+                return false;
+        } else if (jsType == QJsonValue::Undefined) {
+            continue;
+        } else if (jsType != value.type()) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static inline QVariant convertArgument(const QJsonValue &argument,
+                                       const ParameterInfo &info)
+{
+    if (argument.isUndefined())
+        return QVariant(info.type, Q_NULLPTR);
+
+    if (info.type == QMetaType::QJsonValue || info.type == QMetaType::QVariant ||
+        info.type >= QMetaType::User) {
+
+        if (info.type == QMetaType::QVariant)
+            return argument.toVariant();
+
+        QVariant result(argument);
+        if (info.type >= QMetaType::User && result.canConvert(info.type))
+            result.convert(info.type);
+        return result;
+    }
+
+    QVariant result = argument.toVariant();
+    if (result.userType() == info.type || info.type == QMetaType::QVariant) {
+        return result;
+    } else if (result.canConvert(info.type)) {
+        result.convert(info.type);
+        return result;
+    } else if (info.type < QMetaType::User) {
+        // already tried for >= user, this is the last resort
+        QVariant result(argument);
+        if (result.canConvert(info.type)) {
+            result.convert(info.type);
+            return result;
+        }
+    }
+
+    return QVariant();
+}
+
+QJsonValue DapRpcService::convertReturnValue(QVariant &aReturnValue)
+{
+    if (static_cast<int>(aReturnValue.type()) == qMetaTypeId<QJsonObject>())
+        return QJsonValue(aReturnValue.toJsonObject());
+    else if (static_cast<int>(aReturnValue.type()) == qMetaTypeId<QJsonArray>())
+        return QJsonValue(aReturnValue.toJsonArray());
+
+    switch (aReturnValue.type()) {
+    case QMetaType::Bool:
+    case QMetaType::Int:
+    case QMetaType::Double:
+    case QMetaType::LongLong:
+    case QMetaType::ULongLong:
+    case QMetaType::UInt:
+    case QMetaType::QString:
+    case QMetaType::QStringList:
+    case QMetaType::QVariantList:
+    case QMetaType::QVariantMap:
+        return QJsonValue::fromVariant(aReturnValue);
+    default:
+        // if a conversion operator was registered it will be used
+        if (aReturnValue.convert(QMetaType::QJsonValue))
+            return aReturnValue.toJsonValue();
+        else
+            return QJsonValue();
+    }
+}
+
+static inline QByteArray methodName(const DapRpcMessage &request)
+{
+    const QString &methodPath(request.method());
+    return methodPath.midRef(methodPath.lastIndexOf('.') + 1).toLatin1();
+}
+
+DapRpcMessage DapRpcService::dispatch(const DapRpcMessage &aRequest)
+{
+    if (aRequest.type() != DapRpcMessage::Request &&
+        aRequest.type() != DapRpcMessage::Notification) {
+        return aRequest.createErrorResponse(DapErrorCode::InvalidRequest, "invalid request");
+    }
+
+    const QByteArray &method(methodName(aRequest));
+    if (!m_invokableMethodHash.contains(method)) {
+        return aRequest.createErrorResponse(DapErrorCode::MethodNotFound, "invalid method called");
+    }
+
+    int idx = -1;
+    QVariantList arguments;
+    const QList<int> &indexes = m_invokableMethodHash.value(method);
+    const QJsonValue &params = aRequest.params();
+    QVarLengthArray<void *, 10> parameters;
+    QVariant returnValue;
+    QMetaType::Type returnType = QMetaType::Void;
+
+    bool usingNamedParameters = params.isObject();
+    foreach (int methodIndex, indexes) {
+        MethodInfo &info = m_methodInfoHash[methodIndex];
+        bool methodMatch = usingNamedParameters ?
+            jsParameterCompare(params.toObject(), info) :
+            jsParameterCompare(params.toArray(), info);
+
+        if (methodMatch) {
+            idx = methodIndex;
+            arguments.reserve(info.parameters.size());
+            returnType = static_cast<QMetaType::Type>(info.returnType);
+            returnValue = (returnType == QMetaType::Void) ?
+                QVariant() : QVariant(returnType, Q_NULLPTR);
+            if (returnType == QMetaType::QVariant)
+                parameters.append(&returnValue);
+            else
+                parameters.append(returnValue.data());
+
+            for (int i = 0; i < info.parameters.size(); ++i) {
+                const ParameterInfo &parameterInfo = info.parameters.at(i);
+                QJsonValue incomingArgument = usingNamedParameters ?
+                    params.toObject().value(parameterInfo.name) :
+                    params.toArray().at(i);
+
+                QVariant argument = convertArgument(incomingArgument, parameterInfo);
+                if (!argument.isValid()) {
+                    QString message = incomingArgument.isUndefined() ?
+                        QString("failed to construct default object for '%1'").arg(parameterInfo.name) :
+                        QString("failed to convert from JSON for '%1'").arg(parameterInfo.name);
+                    return aRequest.createErrorResponse(DapErrorCode::InvalidParams, message);
+                }
+
+                arguments.push_back(argument);
+                if (parameterInfo.type == QMetaType::QVariant)
+                    parameters.append(static_cast<void *>(&arguments.last()));
+                else
+                    parameters.append(const_cast<void *>(arguments.last().constData()));
+            }
+            break;
+        }
+    }
+
+    if (idx == -1) {
+        return aRequest.createErrorResponse(DapErrorCode::InvalidParams, "invalid parameters");
+    }
+
+    MethodInfo &info = m_methodInfoHash[idx];
+
+    bool success =
+        const_cast<DapRpcService*>(this)->qt_metacall(QMetaObject::InvokeMetaMethod, idx, parameters.data()) < 0;
+    if (!success) {
+        QString message = QString("dispatch for method '%1' failed").arg(method.constData());
+        return aRequest.createErrorResponse(DapErrorCode::InvalidRequest, message);
+    }
+
+    if (m_delayedResponse) {
+        m_delayedResponse = false;
+        return DapRpcMessage();
+    }
+
+    if (info.hasOut) {
+        QJsonArray ret;
+        if (info.returnType != QMetaType::Void)
+            ret.append(convertReturnValue(returnValue));
+        for (int i = 0; i < info.parameters.size(); ++i)
+            if (info.parameters.at(i).out)
+                ret.append(convertReturnValue(arguments[i]));
+        if (ret.size() > 1)
+            return aRequest.createResponse(ret);
+        return aRequest.createResponse(ret.first());
+    }
+
+    return aRequest.createResponse(convertReturnValue(returnValue));
+}
diff --git a/DapRPCProtocol/DapRpcService.h b/DapRPCProtocol/DapRpcService.h
new file mode 100644
index 0000000..c13786f
--- /dev/null
+++ b/DapRPCProtocol/DapRpcService.h
@@ -0,0 +1,72 @@
+#ifndef DapRPCSERVICE_H
+#define DapRPCSERVICE_H
+
+#include <QObject>
+#include <QVariant>
+#include <QPointer>
+#include <QVarLengthArray>
+#include <QMetaMethod>
+#include <QEventLoop>
+#include <QDebug>
+
+
+#include "DapRpcMessage.h"
+#include "DapRpcServiceRequest.h"
+
+struct ParameterInfo
+{
+    ParameterInfo(const QString &asName = QString(), int aType = 0, bool aOut = false);
+
+    int type;
+    int jsType;
+    QString name;
+    bool out;
+};
+
+struct MethodInfo
+{
+    MethodInfo();
+    MethodInfo(const QMetaMethod &aMethod);
+
+    QVarLengthArray<ParameterInfo> parameters;
+    int returnType;
+    bool valid;
+    bool hasOut;
+};
+
+class DapRpcService : public QObject
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(DapRpcService)
+
+    QHash<int, MethodInfo > m_methodInfoHash;
+    QHash<QByteArray, QList<int> > m_invokableMethodHash;
+    DapRpcServiceRequest m_currentRequest;
+    bool m_delayedResponse {false};
+
+protected:
+    DapRpcServiceRequest currentRequest() const;
+    void beginDelayedResponse();
+
+public:
+    explicit DapRpcService(QObject *apParent = nullptr);
+    ~DapRpcService();
+
+    void cacheInvokableInfo();
+    static int qjsonRpcMessageType;
+    static int convertVariantTypeToJSType(int aType);
+    static QJsonValue convertReturnValue(QVariant &aReturnValue);
+
+    void setCurrentRequest(const DapRpcServiceRequest &aCurrentRequest);
+
+signals:
+    void result(const DapRpcMessage &aDapRpcMessage);
+    void notifyConnectedClients(const DapRpcMessage &aDapRpcMessage);
+    void notifyConnectedClients(const QString &asMethod, const QJsonArray &aParams = QJsonArray());
+
+public slots:
+    DapRpcMessage dispatch(const DapRpcMessage &aRequest);
+};
+
+#endif // DapRPCSERVICE_H
+
diff --git a/DapRPCProtocol/DapRpcServiceProvider.cpp b/DapRPCProtocol/DapRpcServiceProvider.cpp
new file mode 100644
index 0000000..106e9fe
--- /dev/null
+++ b/DapRPCProtocol/DapRpcServiceProvider.cpp
@@ -0,0 +1,93 @@
+#include "DapRpcServiceProvider.h"
+#include "DapRpcSocket.h"
+
+DapRpcServiceProvider::DapRpcServiceProvider()
+{
+}
+
+DapRpcServiceProvider::~DapRpcServiceProvider()
+{
+}
+
+QByteArray DapRpcServiceProvider::getServiceName(DapRpcService *apService)
+{
+    const QMetaObject *mo = apService->metaObject();
+    for (int i = 0; i < mo->classInfoCount(); i++) {
+        const QMetaClassInfo mci = mo->classInfo(i);
+        if (mci.name() == QLatin1String("serviceName"))
+            return mci.value();
+    }
+
+    return QByteArray(mo->className()).toLower();
+}
+
+bool DapRpcServiceProvider::addService(DapRpcService *apService)
+{
+    QByteArray serviceName = getServiceName(apService);
+    if (serviceName.isEmpty()) {
+        qJsonRpcDebug() << "service added without serviceName classinfo, aborting";
+        return false;
+    }
+
+    if (m_services.contains(serviceName)) {
+        qJsonRpcDebug() << "service with name " << serviceName << " already exist";
+        return false;
+    }
+
+    apService->cacheInvokableInfo();
+    m_services.insert(serviceName, apService);
+    if (!apService->parent())
+        m_cleanupHandler.add(apService);
+    return true;
+}
+
+bool DapRpcServiceProvider::removeService(DapRpcService *apService)
+{
+    QByteArray serviceName = getServiceName(apService);
+    if (!m_services.contains(serviceName)) {
+        qJsonRpcDebug() << "can not find service with name " << serviceName;
+        return false;
+    }
+
+    m_cleanupHandler.remove(m_services.value(serviceName));
+    m_services.remove(serviceName);
+    return true;
+}
+
+void DapRpcServiceProvider::processMessage(DapRpcSocket *apSocket, const DapRpcMessage &aMessage)
+{
+    switch (aMessage.type()) {
+        case DapRpcMessage::Request:
+        case DapRpcMessage::Notification: {
+            QByteArray serviceName = aMessage.method().section(".", 0, -2).toLatin1();
+            if (serviceName.isEmpty() || !m_services.contains(serviceName)) {
+                if (aMessage.type() == DapRpcMessage::Request) {
+                    DapRpcMessage error =
+                        aMessage.createErrorResponse(DapErrorCode::MethodNotFound,
+                            QString("service '%1' not found").arg(serviceName.constData()));
+                    apSocket->notify(error);
+                }
+            } else {
+                DapRpcService *service = m_services.value(serviceName);
+                service->setCurrentRequest(DapRpcServiceRequest(aMessage, apSocket));
+                if (aMessage.type() == DapRpcMessage::Request)
+                    QObject::connect(service, SIGNAL(result(DapRpcMessage)),
+                                      apSocket, SLOT(notify(DapRpcMessage)), Qt::UniqueConnection);
+                DapRpcMessage response = service->dispatch(aMessage);
+                if (response.isValid())
+                    apSocket->notify(response);
+            }
+        }
+        break;
+
+        case DapRpcMessage::Response:
+            break;
+
+        default: {
+            DapRpcMessage error =
+                aMessage.createErrorResponse(DapErrorCode::InvalidRequest, QString("invalid request"));
+            apSocket->notify(error);
+            break;
+        }
+    };
+}
diff --git a/DapRPCProtocol/DapRpcServiceProvider.h b/DapRPCProtocol/DapRpcServiceProvider.h
new file mode 100644
index 0000000..6e74d30
--- /dev/null
+++ b/DapRPCProtocol/DapRpcServiceProvider.h
@@ -0,0 +1,29 @@
+#ifndef DapRPCSERVICEPROVIDER_H
+#define DapRPCSERVICEPROVIDER_H
+
+#include <QScopedPointer>
+#include <QObjectCleanupHandler>
+#include <QHash>
+#include <QMetaObject>
+#include <QMetaClassInfo>
+#include <QDebug>
+
+#include "DapRpcService.h"
+
+class DapRpcServiceProvider
+{
+    QHash<QByteArray, DapRpcService*> m_services;
+    QObjectCleanupHandler m_cleanupHandler;
+
+protected:
+    DapRpcServiceProvider();
+    void processMessage(DapRpcSocket *apSocket, const DapRpcMessage &aMessage);
+
+public:
+    virtual ~DapRpcServiceProvider();
+    virtual bool addService(DapRpcService *apService);
+    virtual bool removeService(DapRpcService *apService);
+    QByteArray getServiceName(DapRpcService *apService);
+};
+
+#endif // DapRPCSERVICEPROVIDER_H
diff --git a/DapRPCProtocol/DapRpcServiceReply.cpp b/DapRPCProtocol/DapRpcServiceReply.cpp
new file mode 100644
index 0000000..ba26f38
--- /dev/null
+++ b/DapRPCProtocol/DapRpcServiceReply.cpp
@@ -0,0 +1,31 @@
+#include "DapRpcServiceReply.h"
+
+DapRpcServiceReply::DapRpcServiceReply(QObject *apParent)
+    : QObject(apParent)
+{
+}
+
+DapRpcServiceReply::~DapRpcServiceReply()
+{
+
+}
+
+void DapRpcServiceReply::setRequest(const DapRpcMessage &aRequest)
+{
+    m_request = aRequest;
+}
+
+void DapRpcServiceReply::setResponse(const DapRpcMessage &aResponse)
+{
+    m_response = aResponse;
+}
+
+DapRpcMessage DapRpcServiceReply::request() const
+{
+    return m_request;
+}
+
+DapRpcMessage DapRpcServiceReply::response() const
+{
+    return m_response;
+}
diff --git a/DapRPCProtocol/DapRpcServiceReply.h b/DapRPCProtocol/DapRpcServiceReply.h
new file mode 100644
index 0000000..2916114
--- /dev/null
+++ b/DapRPCProtocol/DapRpcServiceReply.h
@@ -0,0 +1,30 @@
+#ifndef DapRPCSERVICEREPLY_H
+#define DapRPCSERVICEREPLY_H
+
+#include <QObject>
+#include <QNetworkReply>
+
+#include "DapRpcMessage.h"
+
+class DapRpcServiceReply : public QObject
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(DapRpcServiceReply)
+    DapRpcMessage m_request;
+    DapRpcMessage m_response;
+
+public:
+    explicit DapRpcServiceReply(QObject *apParent = nullptr);
+    virtual ~DapRpcServiceReply();
+
+    DapRpcMessage request() const;
+    DapRpcMessage response() const;
+
+    void setRequest(const DapRpcMessage &aRequest);
+    void setResponse(const DapRpcMessage &aResponse);
+
+signals:
+    void finished();
+};
+
+#endif // DapRPCSERVICEREPLY_H
diff --git a/DapRPCProtocol/DapRpcServiceRequest.cpp b/DapRPCProtocol/DapRpcServiceRequest.cpp
new file mode 100644
index 0000000..3a00a07
--- /dev/null
+++ b/DapRPCProtocol/DapRpcServiceRequest.cpp
@@ -0,0 +1,69 @@
+#include "DapRpcSocket.h"
+#include "DapRpcServiceRequest.h"
+
+
+DapRpcServiceRequest::DapRpcServiceRequest()
+{
+}
+
+DapRpcServiceRequest::~DapRpcServiceRequest()
+{
+}
+
+DapRpcServiceRequest::DapRpcServiceRequest(const DapRpcServiceRequest &aDapRpcServiceRequest)
+{
+    m_request = aDapRpcServiceRequest.m_request;
+    m_socket = aDapRpcServiceRequest.m_socket;
+}
+
+DapRpcServiceRequest::DapRpcServiceRequest(const DapRpcMessage &aRequest,
+                                               DapRpcSocket *apSocket)
+{
+    m_request = aRequest;
+    m_socket = apSocket;
+}
+
+DapRpcServiceRequest &DapRpcServiceRequest::operator=(const DapRpcServiceRequest &other)
+{
+    m_request = other.m_request;
+    m_socket = other.m_socket;
+    return *this;
+}
+
+bool DapRpcServiceRequest::isValid() const
+{
+    return (m_request.isValid() && !m_socket.isNull());
+}
+
+DapRpcMessage DapRpcServiceRequest::request() const
+{
+    return m_request;
+}
+
+DapRpcSocket *DapRpcServiceRequest::socket() const
+{
+    return m_socket;
+}
+
+bool DapRpcServiceRequest::respond(QVariant aReturnValue)
+{
+    if (!m_socket) {
+        qJsonRpcDebug() << "socket was closed";
+        return false;
+    }
+
+    DapRpcMessage response =
+        m_request.createResponse(DapRpcService::convertReturnValue(aReturnValue));
+    return respond(response);
+}
+
+bool DapRpcServiceRequest::respond(const DapRpcMessage &aResponse)
+{
+    if (!m_socket) {
+        qJsonRpcDebug() << "socket was closed";
+        return false;
+    }
+
+    QMetaObject::invokeMethod(m_socket, "notify", Q_ARG(DapRpcMessage, aResponse));
+    return true;
+}
diff --git a/DapRPCProtocol/DapRpcServiceRequest.h b/DapRPCProtocol/DapRpcServiceRequest.h
new file mode 100644
index 0000000..aa689f8
--- /dev/null
+++ b/DapRPCProtocol/DapRpcServiceRequest.h
@@ -0,0 +1,31 @@
+#ifndef DapRPCSERVICEREQUEST_H
+#define DapRPCSERVICEREQUEST_H
+
+#include <QPointer>
+#include <QMetaObject>
+#include <QDebug>
+
+#include "DapRpcMessage.h"
+
+class DapRpcSocket;
+class DapRpcServiceRequest
+{
+    DapRpcMessage m_request;
+    QPointer<DapRpcSocket> m_socket;
+
+public:
+    DapRpcServiceRequest();
+    DapRpcServiceRequest(const DapRpcServiceRequest &aDapRpcServiceRequest);
+    DapRpcServiceRequest(const DapRpcMessage &aRequest, DapRpcSocket *apSocket);
+    DapRpcServiceRequest &operator=(const DapRpcServiceRequest &aDapRpcServiceRequest);
+    ~DapRpcServiceRequest();
+
+    bool isValid() const;
+    DapRpcMessage request() const;
+    DapRpcSocket *socket() const;
+
+    bool respond(const DapRpcMessage &aResponse);
+    bool respond(QVariant aReturnValue);
+};
+
+#endif // DapRPCSERVICEREQUEST_H
diff --git a/DapRPCProtocol/DapRpcSocket.cpp b/DapRPCProtocol/DapRpcSocket.cpp
new file mode 100644
index 0000000..d769b41
--- /dev/null
+++ b/DapRPCProtocol/DapRpcSocket.cpp
@@ -0,0 +1,262 @@
+#include "DapRpcSocket.h"
+#include "DapRpcService.h"
+
+DapRpcSocket::DapRpcSocket(QObject *apParent)
+    : QObject(apParent)
+{
+
+}
+
+DapRpcSocket::DapRpcSocket(QIODevice *apDevice, QObject *apParent)
+    : QObject(apParent)
+{
+    m_pDevice = apDevice;
+    connect(m_pDevice, SIGNAL(readyRead()), this, SLOT(processIncomingData()));
+}
+
+DapRpcSocket::~DapRpcSocket()
+{
+}
+
+int DapRpcSocket::findJsonDocumentEnd(const QByteArray &aJsonData)
+{
+    const char* pos = aJsonData.constData();
+    const char* end = pos + aJsonData.length();
+
+    char blockStart = 0;
+    char blockEnd = 0;
+    int index = 0;
+
+    while (true) {
+        if (pos == end) {
+            return -1;
+        } else if (*pos == '{') {
+            blockStart = '{';
+            blockEnd = '}';
+            break;
+        } else if(*pos == '[') {
+            blockStart = '[';
+            blockEnd = ']';
+            break;
+        }
+
+        pos++;
+        index++;
+    }
+
+    pos++;
+    index++;
+    int depth = 1;
+    bool inString = false;
+    while (depth > 0 && pos <= end) {
+        if (*pos == '\\') {
+            pos += 2;
+            index += 2;
+            continue;
+        } else if (*pos == '"') {
+            inString = !inString;
+        } else if (!inString) {
+            if (*pos == blockStart)
+                depth++;
+            else if (*pos == blockEnd)
+                depth--;
+        }
+
+        pos++;
+        index++;
+    }
+
+    return depth == 0 ? index-1 : -1;
+}
+
+void DapRpcSocket::writeData(const DapRpcMessage &asMessage)
+{
+    QJsonDocument doc = QJsonDocument(asMessage.toObject());
+    QByteArray data = doc.toJson(QJsonDocument::Compact);
+
+    m_pDevice.data()->write(data);
+    qJsonRpcDebug() << "sending: " << data;
+}
+
+void DapRpcSocket::setDefaultRequestTimeout(int aiMsecs)
+{
+    if (aiMsecs < 0) {
+        qJsonRpcDebug() << "Cannot set a negative request timeout msecs value";
+        return;
+    }
+
+    m_defaultRequestTimeout = aiMsecs;
+}
+
+int DapRpcSocket::getDefaultRequestTimeout() const
+{
+    return m_defaultRequestTimeout;
+}
+
+bool DapRpcSocket::isValid() const
+{
+    return m_pDevice && m_pDevice.data()->isOpen();
+}
+
+DapRpcMessage DapRpcSocket::sendMessageBlocking(const DapRpcMessage &asMessage, int aMsecs)
+{
+    DapRpcServiceReply *reply = sendMessage(asMessage);
+    QScopedPointer<DapRpcServiceReply> replyPtr(reply);
+
+    QEventLoop responseLoop;
+    connect(reply, SIGNAL(finished()), &responseLoop, SLOT(quit()));
+    QTimer::singleShot(aMsecs, &responseLoop, SLOT(quit()));
+    responseLoop.exec();
+
+    if (!reply->response().isValid()) {
+        m_replies.remove(asMessage.id());
+        return asMessage.createErrorResponse(DapErrorCode::TimeoutError, "request timed out");
+    }
+
+    return reply->response();
+}
+
+DapRpcServiceReply *DapRpcSocket::sendMessage(const DapRpcMessage &asMessage)
+{
+    if (!m_pDevice) {
+        qJsonRpcDebug() << "trying to send message without device";
+        return nullptr;
+    }
+
+    notify(asMessage);
+    QPointer<DapRpcServiceReply> reply(new DapRpcServiceReply);
+    reply->setRequest(asMessage);
+    m_replies.insert(asMessage.id(), reply);
+    return reply;
+}
+
+void DapRpcSocket::notify(const DapRpcMessage &asMessage)
+{
+    if (!m_pDevice) {
+        qJsonRpcDebug() << "trying to send message without device";
+        return;
+    }
+
+    DapRpcService *service = qobject_cast<DapRpcService*>(sender());
+    if (service)
+        disconnect(service, SIGNAL(result(DapRpcMessage)), this, SLOT(notify(DapRpcMessage)));
+
+    writeData(asMessage);
+}
+
+DapRpcMessage DapRpcSocket::invokeRemoteMethodBlocking(const QString &asMethod, int aMsecs, const QVariant &param1,
+                                                           const QVariant &param2, const QVariant &param3,
+                                                           const QVariant &param4, const QVariant &param5,
+                                                           const QVariant &param6, const QVariant &param7,
+                                                           const QVariant &param8, const QVariant &param9,
+                                                           const QVariant &param10)
+{
+    QVariantList params;
+    if (param1.isValid()) params.append(param1);
+    if (param2.isValid()) params.append(param2);
+    if (param3.isValid()) params.append(param3);
+    if (param4.isValid()) params.append(param4);
+    if (param5.isValid()) params.append(param5);
+    if (param6.isValid()) params.append(param6);
+    if (param7.isValid()) params.append(param7);
+    if (param8.isValid()) params.append(param8);
+    if (param9.isValid()) params.append(param9);
+    if (param10.isValid()) params.append(param10);
+
+    DapRpcMessage request =
+        DapRpcMessage::createRequest(asMethod, QJsonArray::fromVariantList(params));
+    return sendMessageBlocking(request, aMsecs);
+}
+
+DapRpcMessage DapRpcSocket::invokeRemoteMethodBlocking(const QString &asMethod, const QVariant &param1,
+                                                           const QVariant &param2, const QVariant &param3,
+                                                           const QVariant &param4, const QVariant &param5,
+                                                           const QVariant &param6, const QVariant &param7,
+                                                           const QVariant &param8, const QVariant &param9,
+                                                           const QVariant &param10)
+{
+    return invokeRemoteMethodBlocking(asMethod, m_defaultRequestTimeout, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
+}
+
+DapRpcServiceReply *DapRpcSocket::invokeRemoteMethod(const QString &asMethod, const QVariant &param1,
+                                                         const QVariant &param2, const QVariant &param3,
+                                                         const QVariant &param4, const QVariant &param5,
+                                                         const QVariant &param6, const QVariant &param7,
+                                                         const QVariant &param8, const QVariant &param9,
+                                                         const QVariant &param10)
+{
+    QVariantList params;
+    if (param1.isValid()) params.append(param1);
+    if (param2.isValid()) params.append(param2);
+    if (param3.isValid()) params.append(param3);
+    if (param4.isValid()) params.append(param4);
+    if (param5.isValid()) params.append(param5);
+    if (param6.isValid()) params.append(param6);
+    if (param7.isValid()) params.append(param7);
+    if (param8.isValid()) params.append(param8);
+    if (param9.isValid()) params.append(param9);
+    if (param10.isValid()) params.append(param10);
+
+    DapRpcMessage request =
+        DapRpcMessage::createRequest(asMethod, QJsonArray::fromVariantList(params));
+    return sendMessage(request);
+}
+
+void DapRpcSocket::processIncomingData()
+{
+    if (!m_pDevice) {
+        qJsonRpcDebug() << "called without device";
+        return;
+    }
+
+    m_aBuffer.append(m_pDevice.data()->readAll());
+    while (!m_aBuffer.isEmpty()) {
+        int dataSize = findJsonDocumentEnd(m_aBuffer);
+        if (dataSize == -1) {
+            return;
+        }
+
+        QJsonParseError error;
+        QJsonDocument document = QJsonDocument::fromJson(m_aBuffer.mid(0, dataSize + 1), &error);
+        if (document.isEmpty()) {
+            if (error.error != QJsonParseError::NoError) {
+                qJsonRpcDebug() << error.errorString();
+            }
+
+            break;
+        }
+
+        m_aBuffer = m_aBuffer.mid(dataSize + 1);
+        if (document.isArray()) {
+            qJsonRpcDebug() << "bulk support is current disabled";
+        } else if (document.isObject()){
+            qJsonRpcDebug() << "received: " << document.toJson(QJsonDocument::Compact);
+            DapRpcMessage message = DapRpcMessage::fromObject(document.object());
+            Q_EMIT messageReceived(message);
+
+            if (message.type() == DapRpcMessage::Response ||
+                message.type() == DapRpcMessage::Error) {
+                if (m_replies.contains(message.id())) {
+                    QPointer<DapRpcServiceReply> reply = m_replies.take(message.id());
+                    if (!reply.isNull()) {
+                        reply->setResponse(message);
+                        reply->finished();
+                    }
+                }
+            } else {
+                processRequestMessage(message);
+            }
+        }
+    }
+}
+
+void DapRpcSocket::setIODevice(QIODevice *pDevice)
+{
+    m_pDevice = pDevice;
+    connect(m_pDevice, SIGNAL(readyRead()), this, SLOT(processIncomingData()));
+}
+
+void DapRpcSocket::processRequestMessage(const DapRpcMessage &asMessage)
+{
+    Q_UNUSED(asMessage)
+}
diff --git a/DapRPCProtocol/DapRpcSocket.h b/DapRPCProtocol/DapRpcSocket.h
new file mode 100644
index 0000000..9a2836d
--- /dev/null
+++ b/DapRPCProtocol/DapRpcSocket.h
@@ -0,0 +1,74 @@
+#ifndef DapRPCSOCKET_H
+#define DapRPCSOCKET_H
+
+#include <QObject>
+#include <QIODevice>
+#include <QPointer>
+#include <QTimer>
+#include <QEventLoop>
+#include <QDebug>
+#include <QJsonDocument>
+
+#include "DapRpcServiceProvider.h"
+#include "DapRpcMessage.h"
+#include "DapRpcServiceReply.h"
+
+#define DEFAULT_MSECS_REQUEST_TIMEOUT (30000)
+
+class DapRpcSocket : public QObject
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(DapRpcSocket)
+
+    QPointer<QIODevice> m_pDevice;
+    QByteArray m_aBuffer;
+    QHash<int, QPointer<DapRpcServiceReply>> m_replies;
+    int m_defaultRequestTimeout;
+
+protected:
+    virtual void processRequestMessage(const DapRpcMessage &asMessage);
+
+public:
+    explicit DapRpcSocket(QObject *apParent = nullptr);
+    explicit DapRpcSocket(QIODevice *apDevice, QObject *apParent = nullptr);
+    virtual ~DapRpcSocket();
+
+    virtual bool isValid() const;
+    void setDefaultRequestTimeout(int aiMsecs);
+    int getDefaultRequestTimeout() const;
+
+    void setIODevice(QIODevice *pDevice);
+
+signals:
+    void messageReceived(const DapRpcMessage &asMessage);
+
+private slots:
+    virtual void processIncomingData();
+    int findJsonDocumentEnd(const QByteArray &aJsonData);
+    void writeData(const DapRpcMessage &asMessage);
+
+public slots:
+    virtual void notify(const DapRpcMessage &asMessage);
+    virtual DapRpcMessage sendMessageBlocking(const DapRpcMessage &asMessage, int aMsecs = DEFAULT_MSECS_REQUEST_TIMEOUT);
+    virtual DapRpcServiceReply *sendMessage(const DapRpcMessage &asMessage);
+    DapRpcMessage invokeRemoteMethodBlocking(const QString &asMethod, int aMsecs, const QVariant &arg1 = QVariant(),
+                                               const QVariant &arg2 = QVariant(), const QVariant &arg3 = QVariant(),
+                                               const QVariant &arg4 = QVariant(), const QVariant &arg5 = QVariant(),
+                                               const QVariant &arg6 = QVariant(), const QVariant &arg7 = QVariant(),
+                                               const QVariant &arg8 = QVariant(), const QVariant &arg9 = QVariant(),
+                                               const QVariant &arg10 = QVariant());
+    DapRpcMessage invokeRemoteMethodBlocking(const QString &asMethod, const QVariant &arg1 = QVariant(),
+                                               const QVariant &arg2 = QVariant(), const QVariant &arg3 = QVariant(),
+                                               const QVariant &arg4 = QVariant(), const QVariant &arg5 = QVariant(),
+                                               const QVariant &arg6 = QVariant(), const QVariant &arg7 = QVariant(),
+                                               const QVariant &arg8 = QVariant(), const QVariant &arg9 = QVariant(),
+                                               const QVariant &arg10 = QVariant());
+    DapRpcServiceReply *invokeRemoteMethod(const QString &asMethod, const QVariant &arg1 = QVariant(),
+                                             const QVariant &arg2 = QVariant(), const QVariant &arg3 = QVariant(),
+                                             const QVariant &arg4 = QVariant(), const QVariant &arg5 = QVariant(),
+                                             const QVariant &arg6 = QVariant(), const QVariant &arg7 = QVariant(),
+                                             const QVariant &arg8 = QVariant(), const QVariant &arg9 = QVariant(),
+                                             const QVariant &arg10 = QVariant());
+};
+
+#endif // DapRPCSOCKET_H
diff --git a/DapRPCProtocol/DapRpcTCPServer.cpp b/DapRPCProtocol/DapRpcTCPServer.cpp
new file mode 100644
index 0000000..3c361b1
--- /dev/null
+++ b/DapRPCProtocol/DapRpcTCPServer.cpp
@@ -0,0 +1,110 @@
+#include "DapRpcTCPServer.h"
+
+DapRpcTCPServer::DapRpcTCPServer(QObject *apParent)
+    : QTcpServer(apParent)
+{
+
+}
+
+void DapRpcTCPServer::notifyConnectedClients(const DapRpcMessage &message)
+{
+    DapRpcAbstractServer::notifyConnectedClients(message);
+}
+
+void DapRpcTCPServer::notifyConnectedClients(const QString &method, const QJsonArray &params)
+{
+    DapRpcAbstractServer::notifyConnectedClients(method, params);
+}
+
+DapRpcTCPServer::~DapRpcTCPServer()
+{
+    foreach (QTcpSocket *socket, m_socketLookup.keys()) {
+        socket->flush();
+        socket->deleteLater();
+    }
+    m_socketLookup.clear();
+
+    foreach (DapRpcSocket *client, m_clients)
+        client->deleteLater();
+    m_clients.clear();
+}
+
+bool DapRpcTCPServer::listen(const QString &asAddress, quint16 aPort)
+{
+    return ((asAddress.isNull() || asAddress.isEmpty()) ?
+                QTcpServer::listen(QHostAddress::Any, aPort) :
+                QTcpServer::listen(QHostAddress(asAddress), aPort));
+}
+
+bool DapRpcTCPServer::addService(DapRpcService *apService)
+{
+    if (!DapRpcServiceProvider::addService(apService))
+        return false;
+
+    connect(apService, SIGNAL(notifyConnectedClients(DapRpcMessage)),
+               this, SLOT(notifyConnectedClients(DapRpcMessage)));
+    connect(apService, SIGNAL(notifyConnectedClients(QString,QJsonArray)),
+               this, SLOT(notifyConnectedClients(QString,QJsonArray)));
+    return true;
+}
+
+bool DapRpcTCPServer::removeService(DapRpcService *apService)
+{
+    if (!DapRpcServiceProvider::removeService(apService))
+        return false;
+
+    disconnect(apService, SIGNAL(notifyConnectedClients(DapRpcMessage)),
+                  this, SLOT(notifyConnectedClients(DapRpcMessage)));
+    disconnect(apService, SIGNAL(notifyConnectedClients(QString,QJsonArray)),
+                  this, SLOT(notifyConnectedClients(QString,QJsonArray)));
+    return true;
+}
+
+void DapRpcTCPServer::clientDisconnected()
+{
+    QTcpSocket *tcpSocket = static_cast<QTcpSocket*>(sender());
+    if (!tcpSocket) {
+        qJsonRpcDebug() << "called with invalid socket";
+        return;
+    }
+
+    if (m_socketLookup.contains(tcpSocket)) {
+        DapRpcSocket *socket = m_socketLookup.take(tcpSocket);
+        m_clients.removeAll(socket);
+        socket->deleteLater();
+    }
+
+    tcpSocket->deleteLater();
+    emit onClientDisconnected();
+}
+
+void DapRpcTCPServer::messageProcessing(const DapRpcMessage &asMessage)
+{
+    DapRpcSocket *socket = static_cast<DapRpcSocket*>(sender());
+    if (!socket) {
+        qJsonRpcDebug() << "called without service socket";
+        return;
+    }
+
+    processMessage(socket, asMessage);
+}
+
+void DapRpcTCPServer::incomingConnection(qintptr aSocketDescriptor)
+{
+    QTcpSocket *tcpSocket = new QTcpSocket(this);
+    if (!tcpSocket->setSocketDescriptor(aSocketDescriptor))
+    {
+        qJsonRpcDebug() << "can't set socket descriptor";
+        tcpSocket->deleteLater();
+        return;
+    }
+
+    QIODevice *device = qobject_cast<QIODevice*>(tcpSocket);
+    DapRpcSocket *socket = new DapRpcSocket(device, this);
+    connect(socket, SIGNAL(messageReceived(DapRpcMessage)),
+              this, SLOT(_q_processMessage(DapRpcMessage)));
+    m_clients.append(socket);
+    connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(_q_clientDisconnected()));
+    m_socketLookup.insert(tcpSocket, socket);
+    emit onClientConnected();
+}
diff --git a/DapRPCProtocol/DapRpcTCPServer.h b/DapRPCProtocol/DapRpcTCPServer.h
new file mode 100644
index 0000000..00b2e47
--- /dev/null
+++ b/DapRPCProtocol/DapRpcTCPServer.h
@@ -0,0 +1,44 @@
+#ifndef DapRPCTCPSERVER_H
+#define DapRPCTCPSERVER_H
+
+#include <QObject>
+#include <QTcpSocket>
+#include <QTcpServer>
+
+#include "DapRpcSocket.h"
+#include "DapRpcAbstractServer.h"
+
+class DapRpcTCPServer : public QTcpServer, public DapRpcAbstractServer
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(DapRpcTCPServer)
+
+    QHash<QTcpSocket*, DapRpcSocket*> m_socketLookup;
+
+protected:
+    virtual void incomingConnection(qintptr aSocketDescriptor);
+
+public:
+    explicit DapRpcTCPServer(QObject *apParent = nullptr);
+    virtual ~DapRpcTCPServer();
+
+    virtual bool listen(const QString &asAddress = QString(), quint16 aPort = 0);
+
+    bool addService(DapRpcService *apService);
+    bool removeService(DapRpcService *apService);
+
+signals:
+    void onClientConnected();
+    void onClientDisconnected();
+
+protected slots:
+    void clientDisconnected();
+    void messageProcessing(const DapRpcMessage &asMessage);
+
+    // DapRpcAbstractServer interface
+public slots:
+    void notifyConnectedClients(const DapRpcMessage &message);
+    void notifyConnectedClients(const QString &method, const QJsonArray &params);
+};
+
+#endif // DapRPCTCPSERVER_H
diff --git a/KelvinDashboardGUI/qml.qrc b/KelvinDashboardGUI/qml.qrc
index 9bc4dd2..8d93e1b 100755
--- a/KelvinDashboardGUI/qml.qrc
+++ b/KelvinDashboardGUI/qml.qrc
@@ -12,8 +12,6 @@
         <file>DapUiQmlWidgetChainWallet.ui.qml</file>
         <file>DapUiQmlScreenDialog.qml</file>
         <file>Resources/Icons/icon.png</file>
-        <file>Resources/Icons/icon.ico</file>
-        <file>Resources/Icons/add.png</file>
         <file>DapUiQmlScreenAbout.ui.qml</file>
         <file>DapQmlScreenAbout.qml</file>
         <file>Resources/Icons/iconErrorNetwork.png</file>
@@ -22,5 +20,7 @@
         <file>DapUiQmlWidgetDelegateForm.ui.qml</file>
         <file>DapUiQmlScreenChangeWidget.qml</file>
         <file>DapUiQmlWidgetChainNodeLogs.ui.qml</file>
+        <file>Resources/Icons/add.png</file>
+        <file>Resources/Icons/icon.ico</file>
     </qresource>
 </RCC>
diff --git a/KelvinDashboardService/Resources/Icons/iconErrorNetwork.png b/KelvinDashboardService/Resources/Icons/iconErrorNetwork.png
new file mode 100644
index 0000000000000000000000000000000000000000..57fa419adeeb2cd4c7de38cf11d9ca445b628f25
GIT binary patch
literal 2548
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
KMNUMnLSTaD&fT~G

literal 0
HcmV?d00001

diff --git a/KelvinDashboardService/Resources/Icons/iconNetwork.png b/KelvinDashboardService/Resources/Icons/iconNetwork.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa2d0ac25a1a88aeebe52eb8cb9dada732a46287
GIT binary patch
literal 2300
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
W0d1+f8X+?P0000<MNUMnLSTYk>N@HG

literal 0
HcmV?d00001

-- 
GitLab