Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unnecessary Port Binding for ArtNet Sender #26

Open
tylerstraub opened this issue May 31, 2024 · 1 comment
Open

Unnecessary Port Binding for ArtNet Sender #26

tylerstraub opened this issue May 31, 2024 · 1 comment

Comments

@tylerstraub
Copy link

When using the artnet4j library in a Processing 4 application, the library creates a UDP port binding even when acting as an ArtNet Sender. This behavior leads to binding conflicts with other applications that may need to communicate locally, such as DigiShow LINK and QLC+, which do not bind to a specific port when sending ArtNet data.

Expected Behavior

ArtNet Sender applications should use an ephemeral port for sending data and should not bind to a specific port (e.g. 6454). Only ArtNet Receiver applications require a static bind to listen for incoming ArtNet packets.

Observed Behavior

The artnet4j library binds a port regardless of whether it is used as a Sender or Receiver. This causes conflicts when other applications attempt to bind to the same port.

Steps to Reproduce

  1. Condition 1: Sender = Processing 4, Receiver = DigiShow LINK

    • Launch DigiShow LINK before Processing 4: No port conflict.
    • Launch Processing 4 before DigiShow LINK: Port conflict occurs.
  2. Condition 2: Receiver = Processing 4, Sender = DigiShow LINK

    • Launch DigiShow LINK before Processing 4: No port conflict.
    • Launch Processing 4 before DigiShow LINK: No port conflict.
  3. Extra Tests:

    • DigiShow LINK Sender launched in isolation: No binding on port 6454.
    • Processing 4 Sender launched in isolation: Binding occurs on port 6454.

Diagnostic Results

Using lsof on macOS reveals that DigiShow LINK does not create a binding when sending ArtNet, but the Processing 4 application using artnet4j does:

  • DigiShow LINK as Sender (no binding):

    sudo lsof -i :6454

    Output: (empty)

  • Processing 4 as Sender (binding occurs):

    sudo lsof -i :6454
    COMMAND   PID        USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
    java    27353 tylerstraub   21u  IPv6 0xe877803c50f1714e      0t0  UDP *:6454

Validation of Ephemeral Port Usage in DigiShow LINK

After examining the DigiShow LINK source code, it was confirmed that DigiShow LINK uses ephemeral ports for sending ArtNet data. This approach prevents port conflicts and allows the application to coexist with other applications that need to bind as a Reciever.

Key Points from dgs_artnet_interface.cpp:

  1. UDP Port Definition:

    #define ARTNET_UDP_PORT 6454
  2. UDP Socket Initialization:

    m_udp = new QUdpSocket();
  3. Binding the UDP Socket for Input:

    if (m_interfaceInfo.mode == INTERFACE_ARTNET_INPUT) {
        // for artnet receiver
        m_dataAll.clear();
        m_udp->bind(m_udpPort);
        connect(m_udp, SIGNAL(readyRead()), this, SLOT(onUdpDataReceived()));
    }
  4. Setting Up the UDP Socket for Output:

    if (m_interfaceInfo.mode == INTERFACE_ARTNET_OUTPUT) {
        // for artnet sender
        m_sequence = 0;
        const unsigned char bytes[] = {
            0x41, 0x72, 0x74, 0x2d, 0x4e, 0x65, 0x74, 0x00,  // Art-Net
            0x00, 0x20, // opcode ARTNET_POLL
            0x00, 0x0e, // version
            0x00, 0x00  // take-to-me
        };
        m_udp->writeDatagram((const char*)bytes, sizeof(bytes),
                             m_udpHost,
                             (quint16)m_udpPort);
        m_timer = new QTimer();
        connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimerFired()));
        m_timer->setTimerType(Qt::PreciseTimer);
        m_timer->setSingleShot(false);
        m_timer->setInterval(1000 / frequency);
        m_timer->start();
    }

Suggested Fix

Modify the artnet4j library to avoid static binding its port when acting solely as a Sender. Instead, it should use an ephemeral port for sending data.

Proposed Code Changes

Here is an example modification to the ArtNetServer class:

public void start(InetAddress networkAddress, boolean isReceiver) throws SocketException, ArtNetException {
    if (broadCastAddress == null) {
        setBroadcastAddress(DEFAULT_BROADCAST_IP);
    }
    if (socket == null) {
        socket = new DatagramSocket(null);
        socket.setReuseAddress(true);
        socket.setBroadcast(true);

        if (isReceiver) {
            if (networkAddress == null)
                networkAddress = socket.getLocalAddress();

            socket.bind(new InetSocketAddress(networkAddress, port));
            logger.info("Art-Net server started at: " + networkAddress.getHostAddress() + ":" + port);
        } else {
            logger.info("Art-Net server started as sender using ephemeral port.");
        }

        for (ArtNetServerListener l : listeners) {
            l.artNetServerStarted(this);
        }
        isRunning = true;
        serverThread = new Thread(this);
        serverThread.start();
    } else {
        throw new ArtNetException(
                "Couldn't create server socket, server already running?");
    }
}

In the application code, specify whether the instance is acting as a Sender or Receiver:

ArtNetServer server = new ArtNetServer();
server.start(null, false); // For sender
// or
server.start(null, true);  // For receiver

Conclusion

By implementing these changes, the artnet4j library will avoid unnecessary port bindings when acting as a Sender, preventing conflicts with other applications and allowing for more flexible execution order.

tylerstraub added a commit to tylerstraub/artnet4j that referenced this issue May 31, 2024
Modify the artnet4j library to avoid static binding its port when acting solely as a Sender. Instead, it should use an ephemeral port for sending data.
tylerstraub added a commit to tylerstraub/artnet4j that referenced this issue May 31, 2024
Modify the artnet4j library to avoid static binding its port when acting solely as a Sender. Instead, it should use an ephemeral port for sending data.
@tylerstraub
Copy link
Author

tylerstraub commented May 31, 2024

PR sent: #27

This branch implements a boolean option to start() which launches the port binding in "ephemeral mode".

I'm not sure if this fix is necessarily ideal for main branch but it exists now for anyone out there struggling with this same problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant