Kleine Verständnisfrage zu Recv()



  • Hallo liebe Forengemeinde,

    ich habe nur eine kleine Ja/Nein Frage. Wenn ich per TCP-Socket über den Befehl Recv() angebe, dass er z.B. 1024 Zeichen in den Buffer schreiben soll, also:
    recv(socket, buffer, 1024, 0);
    wird dann von der Funktion tatsächlich so lange gewartet, bis genau 1024 Zeichen empfangen wurden?
    Ich stolpere darüber, was passiert, wenn ich z.B. nur 512 Zeichen sende. Werden dann die anderen 512 Zeichen im Buffer weggelassen oder schreibt die Funktion sie in den Buffer und wartet auf den Rest oder wartet sie gleich auf den Rest und schreibt dann?

    Freue mich auf eure Antworten=)



  • BootLag-BootLag- schrieb:

    Hallo liebe Forengemeinde,

    ich habe nur eine kleine Ja/Nein Frage. Wenn ich per TCP-Socket über den Befehl Recv() angebe, dass er z.B. 1024 Zeichen in den Buffer schreiben soll, also:
    recv(socket, buffer, 1024, 0);
    wird dann von der Funktion tatsächlich so lange gewartet, bis genau 1024 Zeichen empfangen wurden?

    Nein. recv() empfängt bei blocking Sockets 1-N Bytes bzw. bei non-blocking Sockets 0-N Bytes. (bzw. auch bei blocking Sockets 0 Bytes im Fehlerfall)

    Ich stolpere darüber, was passiert, wenn ich z.B. nur 512 Zeichen sende. Werden dann die anderen 512 Zeichen im Buffer weggelassen oder schreibt die Funktion sie in den Buffer und wartet auf den Rest oder wartet sie gleich auf den Rest und schreibt dann?

    recv() empfängt wie gesagt 1-N Bytes, und das völlig unabhängig davon wieviel gesendet wurde.
    Eine TCP/IP Verbindung ist ein Byte-Stream, und kennt keine "Paketgrenzen".
    D.h. auch wenn du 512 Byte auf einmal mit send() abschickst, kann es passieren dass recv() nur 1 Byte oder 2 oder 10 oder alles >= 1 und <= 512 empfängt.

    Bzw. wenn du 2x send() mit je 512 Byte aufrufst, kann ein einziges recv() auf der Gegenseite 1-1024 Byte auf einmal empfangen.

    Kannst du dir vorstellen wie wenn du in ein File schreibst, und ein anderes Programm später diese Daten ausliest. Das kann auch nicht sehen welche Stücke der Datei du auf einmal mit einem fwrite() geschrieben hast, es sieht nur eine lange Folge von Bytes. Die Info wie die da reingekommen sind geht verloren.

    Du musst also selbst die nötigen Daten in deinen Byte-Stream packen, die du auf der Gegenseite brauchst, um bestimmen zu können was "zusammen gehört".
    Und natürlich recv() in einer Schleife so lange aufrufen, bis du alle Daten zusammen hast, die du brauchst.



  • Das heißt, wenn ich das richtig verstanden habe, die Angabe 1024 gibt nicht an, dass das die Funktion 1024 Bytes empfangen MUSS?
    Rufe ich die besagte Funktion mit 1024 genau einmal auf(ist blockierend) und es werden von der Gegenseite nur 512 Bytes gesendet, dann werden auch nur 512 Bytes empfangen und die Funktion ist fertig und das Programm läuft weiter?



  • Jop. 1024 ist nur die Größenangabe deines Puffers.



  • BootLag-BootLag- schrieb:

    Das heißt, wenn ich das richtig verstanden habe, die Angabe 1024 gibt nicht an, dass das die Funktion 1024 Bytes empfangen MUSS?

    Genau. Die 1024 sind die Obergrenze (der Puffer ist ja nicht unendlich gross), die Untergrenze ist wie gesagt 1 bzw. 0.

    Rufe ich die besagte Funktion mit 1024 genau einmal auf(ist blockierend) und es werden von der Gegenseite nur 512 Bytes gesendet, dann werden auch nur 512 Bytes empfangen und die Funktion ist fertig und das Programm läuft weiter?

    Nö. Bzw. ja, kommt drauf an wie du es meinst.

    Es werden dann nicht nötigerweise 512 Bytes empfangen, sondern zwischen 1 und 512 Byte - aber natürlich maximal 512 Byte, da ja nicht mehr weggeschickt wurden.
    Wie viel es genau sind, ist mehr oder weniger Zufall.

    Wenn du eine Ethernet Verbindung hast, wirst du kaum jemals was anderes als 512 Byte sehen, da sich 512 Byte noch in einem Paket ausgehen. Allerdings sollte man sich darauf niemals verlassen, denn es ist nicht garantiert dass die 512 Byte auf einmal empfangen werden. Wenn davor bereits andere Daten übertragen wurden wird es auch real vorkommen dass NICHT alles auf einmal ankommt, bzw. wenn die Verbindung aus irgend einem Grund eine sehr kleine MTU hat.

    Wenn du den Effekt beobachten möchtest, dann nimm statt 512 Byte einfach mal 64KB, und schick die Daten über ein echtes Netzwerk. D.h. NICHT nur VON deinem PC ZU deinem PC, sondern vielleicht von deinem PC an die Public-IP deines Routers, so dass der die Daten wieder zu deinem PC zurückschickt --- passendes Port-Mapping vorausgesetzt. Und je langsamer das Netzwerk desto besser, da der Effekt dann umso eher beobachtbar wird.

    Was recv macht kann man sich (stark vereinfacht) ca. so vorstellen:

    int recv(SOCKET s, void* buffer, int buffer_size)
    {
        int gepufferte_daten = daten_in_empfangspuffer(s);
    
        if (gepufferte_daten == 0)
        {
            if (!ist_blocking_socket(s))
                return 0; // bei non-blocking sockets wird nicht gewartet
    
            warte_auf_daten(s); // wartet bis min 1 Byte empfangen wurde, ggf. auch mehr
    
            gepufferte_daten = daten_in_empfangspuffer(s);
            assert(gepufferte_daten > 0); // jetzt muss was da sein
       }
    
       int transfer_size;
       if (gepufferte_daten > buffer_size)
           transfer_size = buffer_size;
       else
           transfer_size = gepufferte_daten;
    
       // "transfer_size" Bytes aus dem Empfangs-Puffer in den User-Puffer kopieren
       // (und gleichzeitig aus dem Empfangs-Puffer entfernen)
       kopiere_daten_aus_empfangspuffer(s, buffer, transfer_size);
       return transfer_size; // dem Aufrufer zurückgeben wie viel Bytes wirklich transferiert wurden
    }
    

Anmelden zum Antworten