Tunnel Client Example

A client that creates a tunnel connection to a KNX/netIP server.

Tunnel Client shows how to create a tunnel connection to a KNX/netIP server.

Usage

Here are the parameters that the client accepts:

    Usage: ./tunnelclient [options]

Options:
  -h, --help                       Displays this help.
  -t, --timeout <timeout>          The heartbeat timeout in seconds.
  -n, --nat                        Use Network Address Translation to traverse
                                   across network routers.
  --localAddress <localAddress>    The local IP address used for the control
                                   endpoint.
  --localPort <localPort>          The local UDP port used for the control
                                   endpoint.
  --remoteAddress <remoteAddress>  The remote IP address used by the server the
                                   control endpoint.
  --remotePort <remotePort>        The remote UDP port used by the server the
                                   control endpoint.

The usual way to run the client is:

    ./tunnelclient --remoteAddress 10.9.78.81  --localAddress 10.9.78.59
Sending connect request: 0x06100205001a08010a094e3bbf0b08010a094e3bbf0b04040200
Type 'quit' to stop the application.
Received connect response: 0x0610020600143d0008010a094e510e5704041103
Sending connection state request: 0x0610020700103d0008010a094e3bbf0b
Received connection state response: 0x0610020800083d00

This is the initial state of the client. The client pauses indefinitely waiting for keyboard interaction. The user can then send KNX frames by pasting the stream of bytes directly into the terminal. Pressing Enter will notify the client to encapsulate the stream of bytes in a KNX tunneling request and send it to the KNX server throughout the tunnel. This can be seen below:

1100b4e000000002010000
Sending tunneling request:0x061004200015043d00001100b4e000000002010000
Received tunneling acknowledge: 0x06100421000a043d0000
Received tunneling request: 0x061004200015043d00002e00b4e011030002010000
Sending tunneling acknowledge: 0x06100421000a043d0000
Received tunneling request: 0x061004200015043d01002900b4e011010002010040
Sending tunneling acknowledge: 0x06100421000a043d0100

By typing the word quit in the terminal the client will disconnect and terminate.

quit
Sending disconnect request: 0x0610020900103d0008010a094e3bbf0b
Received disconnect response: 0x0610020a00083d00

Implementation

In the main function, we create a tunnel connection by instantiating the QKnxNetIpTunnel class. This class will manage the tunnel connection.

int main(int argc, char *argv[])
{
    ...
    tunnel.setLocalAddress(QHostAddress(parser.value("localAddress")));

All of the CLI parameters are used to configure the QKnxNetIpTunnel instance. A handler is installed to capture the signal QKnxNetIpTunnel::disconnected that will terminate the connection.

    ...
    QObject::connect(&tunnel, &QKnxNetIpTunnel::disconnected, &app, &QCoreApplication::quit);

The connection is established using the server address and port number previously provided and calling the method QKnxNetIpTunnel::connectToHost.

int main(int argc, char *argv[])
{
    ...
    tunnel.connectToHost(QHostAddress(parser.value("remoteAddress")),
        parser.value("remotePort").toUInt());
    ...
    QTextStream input(stdin, QIODevice::ReadOnly);
#ifdef Q_OS_WIN
    QWinEventNotifier notifier(GetStdHandle(STD_INPUT_HANDLE));
    QObject::connect(&notifier, &QWinEventNotifier::activated, [&](HANDLE) {
#else
    QSocketNotifier notifier(STDIN_FILENO, QSocketNotifier::Read);
    QObject::connect(&notifier, &QSocketNotifier::activated, [&](int) {
#endif
        auto tmp = input.readLine().toLatin1();
        if (tmp != "quit") {
            const auto bytes = QKnxByteArray::fromHex(tmp);
            auto frame = QKnxLinkLayerFrame::builder()
                .setData(bytes)
                .setMedium(QKnx::MediumType::NetIP)
                .createFrame();
            tunnel.sendFrame(frame);
        } else {
            tunnel.disconnectFromHost();
        }
    });

    if (tunnel.error() == QKnxNetIpTunnel::Error::None) {
        qInfo().noquote() << "Type 'quit' to stop the application.";
        app.exec();
    } else {
        qInfo().noquote() << tunnel.error() << tunnel.errorString();
    }

    return 0;
}

The signals QWinEventNotifier::activated (from Windows) and QSocketNotifier::activated (from other platforms) are used for detecting user data ready to be read from the terminal. The stream of bytes is captured by a QTextStream instance. Then, a KNX link layer frame is built from it and sent over the tunnel connection.

Files:

© 2023 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.