Winsock TCP optimierung bzw machung^^



  • nep schrieb:

    hustbaer schrieb:

    nep schrieb:

    Ähm noch was anderes: Es ist doch glaube ich nur so, dass die Bytes, welche durch einen Aufruf mit send verschickt werden in korrekter Reihenfolge ankommen. Es ist aber nicht garantiert, dass die Bytes welche durch mehrere send-Aufrufe verschickt werden, auch genau in der Reihenfolge der Aufrufe ankommen. Oder irre ich mich da jetzt? (Alles natürlich auf TCP Sockets bezogen)

    Du hast TCP geschrieben, und TCP impliziert nunmal nen stream socket (SOCK_STREAM). Bei stream sockets ist es eben so wie ich geschrieben habe, also dass send() nur soviel verschickt wie es gerade lustig ist, dafür ist allerdings 101% garantiert dass die Daten in der richtigen Reihenfolge am anderen Ende rauskommen.

    Bei nem datagram socket (SOCK_DGRAM, das wäre dann z.B. UDP statt TCP) ist es eben andersrum, d.h. entweder verschickt send() alles was man übergeben hat oder garnix, dafür ist NICHT garantiert dass irgendwas in der Reihenfolge ankommt wie es gesendet wurde.

    Du musst die Doku schon aufmerksam lesen, und nachlesen wenn da etwas steht wo dir nicht klar ist was es bedeutet bzw. ob es auf dein Programm zutrifft -- eben sowas wie "For message-oriented sockets" (was eben einen "datagram socket" beschreibt, und damit niemals auf TCP zutreffen kann).

    Äh und? Das was ich meinte hat doch gar nix damit zu tun, wie bzw. wann send() nun seine Daten verschickt. Das war was ganz anderes. Ausserdem hab ich hier doch gar kein Problem mit "meinem" Programm. Wollte nur was anmerken, wobei ich mir eben unsicher war ob das überhaupt stimmt. Wenn ich das so wirklich bräuchte würde ich schon die Doku lesen, keine Sorge... Übrigens ist mit der Unterschied zwischen UDP und TCP sehr wohl bewusst...

    *räusper* Tschulligung, hab nicht geguckt wie der OP heisst, und aufgrund der Formulierung vermutet es wäre dein Thread 🙂
    Vergiss also meine Antwort, richtige Antwort folgt nun:

    nep schrieb:

    Ähm noch was anderes: Es ist doch glaube ich nur so, dass die Bytes, welche durch einen Aufruf mit send verschickt werden in korrekter Reihenfolge ankommen. Es ist aber nicht garantiert, dass die Bytes welche durch mehrere send-Aufrufe verschickt werden, auch genau in der Reihenfolge der Aufrufe ankommen. Oder irre ich mich da jetzt? (Alles natürlich auf TCP Sockets bezogen)

    Du irrst dich. Das ist garantiert (bei stream Sockets, also TCP, wie du eben schreibst). Weiss nicht was daran so kompliziert ist -- heisst ja "stream" und nicht "partially ordered stream" oder so 😉

    Was du schreibst trifft im Übrigen auf datagram Sockets (UDP) zu, da werden die einzelnen Bytes eines Paketes auch nicht durcheinandergewürfelt -- wäre ja komplett unbrauchbar. Dort gilt dann auch dass die Reihenfolge der einzelnen Pakete nicht garantiert wird, bzw. nichtmal dass die überhaupt ankommen...



  • (naja, die Pakete an sich könnten rein-theoretisch schon ungeordnet durch die Leitungen schwirren, aber wir befinden uns hier auf ganz anderer Ebene und kriegen davon eh nix mit - Das wird voher schon für uns in richtiger Reihenfolge zusammengesetzt ;D)

    Bei recv() kommen die Bytes auf jedenfall in der Reihenfolge wie sie gesendet wurden an 😉



  • hustbaer schrieb:

    nep schrieb:

    Ähm noch was anderes: Es ist doch glaube ich nur so, dass die Bytes, welche durch einen Aufruf mit send verschickt werden in korrekter Reihenfolge ankommen. Es ist aber nicht garantiert, dass die Bytes welche durch mehrere send-Aufrufe verschickt werden, auch genau in der Reihenfolge der Aufrufe ankommen. Oder irre ich mich da jetzt? (Alles natürlich auf TCP Sockets bezogen)

    Du irrst dich. Das ist garantiert (bei stream Sockets, also TCP, wie du eben schreibst). Weiss nicht was daran so kompliziert ist -- heisst ja "stream" und nicht "partially ordered stream" oder so 😉

    Was du schreibst trifft im Übrigen auf datagram Sockets (UDP) zu, da werden die einzelnen Bytes eines Paketes auch nicht durcheinandergewürfelt -- wäre ja komplett unbrauchbar. Dort gilt dann auch dass die Reihenfolge der einzelnen Pakete nicht garantiert wird, bzw. nichtmal dass die überhaupt ankommen...

    Ja das hat Ceos weiter oben schon geschrieben 😉 Wie gesagt sollte nur ein Einwurf sein, den ich aber extra nochmal hinterfragt habe, weil ich mir da selbst unsicher war wie das genau aussieht (und zu faul zum Doku lesen war 😉 ).
    Und nochmal, ich kenn den Unterschied zwischen UDP und TCP... das was du schreibst stimmt ja auch, aber das hat so eigentlich gar nix mit meinem "Einwurf" zu tun. Wenn du UDP Sockets hast, kannst du ja eh nur so viele Daten an send() geben, wie die maximale UDP Packet-Größe beträgt (müssten so ~64 Kb sein). Und das die Bytes in einem Packet nicht durcheinandergewürfelt werden ist ja wohl logisch...



  • Fachmann schrieb:

    aber wie wird sowas dann mit TCP gemacht?

    Du müsstest quasi deine send()-Routine austauschen gegen eine send_really() oder so. Ähnlich hat das ja derjenige in dem oben genannten Thread auch gelöst.
    Quasi sicherstellen das send() bzw. recv() wirklich alles sendet.

    Sagen wir du willst 10 Bytes verschicken und rufst send() auf. send() meldet es hätte 2 Bytes verschickt. Also weisste das noch die restlichen 8 Byte hinterher müssen. Also send() mit den restlichen 8 Bytes nochmal aufrufen.
    Diesmal sagen wir mal hätte send() 7 Bytes verschickt. Fehlt also noch einer. Den kriegste dann mit dem letzten send() weg.
    Quasi ne kleine Schleife und nen byte-Pointer einfach weitersetzen bis alles weg ist.

    Ähnlich ist es bei recv() 😉

    Evtl. interessant ist da auch:
    http://msdn2.microsoft.com/en-us/library/ms740565.aspx



  • Sagen wir du willst 10 Bytes verschicken und rufst send() auf. send() meldet es hätte 2 Bytes verschickt. Also weisste das noch die restlichen 8 Byte hinterher müssen. Also send() mit den restlichen 8 Bytes nochmal aufrufen.
    Diesmal sagen wir mal hätte send() 7 Bytes verschickt. Fehlt also noch einer. Den kriegste dann mit dem letzten send() weg.
    Quasi ne kleine Schleife und nen byte-Pointer einfach weitersetzen bis alles weg ist.

    Das ist aber auch am thema vorbei!!!
    Send sendet (eigentlich) IMMER ALLES was übergeben wurde.
    Nur muss das erfolgreiche senden des Array nicht heissen das auch alle bytes in einem schwung gesendet wurden. Der Socket arbeitet in einem ganz anderen kontext als dein programm, du schiebst praktisch nur die bytes in die queue.
    Wenn du grad 40 von 100 bytes in den puffer geschreiben hast, kann es passieren das der socket anfängt zu schreiben.
    die send methode wird dadurch nicht unterbrochen, sie blockiert nur (im nanosekundenbereich denk ich).
    nun sind 40 bytes gesendet, die sendfunktion setzt fort und kopiert die restlichen 60 byte.
    Die Send funktion meldet 100 geschreibene bytes und dennoch kommen am client 2 "pakete" 1 40bytes das andere 60bytes

    falls dem so nicht ist bitte ich um verzeihung, ich habde das von unserem Java-Dozenten, der mir ehrlich gesagt ein wenig zu viel wissen will



  • send() sendet unter Windows in den allermeisten Fällen alles.
    (Intern wird das ganze in einen Puffer geschrieben, in Pakete verpackt und gesendet)
    ...aber der Rückgabewert ist nicht umsonst da - "allermeiste Fälle" sind nicht "alle Fälle" 😉

    ...statt "ähnlich ist es bei recv()" hätte ich noch besser "vor allem ist es bei recv()" geschrieben. Bei recv() taucht quasi das umgekehrte Problem deutlich häufiger auf.
    Wer mit einem recv() meint den gesamten Buffer sofort voll zu haben, wird spätestens bei ner größeren Anzahl von Bytes (z.B. wenn die maximale Paketlänge überschritten wird) und ner relativ lahmen Leitung schnell merken dass recv() gerne nur das zurückliefert was es bereits an Paketen bekommen und in richtiger Reihenfolge wieder in Bytes verpacken konnte...

    (btw: Ich hab hier das Gefühl das hier schonmal "Paket" mit den Bytes aus send() gleichgesetzt wird - Was für Pakete daraus entstehen kann man nicht direkt ableiten)



  • Ceos schrieb:

    falls dem so nicht ist bitte ich um verzeihung, ich habde das von unserem Java-Dozenten, der mir ehrlich gesagt ein wenig zu viel wissen will

    So wie ich die Doku da lese (http://java.sun.com/j2se/1.4.2/docs/api/java/net/Socket.html#getOutputStream()) scheint man da ja einfach mit Streams zu arbeiten und hat das Glück mit ner komplizierten send()-Methode gar nicht in Kontakt treten zu müssen 😉

    Hmm, so langsam glaube ich wir meinen dasselbe und reden bloß aneinander vorbei...

    wikipedia schrieb:

    Sockets bilden eine standardisierte Schnittstelle (API) zwischen der Netzwerkprotokollimplementierung des Betriebssystems und der eigentlichen Applikationssoftware. [..] Stream Sockets verwenden meist TCP, was aufgrund der Eigenschaften von TCP zu einer hohen Verlässlichkeit führt. Andere Transportprotokolle als TCP sind denkbar, aber wenig verbreitet. Datagram Sockets arbeiten üblicherweise über UDP, also verbindungslos. Dies impliziert schnellen Datenaustausch, jedoch geringe Verlässlichkeit. Auch hier sind natürlich alternative Protokolle möglich.

    Ich denke wir lehnen uns hier zu sehr an TCP/UDP an und reden daher aneinander vorbei. Dabei liegen Sockets ja im Prinzip in einer noch abstrakteren Ebene als TCP/UDP...



  • das von 1000 byte nur ein byte gesendet wird steht bestimmt nirgends (soll ich gucken)

    wenn dann steht da:
    es kann sein das ein paket (gröse 1000) gesendet wird und 2 pakete (a 500 byte) ankommen ... das hat für meine begriffe die relvanz das man beim empfangen der pakete nach schauen muss [wenn man es nicht wie ein stream behandelt] ...

    wie gesagt TCP ist die gesicherte variante von udp (jedes paket was nicht korrekt empfangen würde wird noch mal gesendet:

    Sender ............. empfänger
    --------------DATEN-------->
    <---------CRC32-------------
    ------------OK------------->

    für nur EIN paket !!!!!!!!!!!! zum glück aber nicht von endpunkt zu endpunkt sondern von jeder übertragungsstrecke[ die auf TCP basiert, Bsp Lan mit mehren Subnets die per router komunizieren]

    sollte da ein NOT OK (falsche CRC32) gesendet, wird das paket noch mal gesendet (bis zu 3 mal, und dann is der socket als "tod" gekennzeichnet (zumindest bei Winsock unter VB6 ... wie und ob man das in C++ feststellen kann kA wird schon nen fehler wo kommen notfalls mal das lan kabel ziehen 😉 dann weiss ma was passiert und kann dafür ne fehlerbehandlung schreiben)

    @fachman ... ich bin zwar in c++ noch am testen [so wie du] aber das wichtigste über TCP/UDP hab ich in meiner lehre gelernt ... also denn darfste mir abnehmen 😉
    die methode war für mich alleine ´nur deswegen zum scheitern verurteilt weil da ja null fehlerbehandlung vorkommt (und netzwerk ist ja nun mal so ne sache)

    PS TCP/UDP sind standardiesierte netzwerk protokolle jede firma nutzt das - weils keine andere oder gar bessere methode gibt UDP = schnell(unzuverlassig) TCP = zuverlässig(langsam), rattet mal warum man mit http langsamer downloaded als mit ftp 😉 [bei ftp ist das aber über andere "wege" gesichert ;)]



  • LinkeT schrieb:

    das von 1000 byte nur ein byte gesendet wird steht bestimmt nirgends (soll ich gucken)

    Außer in der MSDN:

    MSDN schrieb:

    If no error occurs, send returns the total number of bytes sent, which can be less than the number requested to be sent in the len parameter.

    http://msdn2.microsoft.com/en-us/library/ms740149.aspx
    (Natürlich muss man beachten das sich die Socket-Komponenten in VisualBasic und im .NET-Framework diese Einschränkung vermutlich selber umgehen...)



  • Ok. Ganz einfach: es ist (wie auch schon mehrfach aus der MSDN zitiert wurde) eben NICHT garantiert dass send() immer alles sendet. Fertig, ende der Diskussion. Ob es meistens so ist, oder sogar immer bis auf die Tage wo der Osterhase Schneewittchen im Wald getroffen hat und gleichzeitig Omis Kuckuksuhr kaputtging, ist vollkommen egal. Nicht garantiert heisst "wer sich trotzdem drauf verlässt ist des selberen schuld". Blubb.
    🙂



  • 😃

    bin mal gespannt wann ich drauf stosse 😉

    wenns so sein sollte ok aberwie gesagt fehlerbehandlung mit reinbringen und ok 😛



  • es ist (wie auch schon mehrfach aus der MSDN zitiert wurde) eben NICHT garantiert dass send() immer alles sendet.

    ich glaub das ist so selten der fall, dass TCP sogar von banküberweissungen von millionen dollars machen und auch vista ihre gesamte ergatterte daten ans FBI/CIS usw schicken 😉
    das risiko geh ich ein weil es ist bestimmt selten dass was nicht ankommt. warscheinlich so selten wie dass daten vom prozessor nicht zur grfikkarte kommen

    also fackt ist wenn ich in der schleife alle daten über send schicke werden in nen internen puffer geschrieben und früher oder später abgeschcikt ( verloren geht nix). nachdem alles im puffer is (un teile schon geschickt wurden) close ich den socket. dann wird close_socket warscheinlich auch hinten an den send-stack geschickt sodass auch keine daten verloren gehn. das problem steckt also warschinlich bei recv(). aber wie? er empfängt solang bis close_socket ankommt (oder so) un nach close_socket kommen auch keine daten mehr? wo liegt dann das prob? oder bricht socketclose einfach die verbindung ab un löscht den puffer??

    #edit
    wenn ich meine beiträge mal lese merk ich wie schwer es ist den text zu verstehen...sry

    #edit
    achja hab bei google "send really sock" eingegeben und es kam:
    "do really rich people wash their socks?" :D^^



  • Auf die Gefahr hin das dein Source noch wie auf Seite 1 aussieht, zitiere ich nochmal die msdn 😉

    MSDN schrieb:

    For connection-oriented sockets (type SOCK_STREAM for example), calling recv will return as much data as is currently available — up to the size of the buffer specified.

    Quelle: http://msdn2.microsoft.com/en-us/library/ms740121.aspx

    D.h. wenn du recv(socket,&buf,1000,0); aufrufst, sind nachher in buf bis zu 1000 Bytes drin, aber möglicherweise auch weniger. Der Rest käme dann bei folgenden recv()-Aufrufen rein.
    Bei recv() ist das Risiko das nicht alle 1000 Bytes gleich da sind um einen exorbitant großen Faktor größer als das Risiko das send() nicht sofort alles sendet 😉

    Nachtrag - Schnell gefrickelt und ungetestet:

    int recv_complete(SOCKET mySocket, char* myBuffer, int recv_bytes)
    {
        int BytesRead=0;
        char* buff_ptr;
        int BytesLeft=0;
    
        buff_ptr = myBuffer;
        BytesLeft = recv_bytes;
    
        while(BytesLeft > 0)
        {
            BytesRead = recv(mySocket, buff_ptr, BytesLeft, 0);
    	if (BytesRead==0)
    	{
    	     // Socket wurde geschlossen!
    	     return (recv_bytes-BytesLeft);
    	}
    	else if (BytesRead==SOCKET_ERROR)
    	{
    	     // Fehler aufgetreten!
    	     return SOCKET_ERROR;
    	}
    	else
    	{
    	     // Daten empfangen!
    	     buff_ptr+=BytesRead;
    	     BytesLeft-=BytesRead;
    	}        
        }
    
        return recv_bytes;
    }
    


  • ACHTUNG das ist meine meinung, das ist kein erfahrungswert oder getestet geschweige denn irgendwie von mir schrifftlich belegbar

    also das send WENIGER zurückgibt als du angegeben hast wird ausschliesslich dann eintreten, wenn du den hardwareseitigen puffer der netzwerkkarte überlastest.
    ich glaube kaum das beim senden von 4 byte wirklich nur 1 byte übertragen wird, aber wenn du jetzt [utopie] 1GB [/utopie] in form eines array in das send pushst dann wird er nur so viel senden können wie gepuffert werden kann, den rest wird er einfach ignorieren.

    die zitate sind zwar schön und gut, aber kann jemand auch mal zeigen wo steht WANN GENAU der wert kleiner wird als das was eingegeben wurde. Weil sonst wird das hier noch n echt buntes verschwörungstheorieraten ...

    falls keiner was passendes findet ...
    ich tipp mal, da sitzt ein kleines männchen mit axt und immer wenn ein datenstrom durchflutscht versucht er ihn zu treffen _



  • Mit setsockopt() kann man zumindest den "per-socket" Receive und Send Buffer verändern. Allerdings steht bei beiden sowas:

    This is unrelated to SO_MAX_MSG_SIZE and does not necessarily correspond to the size of a TCP send/receive window..

    http://msdn2.microsoft.com/en-us/library/ms740476.aspx

    Welchen Wert der TCP (also nicht Winsock) send/receive Buffer hat: k.A. Für Windows XP war mal auf irgendner Seite irgendwas um die 16K erwähnt und das man dessen Wert wohl auch irgendwo verändern kann.

    Aber egal, diese kleine Einschränkung das nicht unbedingt der gesamte Buffer mit einem Schlag weg ist bzw. empfangen ist lässt sich ja nun eigentlich mit geringstem Aufwand beseitigen...



  • @Ceos:
    Ja, blubb. Blubberliblubbdidubb.
    Wir richten uns nach der Doku und sind deswegen doof, und du bist schlauer oder wie?



  • @hustbaer
    klar bin ich das 🙄 (das war ironisch gemeint)

    nur hab ich mich nicht mit der halbherzigen doku zufrieden gegeben und mal nachgedacht und selber probiert ...
    und heraus kam egal wie schnell ich versucht habe zu senden, nur wenn das array hinreichend groß ist habe ich bei send weniger im return als ich reingeschickt habe ... ausserdem war mein kommentar weniger ein angriff sondern eher ne bitte nicht immerwieder dasselbe zu wiederholen und dann es nach eurem willen auszulegen

    PS google iss ja manchmal echt wertlos

    EDIT hab vergessen zu erwähnen das ich NICHT mit setsockopt rumgespielt hab, womöglich hat die option SO_NODELAY einfluss auf das sendeverhalten



  • Ich glaube das "which can be less than the number requested to be sent in the len parameter" bei send steht da wegen non-blocking sockets.

    Im Blocking-Mode würde das doch auch absolut keinen Sinn machen das die Funktion früher abbricht.



  • In der Doku zu WSASend stehts nämlich so: "Given the same buffer situation and a blocking socket, WSASend will block until all of the application buffer contents have been consumed."



  • send sendet ALLES, sofern es sich um einen Socket im blocking-mode handelt. Bei einem Socket im non-blocking - mode muß geprüft werden, wieviel gesendet worden ist und ggf. weiter gesendet werden.

    Allerdings ist das beim recv nicht so. D.h. wenn ich 1000Byte via send verschicke, ist NICHT gewährleistet, daß ich die mit EINEM recv auch alle bekomme. Beim recv ist IMMER zu prüfen, ob die erwartete Datenmenge gelesen werden konnte, und ggf. weiter zu empfangen.


Anmelden zum Antworten