Winsock: Was passiert, wenn man zu viel sendet?



  • Hi schrieb:

    Aber wie soll man darauf reagieren...
    zB. man will 1 GB versenden; Sendet mit 3 MB/sec. Empfänger hat aber 56k Einwahl. Und man sendet, und sendet, und sendet... und wo stapeln sich die inzwischen zB. fast 500 versendeten MB (Von denen der Empfänger zB. erst 1 MB erhalten hat)? Und wenn es mehr werden? Was passiert und was soll man (die Anwendung) dann tun?

    Versuchen wir es mal darzustellen:

    Sender hat 3 mpbs, Empfänger hat 56 kbps. Sowohl Sender- als auch Empfängerpuffer seien 1 MB groß.

    Ausgansgsituation: Beide Puffer leer, du willst 1 GB senden
    Beim Senden haut das BS von dem GB das erste MB in den Sendepuffer und stößt bei der unterliegenden Schicht das Senden an. Die Pakete werden zum Empfänger geschickt, dank TCP läuft der Empfangspuffer auch nicht über. Dein Programm läuft ja wegen asynch weiter und sobald das eine MB verschickt ist, triggered dein IOCP und du kannst wieder was in den Sendepuffer schieben. Also das nächste Stück von dem GB.

    Das heißt, du musst die Daten vorhalten. In deinem Beispiel mit den versendeten 500 MB von denen der Empfänger erst ein MB erhalten hat, gäbe es gar keine versendeten 500 MB. Einfach, weil das BS nur immer ein bisschen von dem GB in den Sendepuffer übernimmt, nicht gleich 500 MB. Es gäbe also nur 1 versendetes MB, welches im Sendepuffer darauf wartet, verschickt zu werden. Die restlichen 1023 MB hältst du in deinen Datenstrukturen vor.



  • Hmm, ich hatte mir das anders vorgestellt:
    Ich dachte, man würde gleich alles senden, weil es ja nicht blockiert. Also einfach so oft WSASend() aufrufen, bis alle Daten weg sind, sei es 1 GB.

    Da kommt eine weitere Frage auf: Wie viel darf ich einem WSASend()-Aufruf maximal auf einmal übergeben? Könnte ich auch 1 GB mit einem einzigen Aufruf senden? Soll man das machen? Wie geht man bei WSASend()+overlapped überhaupt vor?



  • Hi schrieb:

    Wie viel darf ich einem WSASend()-Aufruf maximal auf einmal übergeben? Könnte ich auch 1 GB mit einem einzigen Aufruf senden? Soll man das machen? Wie geht man bei WSASend()+overlapped überhaupt vor?

    Ich würde es etwa so machen:

    // Datenquelle, hier einfach mal ein einfacher Puffer
    std::vector<char> Buffer;
    
    // Rufen wir von unserem IOCP-System auf, wenn was gesendet wurde
    void trySendBuffer( int BytesSent, bool Error ) {
        if ( Error )
            ; // TODO
        // Sicherheitshalber...
        assert( BytesSent>0 && BytesSent<=Buffer.size() );
        // Was gesendet wurde aus dem Puffer löschen
        Buffer.erase( Buffer.begin(), Buffer.begin()+BytesSent );
        // Nächstes Stück senden
        IOCP.send( &Buffer[0], Buffer.size(), trySendBuffer );
    }
    
    // Um das ganze anzustoßen:
    IOCP.send( &Buffer[0], Buffer.size(), trySendBuffer );
    


  • Einfach anstoßen... Habs kapiert. Ist ja voll easy 🙄



  • Das scheint doch ziemlich sinnlos, wenn man die volle Puffergröße angibt.

    BytesSent wird dann eh gleich Buffer.size() sein, wenn kein Fehler aufgetreten ist.



  • hmmmmmm schrieb:

    BytesSent wird dann eh gleich Buffer.size() sein, wenn kein Fehler aufgetreten ist.

    Meistens und gerade für kleine Größen: Ja. Für auch nur halbwegs ernsthafte Anwendungen darf man aber die unwahrhscheinlichen Fälle nicht außer Acht lassen.



  • Das wäre mir neu, wenn das passieren könnte. Die ganze trySendBuffer-Funktion ergibt für mich eigentlich keinen Sinn. Der IOCP wird AFAIK erst die Completion-Notification bekommen, wenn alle Daten (und nicht nur ein paar Daten) korrekt am Ziel angekommen sind (selbiges gilt auch für WSARecv()).



  • Jodocus schrieb:

    Das wäre mir neu, wenn das passieren könnte. Die ganze trySendBuffer-Funktion ergibt für mich eigentlich keinen Sinn. Der IOCP wird AFAIK erst die Completion-Notification bekommen, wenn alle Daten (und nicht nur ein paar Daten) korrekt am Ziel angekommen sind (selbiges gilt auch für WSARecv()).

    Tatsache, das scheint bei WSASend so zu sein, wusste ich nicht! Bei normalem send kann auch nur ein Teil gesendet werden.

    MSDN zu WSASend schrieb:

    A completion indication will occur, invoking the completion of a routine or setting of an event object, when the buffer(s) have been consumed by the transport.

    MSDN zu send schrieb:

    On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.



  • MisterX schrieb:

    Belli schrieb:

    Blabla13443 schrieb:

    Ein send auf einem blockierenden Socket sendet alles oder nichts.

    Genau das ist falsch!

    Ich zitier mal aus der MSDN:

    If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode. On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.

    Du willst was über blockierende Sockets aussagen und bringst ein Zitat auch noch Fett geschrieben über nicht blockierende Sockets.

    Wenn da "only on nonblocking stream oriented sockets..." stehen würde, hätte es eine Aussagekraft... aber so sagt es rein nichts über blockierende Sockets aus.

    Für diejenigen, die nicht in der Lage sind, zu verstehen, was die MSDN damit sagen will, besteht ja noch die Möglichkeit, das auszutesten ...
    Wenn es Dir gelingt, unter Windows mit einem blockierenden Socket einen send auszuführen, der nur eine Teilmenge verschickt, melde Dich noch einmal mit einem entsprechenden nachvollziehbaren Beispiel.



  • fdfdg schrieb:

    MSDN zu WSASend schrieb:

    A completion indication will occur, invoking the completion of a routine or setting of an event object, when the buffer(s) have been consumed by the transport.

    MSDN zu send schrieb:

    On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.

    Ähh, also hat das completion packet nun doch nix mit dem ACK vom Empfänger zu tun? Bekommt man es also, sobald die Daten transportiert wurden, und nicht erst, wenn der Empfänger empfangen hat? Dann würden die Pakete also auch verworfen (Irgendwo bei den Providern wohl) werden, könnte der Empfänger nicht schnell genug empfangen?!
    Dachte, das wäre nur bei UDP so.



  • Belli schrieb:

    MisterX schrieb:

    Belli schrieb:

    Blabla13443 schrieb:

    Ein send auf einem blockierenden Socket sendet alles oder nichts.

    Genau das ist falsch!

    Ich zitier mal aus der MSDN:

    If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode. On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.

    Du willst was über blockierende Sockets aussagen und bringst ein Zitat auch noch Fett geschrieben über nicht blockierende Sockets.

    Wenn da "only on nonblocking stream oriented sockets..." stehen würde, hätte es eine Aussagekraft... aber so sagt es rein nichts über blockierende Sockets aus.

    Für diejenigen, die nicht in der Lage sind, zu verstehen, was die MSDN damit sagen will, besteht ja noch die Möglichkeit, das auszutesten ...
    Wenn es Dir gelingt, unter Windows mit einem blockierenden Socket einen send auszuführen, der nur eine Teilmenge verschickt, melde Dich noch einmal mit einem entsprechenden nachvollziehbaren Beispiel.

    👍



  • Ich finde auch dass es nicht klar formuliert ist.
    Gemeint ist aber auf jeden Fall, dass auf blocking Stream Sockets immer alles versendet wird (so lange kein Fehler auftritt).



  • hustbaer schrieb:

    Ich finde auch dass es nicht klar formuliert ist.
    Gemeint ist aber auf jeden Fall, dass auf blocking Stream Sockets immer alles versendet wird (so lange kein Fehler auftritt).

    Dann guckst du mal!



  • hustbaer schrieb:

    Ich finde auch dass es nicht klar formuliert ist.

    Das ist es auch nicht. Noch unklarer formuliert ist meiner Meinung aber, dass bei einem recv möglicherweise nicht alles in einem Rutsch gelesen wird.



  • Kennt ihr denn nur MSDN?

    http://www.sockets.com/winsock2.htm das hier dürfte 50% der Fragen beantworten. MS-garbage ausgeschlossen.



  • Oh wow ... das ändert natürlich alles ...



  • Mit "completion packet" meinst du eine Benachrichtigung über einen IO Completion Port, oder? Falls nicht, vergiss den Rest der jetzt folgt.

    Hi schrieb:

    Ähh, also hat das completion packet nun doch nix mit dem ACK vom Empfänger zu tun?

    Richtig, hat es nicht. Macht auch Sinn. Sonst müsste man u.U. recht lange darauf warten.
    Du bekommst die Completion-Notification (über welchen Mechanismus auch immer) genau zu dem Zeitpunkt zu dem ein synchrones, blockierendes send() zurückkommen würde.
    Im Prinzip bedeutet dass nur, dass du deinen Sende-Puffer jetzt wieder freigeben/überschreiben darfst.

    Die Completion-Notification bedeutet nichtmal, dass die Daten schon versendet wurden. Die können zu dem Zeitpunkt noch in Puffern der Netzwerkkarte stehen. Bzw. sogar in Puffern des Betriebssystems, so dass noch nichtmal die Netzwerkkarte was davon mitbekommen hat.

    Anders wäre z.B. Send-Coalescing nicht möglich (siehe z.B. Nagle's Algorithm).

    Bekommt man es also, sobald die Daten transportiert wurden, und nicht erst, wenn der Empfänger empfangen hat?

    Nö, normalerweise sogar noch früher, siehe oben.

    Dann würden die Pakete also auch verworfen (Irgendwo bei den Providern wohl) werden, könnte der Empfänger nicht schnell genug empfangen?!

    Jain.

    Zu dem Zeitpunkt wo die Completion-Notification bekommst, ist nicht garantiert, dass der Empfänger die Daten bereits empfangen hat, oder jemals empfangen wird.
    Es ist allerdings garantiert, dass, WENN die Verbindung nicht abbricht, er die Daten irgendwann bekommen wird. Und zwar alle Daten, und auch genau in der Reihenfolge in der sie gesendet wurden.

    Dazu puffert der TCP/IP Stack vom Sender die Daten im RAM. Wenn die zu sendenden Daten auf dem Weg zum Epfänger ganz oder teilweise verloren gehen, dann sendet der TCP/IP Stack sie einfach nochmal. Und nochmal und nochmal, so lange bis alles angekommen ist. Das ganze passiert völlig transparent, d.h. du brauchst dich darum nicht zu kümmern. z.B. werden die Daten aus dem Puffer des Programms in einen Puffer des TCP/IP Stacks kopiert, bevor die Completion-Notification geschickt wird. Dadurch darfst das Programm "seinen" Puffer sofort nach der Completion-Notification überschreiben, ohne dass das "Neu-Versenden" von Daten dadurch beeinträchtigt wird.

    Und nochmal zu "könnte der Empfänger nicht schnell genug empfangen": der TCP/IP Stack des Senders puffert nur eine begrenzte Menge an Daten. Die Daten werden aus den internen Puffern des TCP/IP Stacks erst dann entfernt, wenn der Empfänger sie bestätigt hat (ACK). Daraus folgt, dass wenn der Sender schneller senden will als der Empfänger empfangen kann, send() irgendwann anfängt zu blockieren (bzw. die Completion-Notification erst später zu schicken). Anders gesagt: das System stellt sicher, dass du einfach nicht schneller senden kannst, als der Empfänger empfangen kann. Wenn du es versuchst, bremst es dich entsprechend aus.



  • Danke SEHR! 👍


Anmelden zum Antworten