SocketServer: Einige Fragen und Diskussion

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo ihr Lieben,

    meiner erster Post, juhuuu :D.

    Ich habe eine Projekt in der Uni am laufen, bei dem ich einen Socketserver aufsetzten soll. Dieser soll Systemdaten dann in Protokollen schreiben. Die Systemdaten sollen von verschiedenen Rechnern immer auf denb Socket geschickt werden. Nun gut, ich habe mal damit begonnen, einen simplen Socketserver aufzusetzten. Dieser kann bis jetzt nur einfache Verbindungen. Erst später werde ich durch Threading mehrere Clients möglich machen. Da man ja in der Informatik das Rad nie neu erfinden soll, habe ich mich bei dem Grundserver auf dieses Tutorial hier gestützt (Server und Client sollen in C++ programmiert werden).

    Server:

    Client:


    Nun habe ich folgendes Problem: Der eine Pi ist der Server, der andere der Client. Auf beiden hat der Code wunderbar kompiliert. Auch öffnen funktioniert auf beiden wunderbar. Nur findet der CLient den Server nicht. Nach aber etwa einer Minute startet der Client den Eingabevorgang, sprich er wirft mir die Meldung heraus, dass er sich verbunden hat. Das kann aber nicht sein, weil der Server wiederrum nichts meldet. DIeser wartet einfach auf eine Verbindung. Wenn ich beim Clienten dann irgendetwas eingebe, schmiert er sofort ab.

    Ich bin etwas am verzweifeln, weil das noch der einfachste Part der Aufgabe ist, und ich auch eigentlich dachte, dass das leicht funktioniert. Ich finde auch einfach keinen Fehler im Code. Gibts vielleicht ne Möglichkeit herauszufinden, warum der Client denkt, er sei verbunden?


    Vielen Dank im Voraus und liebe Grüße,
    Philipp

    Einmal editiert, zuletzt von Flippo710 (11. Januar 2017 um 16:31)

  • Hi,

    ja, da hast du recht. Er hatte in seinem Video allerdings server_addr auch nicht belegt. Ich ging davon aus, dass er automatisch im Netzwerk nach den Servern auf den Ports sucht und somit keine IP braucht. Wie könnte ich denn die IP dem server_addr übergeben?

  • Hallo zusammen,

    habe den Thread jetzt nochmal umgenannt, da ich keinen neuen Thread öffnetn möchte, und über mein Projekt hinweg noch einige Fragen denke ich kommen werden.

    Ich habe auf Basis des Servers von dreamshader einige DInge verändert. Hat auch alles wunderbar geklappt, dazu gehört Protokollierung des Chats und einige weitere Kleinigkeiten. Allerdings hänge ich jetzt an einer total simplen Sache, bei der irgendwas nicht funktioniert.

    Ich möchte, wenn der Client "quit" sendet, dass der Server die Verbindung trennt. Der Buffer im Server ist ein char-Array "dataBuffer". Um diesen zu vergleichen, soweit man dem Internet und meinen Info 1 Erinnerung glauben darf, kann ich diese mit strcmp vergleichen, dass bedeutet in meinem Fall also:


    Code
    if (strcmp(dataBuffer, "quit") == 0)
    {
    close(sockfd);
    } else
    {
    blub}

    Nun macht er aber genau nichts. Sprich, das quit kommt an, aber beendet die Verbindung nicht. Ich habe auch schon versucht einzelne Array-Elemtene zu vergleichen oder simpel die einzelnen Chars zu vergleichen, aber er beendet nie die Verbindung. Ich bin jetzt die ganze Zeit am überlegen, ob vielleicht nicht nur das quit übertragen wird, sondern noch irgendwelche bits oder bytes oder chars dazwzischenhängen, sodass der Vergleich nie zu stande kommen kann.

    Vielleicht hat einer von euch ja eine Idee.

    LG

  • Ohne mehr Code zu sehen kann man das nicht abschliessend beurteilen. Ein paar Tipps:

    - lern den GDB zu benutzen, um mittels EInzelschrittausfuehrung zu sehen, was *wirklich* passiert. dazu gehört, dass du mit Debugsymbolen bauen musst (-g als Compileroption) und ohne Optimierung (-O0)
    - strings in C sind Nullterminiert. Schickst du "quit" oder "quit\0"? Wenn ersteres, ist das klar ein Fehler.

  • Code
    if (strcmp(dataBuffer, "quit") == 0)
    {
    close(sockfd);
    } else
    {
    blub}

    Schrecklicher Programmierstyle...
    Was hälst du von

    Code
    if (strcmp(dataBuffer, "quit") == 0) {
        close(sockfd);
    } else {
        blub;
    }

    Ist doch eigentlich viel besser zu lesen oder nicht? :-/

  • meigrafd: Ja, mein Code ist auch so aufgebaut, es hat nur beim Einfügen von der Codefunktion hier im Forum die Formatierung zerhauen..:-)

    @deets: Mit der Nullterminierung dachte ich vorerst auch, allerdings funktionierte ein strcmp auch mit der angehängten "\0" nicht. Dafür sind meine Kentnisse in C++ glaube ich auch noch zu gering. Außerdem kann ich mir ja das gesendete direkt nach dem Empfang ausgeben im Server und dort wird auch nur das Gesendete ausgegeben. Habe aber nochmal den Code der Servers angehängt. Der des Clienten ist 1:1 der von dreamshader, bis auf die NUMBER_OF_WRITES, die ich nichtmehr nutze, sondern eine while Schleife.

    Server:

    Client von dreamshader:

  • Ich sehe in deinem client code jetzt nicht, wo der client das "quit" schickt. Damit ist es auch nicht wirklich gut zu beurteilen, was da rüber kommt. Allerdings setzt du den Buffer vorher auf \0...

    Was gibt dir denn dein printf aus?

  • Hi, ich sende das quit ja über die Konsole. Genauso wie ich andere Dinge über den Client an den Server schicken kann. Ich öffne das Client- und Serverprogramm und kann dann kommunizieren. In die Konsole gebe ich dann ganz normal quit ein. Genau dieses Quit wird dann auch beim Server auf dem anderen Pi ausgegeben. Und übertragen wird es ja so, dass es im Server in dataBuffer geschrieben wird. Also dachte ich, dass ich dass dann vergleichen kann.

    Kann auch sein, dass ich bei der Funktionsweise irgendwas nicht checke oder verwechsle, deswegen bin ich ja hier, was ihr mir hoffentlich nicht böse nehmt :)

  • Ah, kann es sein, dass du das newline mitschickst? Dann musst du natürlich auf "quit\n" prüfen. Und nochmal: lern den GDB zu benutzen. Das sage ich nicht aus Spass. Debugging ist wichtig. Damit kannst du dir zB die einzelnen Bytes in deinem Buffer betrachten, und sehen, welche Werte sie haben.

    Nachtrag: gerade mal geschaut, ja, fgets behält das newline bei.

  • Servus,
    das mit dem <ENTER> dürfte Dein Problem sein, denke ich ...

    Probier's halt mal mit

    Code
    ...
    #define END_CONNECTION    "quit"
    ...
    if( strncasecmp(buffer, END_CONNECTION, strlen(END_CONNECTION)) == 0 )
    {
    ...


    Damit hättest Du auch gleich Groß-/Kleinschreibung abgedeckt und vergleichst nur 4 Zeichen ...

    btw: erwähnte ich schon, dass Grundlagenforschung evtl. auch ein Thema für Dich sein könnte ;)


    cu,
    -ds-

  • Hi,

    vielen Dank für die Antworten. Mit GDB bin ich gerade dabei, mich dort reinzulesen.

    dreamshader: Ich werde zuhause die Funktion direkt mal ausprobieren. Darf ich noch fragen, warum du per #define einige Dinge immer definierst, anstatt sie einfach einer variablen zuzweisen? Das mit #define hatte ich vorher noch nie gesehen.

    LG und vielen Dank!

  • Defines sind Praeprozessor-Makros und aus Sicht des Compilers konstant. Nach einem #define FOO "pillepalle" wird überall, wo im Code FOO steht, der Text "pillepalle" eingesetzt. Man macht das, um

    - Konstanten statt veränderbare Variablen
    - eine zentrale Stelle zur Definition

    zu haben. In C ist das notwendig, allerdings ist das benutzen des Präprozessors eine umstrittene Angelegenheit, weil er sozusagen eine extra Sprache über der Sprache darstellt. Ein Paper dazu ist zB hier: https://www.cs.cmu.edu/~ckaestne/pdf/ecoop15.pdf

    Wenn es dir ernst ist mit C++ (statt C), dann kann man dort stattdessen zB constexpr benutzen. Ich wuerde dir eher zu C++ raten, nicht nur deswegen - sondern vor allem wegen der besseren Datenstrukturen.

  • Hi,
    dem ist nichts hinzuzufügen ...
    Es ist halt einfacher, das "quit" des Makros (#define) durch z.B. "exit" zu ersetzen, wenn Du es Dir mal anders überlegst. Sonst müsstest Du im gesamten Quellcode die entsprechenden Stellen suchen und ändern (fehleranfälliger).

    cu,
    -ds-

  • Hallöchen, los seit ihr mich noch nicht, hehe :P

    Also, habe noch ne ganze Weile weitergearbeitet, und es hat auch alles funktioniert. Die Protokollierung hatte geklappt und auch die Vorgabe, dass nur die Befehle mit "hostname" protokolliert werden funktioniert einwandfrei.

    Nun an den zweiten Teil der Aufgabe. Ich soll mehrere Clienten zulassen. Habe mir einige Ideen aus dem Internet geholt und folgenden Code zusammengeschustert:

    Natürlich hatte ich einige Fehler in der Kompilierung, die ich aber nach und nach alle ausgemärzt habe. Nun folgendes Problem, hier mal die Konsolenausgabe:

    Code
    Log-Datei wurde erstellt!
    Socket wird initialisiert...
    Socket wurde initialisiert!
    Prozess erstellt!
    Client aus Funktion akzeptiert.
    Prozess erstellt!
    Client aus Funktion akzeptiert.

    Soweit so gut, naja, zumindest halb. Ich verstehe nicht ganz, warum der Prozess zweimal erstellt wird?! Ich glaube da habe ich irgendwo einen Denkfehler, weil er müsste ja in die Prozesserstellung springen und dann nur einmal den Clienten annehmen, aber er tut es zweimal!? (Client aus Funktion habe ich die Ausgabe nur genannt, damit ich mir sicher sein konnte, dass auch die Funktion arbeitet.)

    Das nächste Problem, irgendwo ist ein Fehler bei der Variablenübergabe. Nachdem die Connection aufgebaut ist, macht er nichtsmehr. Er empfängt nichts. Ich schätze der Fehler liegt daran, dass ich ja die Variablen in der Funktion nochmal definiere. Müsste schätze ich aber eigentlich die Variablen als Parameter der Funktion übergeben, oder? (irgendwas in der Art doprocessing(dataBuffer, etc..)).

    Vielen Dank für die Hilfe und schönes Wochenende!

    LG

  • Servus,
    hab' jetzt nur mal schnell drüber geschaut ...

    Da musst Du schon auch den socket verwenden, den Du als Argument mitgibst ;) ...

    //EDIT: und wenn Du Dich mit zwei clients drauf verbindest, werden mit fork() auch zwei Endpunkte erzeugt ...

    cu,
    -ds-

  • Ich sehe darauf Anhieb diverse Probleme.

    Die doppelte Prozessöffnung liegt daran, dass du den fork nicht richtig behandelst. Das ist ein bis zum Erbrechen behandeltes Thema, das zB in Unix System Programming erläutert wird. Die Kurzform: du bekommst eine positive Zahl welche die PID darstellt im Parent, und 0 im Child. Darüber fehlt dir eine Fallunterscheidung.

    Danach hast du Chaos mit den filedeskriptoren, du schließt den falschen. Auch dazu findet sich viel.

    Ganz generell bin ich mir aber nicht sicher, ob du weißt, was du dir mit fork einhandelst. Es ist eine Sache, einen Prozess zu kontrollieren. Es ist eine ganz andere, einen Zoo davon zu managen.

    Falls du über den Server Daten zwischen Clients austauschen willst, musst du auch noch über Pipes kommunizieren. Oder Shared Memory. Oder beides.

    Ich würde

    - mit asynchronen Mitteln arbeiten, wie select oder epoll.
    - falls du das gesamte Protokoll kontrolllierst, statt sockets Nanomsg benutzen. Oder zeromq. Denn bei dem Spaß, ein Netzwerkprotokolle zu schreiben bist du noch gar nicht angekommen.

  • Hallo,

    vielen Dank schonmal für die Antworten. Du hast recht deets, dass ich von fork relativ wenig Ahnung habe, da ich damit noch nie gearbeitet habe. Ursprünglich wollte ich das ganze mit Threads abwickeln, da wir das auch schon in der Vorlesung hatten. Nun hatte ich mich dann mal schlau gemacht, ob es auch Sinn macht Threads zu verwenden und hatte dann gelesen, dass die meisten eher von Threads abgeraten hatten. Aber du hast mir ja jetzt nochmal dargestellt, dass fork vielleicht nicht der beste Weg ist, sodass ich mich jetzt nochmal an die Threads setzten werde.

    dreamshader: Hatte mich bis Dato ja nur mit einem drauf verbunden :P

Jetzt mitmachen!

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