[Patch] svdrp.c SO_REUSE_PORT

  • Hi,


    ich habe das Problem, daß der VDR kein Bind auf UDP Port 6419 machen kann, wenn bereits andere Programme diesen Port gebunden haben.

    Die Fehlermeldung ist folgende:

    Code
    Oct  6 09:22:10 dev vdr: [24382] SVDRP client handler thread started (pid=24342, tid=24382, prio=low)
    Oct  6 09:22:10 dev vdr: [24382] SVDRP dev opening port 6419/udp
    Oct  6 09:22:10 dev vdr: [24382] ERROR (svdrp.c,161): Die Adresse wird bereits verwendet
    Oct  6 09:22:10 dev vdr: [24382] SVDRP client handler thread ended (pid=24342, tid=24382)

    Der angehängte patch setzt einfach nur noch die zusätzliche Socket Option SO_REUSE_PORT (falls vorhanden). Damit funktioniert der Bind des VDR problemlos.

    Wobei ich nicht verstehe, warum das Script peerdemo auf Anhieb ohne Probleme funktioniert hat und nur der VDR nicht wollte.


    Zabrimus

  • Warum lauschen bei dir mehrere Programme auf dem gleichen Port? Wer bekommt denn die Verbindung, wenn man eine zu dem Port aufbaut?


    Lars

  • Es geht nur um den UDP Port 6419, den der VDR zum Discovery (Broadcast) verwendet. Der "normale" SVDRP Port kann und soll natürlich nicht problemlos geshared werden.

    Bei mir sind es in Produktion und Entwicklung: Mehrere VDR Instanzen und auch jonglisto-ng, der die Möglichkeit bekommen soll auf das Discovery zu reagieren.


    Edit:

    Der Einwand war berechtigt. Ich habe den Patch so verändert, daß wirklich nur der UDP port das SO_REUSE_PORT erhält.


    Zabrimus

  • Seltsam. Ich hatte das explizit ausprobiert und dachte, dass das


    // allow it to always reuse the same port:

    int ReUseAddr = 1;

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ReUseAddr, sizeof(ReUseAddr));

    gereicht hätte. Wieso hat das dann bei mir funktioniert und bei dir nicht?


    Klaus

  • Mittlerweile vermute ich, daß die Java Implementierung, die ich verwende (Netty, https://netty.io/) irgendwas mit den Sockets doch anders macht, als die C-Variante.


    Und bezüglich SO_REUSEADDR und SO_REUSEPORT gibt es eine Menge Unklarheiten. Ein User hat auf Stackoverflow (https://stackoverflow.com/ques…hey-differ-do-they-mean-t) versucht, das umfassend zu erklären, aber so richtig viel schlauer bin ich immer noch nicht. SO_REUSEPORT gibt es seit Linux Kernel 3.9 und andere Socketimplementierungen/Systeme haben das oder auch nicht.

    Ich habe gerade nochmal versucht, es kreuzweise in der Java-Implementierung und im VDR zu ändern. SO_REUSEPORT mal an und aus, in Java und in C, aber die einzige Variante, die tatsächlich auf meinem System funktioniert ist, wenn beide Sockets das SO_REUSEPORT erhalten. Warum auch immer das SO_REUSEADDR nicht ausreicht. Vielleicht müsste ich den Bind auf IPv4 begrenzen oder .... keine Ahnung.


    In der Zielkonfiguration erhalten alle VDR und die Java-Applikation eigene IPs, sofern sollte es später kein Problem geben. Es ist nur während der Entwicklung nicht hilfreich, wenn der Bind der Sockets fehlschlägt.


    Edit: Ein Bind auf die IPv4-only funktioniert auch nicht :(


    Zabrimus

  • Ich muss auf das Problem noch einmal zurückkommen, weil ich es mittlerweile wieder habe (ohne den Patch).


    Sobald ich peerdemo und VDR auf derselben Maschine starten will, bekomme ich - abhängig von der Startreihenfolge - entweder einen Fehler im VDR, daß der UDP bind nicht funktioniert hat oder peerdemo meldet, daß der Port schon belegt sei, wobei der SVDRP TCP Port mit dem Parameter -p schon woanders hingelegt wurde. Andere Programme lauschen auf keinen der beiden Ports (UDP und dem SVDRP TCP Port von peerdemo).


    Mit dem Patch kann ich sowohl peerdemo, als auch VDR auf derselben Maschine laufen lassen (Debian Stable, Kernel 4.9).

    Im peerdemo ist der reuse-Port auch aktiviert:
    $UdpSocket = new IO::Socket::INET( LocalPort => $UdpPort, Proto => "udp", ReusePort => 1) || die "$!";


    Ein weiterer Test hat aber dann ergeben, daß ein ReuseAddr (statt ReusePort) im peerdemo zumindest mein Problem auch löst.


    Zabrimus

  • Genau. Die Änderung nur in peerdemo hat den gewünschten Erfolg gebracht - zumindest bei mir. Soweit ich das verstanden habe, bestimmt der erste Socket-Bind (ob in peerdemo oder VDR) die notwendigen Parameter für den Bind anderer Programme.


    Zabrimus

  • Ist das hier die richtige Änderung, mit der es bei dir funktioniert?

    Diff
    --- peerdemo    2018/04/10 17:20:58     5.0
    +++ peerdemo    2019/03/11 10:19:46
    @@ -37,7 +37,7 @@
     $UdpPort = $DefaultSvdrpPort;
    
     $TcpSocket = new IO::Socket::INET(Listen => 5, LocalPort => $TcpPort, Proto => "tcp", ReusePort => 1) || die "$!";
    -$UdpSocket = new IO::Socket::INET(             LocalPort => $UdpPort, Proto => "udp", ReusePort => 1) || die "$!";
    +$UdpSocket = new IO::Socket::INET(             LocalPort => $UdpPort, Proto => "udp", ReuseAddr => 1) || die "$!";
     $SvdrpSelect = new IO::Select($TcpSocket);
     setsockopt($UdpSocket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!', 0, 1000)); # 1ms timeout on UDP socket

    Klaus

  • Genau. Ich habe nur den ReusePort zu ReuseAddr geändert:


    Code
    -$TcpSocket = new IO::Socket::INET(Listen => 5, LocalPort => $TcpPort, Proto => "tcp", ReusePort => 1) || die "$!";
    -$UdpSocket = new IO::Socket::INET(             LocalPort => $UdpPort, Proto => "udp", ReusePort => 1) || die "$!";
    +$TcpSocket = new IO::Socket::INET(Listen => 5, LocalPort => $TcpPort, Proto => "tcp", ReuseAddr => 1) || die "$!";
    +$UdpSocket = new IO::Socket::INET(             LocalPort => $UdpPort, Proto => "udp", ReuseAddr => 1) || die


    Zabrimus

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!