Frage zur Socket-Praxis: connect, shutdown, close



  • Hallo,

    nachdem ich jetzt eigentlich schon eine funktionierende Socket-Klasse ( mit Ableitungen für Client- und Server-Sockets) habe, muss ich feststellen dass ich doch noch nicht damit vertraut bin.

    Wenn ich als Client auf einen Server zugreife, erstelle ich mir ein Objekt einer ClientSocket-Klasse. Im Konstruktor wird der Socket erstellt:

    m_fdSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    

    Nach dem Konstruktor rufe ich dann einmalig eine Methode auf, die den connect()-Befehl enthält.

    Danach kommt nur noch Senden und Empfangen, erst im Destruktor des Socket-Objekts veranlasse ich ein
    close(m_fdSocket).

    Das ging mit ein paar Geräten, mit denen ich kommuniziert habe, auch gut. Aber jetzt habe ich einen Server, der nach dem Senden seiner Daten wohl ein "shutdown()" veranlasst welches ich bis vor kurzem gar nicht kannte. Jedenfalls kann ich nur einmal Daten zum Server senden, danach erhalte ich ein Antwort. Alle weiteren Versuche, Daten zu senden, schlagen fehl (mit select; errno ist danach auf "success"). Kann's sein dass ich es bisher völlig falsch gemacht habe und ich eigentlich

    - vor jeder Kommunikation ein

    m_fdSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    int iRet = connect(m_fdSocket, (struct sockaddr*)&m_destAddr, sizeof(struct sockaddr_in));
    

    - nach jedem Lesen / Schreiben ein abschließendes shutdown()
    - und nach jeder Kommunikation ein close(m_fdSocket)

    machen sollte?

    Die Kommunikation mit dem Server findet übrigens alle 5 - 120 Sekunden statt und enthält ca. 200-500 Zeichen.

    Dankbar für Tipps zum Good Practice,

    Stephan



  • Das hängt von der Art der Kommunikation statt. HTTP zum Beispiel ist verbindungslos, d.h. nach einem erfolgreichen HTTP Request + HTTP Response wird die Verbindung geschlossen und beim nächsten HTTP Request wieder aufgebaut.
    Bei FTP dagegen bleibt die Verbindung bestehen bis der Transfer abgeschlossen ist.



  • Tippgeber schrieb:

    Das hängt von der Art der Kommunikation statt. HTTP zum Beispiel ist verbindungslos, d.h. nach einem erfolgreichen HTTP Request + HTTP Response wird die Verbindung geschlossen und beim nächsten HTTP Request wieder aufgebaut.

    Sind wir da ganz sicher?



  • Wenn der Socket geschlossen wird, musst Du dich neu verbinden, um Daten zu senden.
    Wenn nicht, kannst du beliebig Daten versenden und empfangen.



  • Das hängt von der Art der Kommunikation statt. HTTP zum Beispiel ist verbindungslos, d.h. nach einem erfolgreichen HTTP Request + HTTP Response wird die Verbindung geschlossen und beim nächsten HTTP Request wieder aufgebaut.

    FAIL



  • Failgeber schrieb:

    Das hängt von der Art der Kommunikation statt. HTTP zum Beispiel ist verbindungslos, d.h. nach einem erfolgreichen HTTP Request + HTTP Response wird die Verbindung geschlossen und beim nächsten HTTP Request wieder aufgebaut.

    FAIL

    [

    In HTTP 1.0 and before, TCP connections are closed after each request and response, so each resource to be retrieved requires its own connection.

    ](http://www.jmarshall.com/easy/http/#http1.1c3)



  • wichtig ist dabei das 1.0



  • Ok, danke für die Aufklärung, dann bin ich da nicht so ganz auf dem neuesten Stand 😃

    Mein Beispiel ist aber auch so noch geeignet, da es einmal so war und bei HTTP 1.1 auch noch möglich ist (auch wenn die "persistent connections" Standard sind).



  • Trotzdem ist es TCP und nicht UDP. Also ist verbindungslos falsch. Und natuerlich kann diese Verbindung auch von der anderen Seite geschlossen werden. Das ist nicht HTTP spezifisch. Wenn die Gegenstelle die Verbindung nicht schliesst, dann kannst du den Socket weiterverwenden. Wenn sie die Verbindung schliesst, dann eben nicht. Das haengt ganz von deinem Kommunikationspartner ab.



  • Offenbar macht mein Gegenüber dann mehr als nur einen shutdown(), denn damit würde er ja den Socket nicht schließen, sondern nur das Ende der gesendeten Daten signalisieren.

    In meinem Fall macht es auch Sinn dass der Server die Verbindung beendet, weil es sich um eine konkrete Anfrage mit einer dazugehörigen Antwort handelt. Aber müsste mir errno nach einem fehlerhaften Senden dann nicht einen anderen Fehler zeigen, z.B. ENOTCONN? Irgendwie müsste ich doch feststellen können, dass der Socket dicht ist...um dann fallabhängig ein neues connect() veranlassen.

    Offenbar ist es ja auch keine Pflicht, nach einem Senden/Empfangen ein shutdown() zu veranlassen.

    Ich schätze mal dass ich beim Senden an einen Server immer ein shutdown() machen kann, ohne dass der Server damit Probleme kriegt (eines meiner Geräte wartet bspw. auf ein Line Feed und betrachtet die Anfrage damit als abgeschlossen). Testen kann ich das erst am Montag.

    Aber kann es sein, dass ich fürs Lesen vom Socket für jedes Gerät eine eigene readFromSocket()-Funktion schreiben muss?

    Konkret in meinem Fall

    • eine Funktion, die auf ein Line Feed wartet und dann mit dem Lesen aufhört
    • eine Funktion, die Line Feeds einfach mit einliest und auf ein shutdown() wartet.

    Noch eine Frage: Mit einem shutdown() beim Senden wird das Ende der gesendeten Daten signalisiert. Aber was bringt ein shutdown() nach dem Einlesen von Daten? Signalisiert damit auch etwas, oder werden Daten, die noch anliegen, "geflusht", ...?



  • knivil schrieb:

    Trotzdem ist es TCP und nicht UDP. Also ist verbindungslos falsch. Und natuerlich kann diese Verbindung auch von der anderen Seite geschlossen werden. Das ist nicht HTTP spezifisch. Wenn die Gegenstelle die Verbindung nicht schliesst, dann kannst du den Socket weiterverwenden. Wenn sie die Verbindung schliesst, dann eben nicht. Das haengt ganz von deinem Kommunikationspartner ab.

    Auch wenn TCP verbindungsorientiert ist kann eine darüberliegende Schicht verbindungslos sein, siehe HTTP 1.0 dort wird nach dem Austausch die Verbindung geschlossen.
    Oder nimm dir ein UDP basiertes Protokoll wie TFTP als Beispiel dort wird eine bestehende Verbindung oberhalb von UDP eingerichtet.



  • Ich beschreibe mal meine Lösung, von der ich nicht einschätzen kann wie elegant sie ist, die aber so funktioniert:

    Ich habe
    - eine Socket-Klasse
    - für jedes "Gerät" (remote socket) eine Geräteklasse die gerätespezifische Kommunikation umsetzt. Die Klassen besitzen ein Member der Socket-Klasse.
    - Mehrere Klassen, welche Daten verwalten und Daten übers Netzwerk senden/empfangen kann. Die Klassen haben je ein Member von einer Geräteklasse

    Meine "Geräteklassen" rufen von der Socket-Klasse die Methoden

    // sock_option
    enum { SEND_DATA_ONLY = 0, SEND_SHUTDOWN };
    enum { STOP_AT_LINEFEED = 0, STOP_AT_SHUTDOWN };
    
    int InetSocket::sendData(const string & strData, int sock_option)
    int InetSocket::recvData(string & strData, int sock_option)
    

    auf. Die sendData()-Methode hat nur die Fallunterscheidung ob nach den Daten noch ein shutdown() gesendet wird oder nicht. Die recvData() - Methode ruft fallabhängig eine der Methoden

    int InetSocket::recvDataLF(string & strDataP); // liest bis zum line feed
    int InetSocket::recvDataSD(string & strDataP); // liest bis EOF (shutdown)
    

    auf, da sich diese doch stark unterscheiden.

    Bei der Kommunikation mit dem Server, der die Verbindung immer wieder beendet, muss ich vorher den Socket schließen und ein connect() aufrufen.

    int InetSocket::reconnect(){
        if(-1 != m_fdSocket) {
            close(m_fdSocket);
            m_fdSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        }
        return connectTo();
    }
    

    Die Geräte-Klasse, die sich mit dem Server unterhält, welcher shutdown() sendet, stellt dann keine sendData() und recvData()-Methoden, sondern nur eine processRequest()-Methode zur Verfügung. In der processRequest()-Methode gibt es dann entsprechend die Aufrufe von sendData() und recvData() aus der Socket-Klasse.

    Jetzt hoff ich dass meine Beschreibung verständlich ist...


Log in to reply