Beispiel: Server TCP/IP als daemon

  • Moin zusammen,
    ich hab' mir gedacht, es kann für den einen oder anderen ganz hilfreich sein mal eine Vorlage für einen Server zu haben, der als daemon Prozess im Hintergrund läuft und Verbindung(en) über TCP/IP entgegennimmt.

    Der Source-Code im Anhang kann mit

    Code
    gcc -o CommSrv CommSrv.c -lrt


    übersetzt werden. Das funktioniert z.B. unter z.B. Ubuntu aber auch auf dem RPi unter Raspbian.
    Um das Programm nach dem Übersetzen aufzurufen braucht ihr nur

    Code
    ./CommSrv


    in der Kommandozeile einzugeben.

    Kurzbeschreibung:
    zuerst werden die Variablen mit Default-Werten vorbelegt ( initialize() ). Anschliessend wird die Kommandozeile ausgewertet und evtl. Aufruf-Optionen übernommen ( get_arguments() ).
    Soll das Programm als daemon laufen, wird es entsprechend als session-leader in den Hintergrund geschickt ( start_daemon() ).
    Anschliessend wird ein socket aufgemacht ( make_socket() ) und auf eine Verbindungs-Anfrage gewartet ( wait_client() ).
    Die vom Client gesendeten Daten werden empfangen ( get_request() ) ....

    Achtung! Die folgende Textpassage ist durch den ersten Update überholt.
    naja, und dann ist Schluss. Das Programm wirde beendet ( (cleanup_and_exit() ).


    Um das Ganze jetzt aufzumöbeln ist lediglich nötig, eine Schleife um den Block mit dem Kommentar "// while not stop request" zu legen und nach get_request() eine Funktion zur Verarbeitung der CLient Daten einzufügen. Die Schleife kann dann z.B. durch ein Kommando des Client, das z.B. ein entsprechendes Flag setzt, beendet werden.
    Bitte bei UPDATE weiterlesen ...

    Aufruf-Optionen:
    Das Programm kann mit verschiedenen Argumenten aufgerufen werden:
    --verbose=x
    --debug=x
    sind zwei Optionen, die als Beispiel dienen sollen. Gedacht ist die Gesprächigkeit bzw. Debug-Ausgaben des Servers.
    --help zeigt einen kurzen Hilfetext an - das Programm wird danach beendet und
    --foreground setzt den Prozess nicht als daemon in den Hintergrund sondern lässt ihn als ganz normales Programm im Vordergrund laufen.

    Der Source gehört zu einem aktuellen Vorhaben von mir und wird noch entsprechend erweitert.
    Ich hoffe, es ist für den einen oder anderen zumindest als Anschauungs-Material ganz hilfreich.

    //EDIT: Vergessen, sorry ... testen könnt ihr das Ganze z.B. mit telnet:

    Code
    pi@raspberrypi ~ $ ./CommSrv
    pi@raspberrypi ~ $ telnet localhost 6633
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    
    
    Connection closed by foreign host.


    Sobald ihr <ENTER> drückt, wird die Verbindung unterbrochen, weil der daemon sich beendet.
    Auf dem RPi kann es sein, dass ihr telnet erst noch installieren müsst:

    Code
    pi@raspberrypi ~ $ sudo apt-get install telnet

    UPDATE 10.09.2014 23:35
    Ich habe jetzt das Teil noch ein wenig erweitert, und ein paar rs232-Funktionen in einem eigenen File dazu gepackt.
    Der Server (CommSrv.c) und die rs232-Funktionen (CommRs232.c) nutzen dieselbe Struktur für die Einstellungen der seriellen Schnittstelle (in CommRs232.h definiert).
    Diese Struktur wird nach einem Verbindungsaufbau vom Client übermittelt. Dazu habe ich ein ganz einfaches Protokoll definiert und werte die Daten ebenfalls ganz einfach (quick and dirty ... klappt aber und reicht imho zunächst mal vollkommen aus) mittels sscanf() aus.
    Protokoll:
    Übermittelt wird ein ASCII-String, bestehend aus mindestens zwei Feldern die durch ein Trennzeichen voneinander separiert sind. Das erste Feld beinhaltet den Kommandocode, das zweite die Anzahl der Elemente im String. Trennzeichen ist der Doppelpunkt.
    Folgende Kommandos werden erkannt:
    "Q:2"
    Q = Quit-Request, 2 Argumente im Request-String

    "X:2"
    X = Exit-Request, 2 Argumente im Request-String

    "T:3:2"
    T = Set-Debug-Level-Request, 3 Argumente im Request-String, der neue Debug-Level soll 2 sein

    "V:3:5"
    V = Set-Verbose-Level-Request, 3 Argumente im Request-String, der neue Gesprächs-Level soll 5 sein

    ich denke, bis hierher ist alles so weit klar ...
    Q und X führen in CommSrv dazu, dass die Schleife, in dem die Abarbeitung jetzt läuft, beendet wird.
    Mit T und V werden in der Runtime-Struktur die Werte debug_level und verbose_level gesetzt.
    Der letzte Request ist etwas komplizierter, aber nach dem gleichen Schema aufgebaut:

    "D:10:/dev/ttyAMA0:9600:8:N:1:N:0:rw"
    D = Device request ... das soll später mal die eigentliche Funktionalität des Servers werden. Aber weiter:
    Also D = Device-Request, 10 Argumente im Request String.
    Das erste ist das gewünschte Device (/dev/ttyAMA0), es folgen die Schnittstellen-Parameter - also als zweites die Baudrate (9600), die Werte für Anzahl der Datenbits (8), der zu verwendende Parity-Check (N für None) und als siebtes Argument die Anzahl der übermittelten Stoppbits. Das achte Argument definiert den zu verwendenden Handshake (N für keinen) und das neunte Argument gibt an, ob die Schnittstelle asynchron (1) genutzt werden soll. Das zehnte und letzte Feld definiert den Modus, wie das Device geöffnet werden soll, also "r" für lesen, "w" für schreiben oder eben "rw" für beides.

    Der empfangene Request wird zunächst auf Gültigkeit überprüft. Dazu wird das erste Byte mit einer Tabelle verglichen, in der die einzelnen, bekannten Requests aufgelistet sind.
    Wurde der passende Eintrag gefunden, wird der Request mit dem entsprechenden Format-String durch sscanf() auseinandergepflückt.

    Achtung: das soll keine High-Tech Lösung sein. Die Parserei und Auswertung sollte für eine ernsthafte Anwendung dann doch etwas aufwändiger ausfallen. Für meine Zwecke, und ich denke zur Demonstration, tut es imho die angewandte Methode.

    Anschliessend wird noch auf Vollständigkeit des Requests überprüft (sscanf() liefert die Anzahl der erfolgreich eingelesenen Elemente - sie muss mit der Vorgabe im Request übereinstimmen) und das Kommando einfach zurück geschickt :) ...

    Ach ... ich vergass mal wieder: testen kann man das wieder mit telnet. In CommSrv.c steht ein Request-Eintrag als Kommentar ... einfach copy/paste (ohne Anführungszeichen und absenden ...

    An dieser Stelle ist im Moment wieder Schluss ... in CommSrv.c ist das ab etwa Zeile 781
    Aber ... das Spiel geht weiter ....
    Ende des Updates vom 10.09.2014


    Viel Spass damit,
    -ds-


Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!