Bei send() und recv() wirklich ALLES senden bzw. empfangen?



  • Nein, Server in C auf Linux, Client in C++ mit WinAPI auf Windows.
    sizeof(TM_DATA) lieget unter Linux und Windows das selbe.



  • ChrisK schrieb:

    Diese 20 Suchergebnisse haut der Server in einer While-Schleife sehr schnell raus!

    ChrisK schrieb:

    ...kommt irgendwie nicht immer alles an bzw. ein Paket kommt nicht vollständig an und ein Rest des Paketes kommt hinterher.

    Prüfe mal ob recv () ev. eine Fehlermeldung liefert :

    WSAEMSGSIZE

    The message was too large to fit into the specified buffer and was truncated.



  • Ein "OK" schicken ist wohl die "unperfekteste" Lösung die ich mir vorstellen kann, da es jedesmal eine round-trip-time kostet. Nicht so wild wenn es in einem lokalen Netzwerk passiert, sehr sehr blöd dagegen wenn man mal nen Ping von 300ms oder so hat.

    Wichtig ist dass der empfangende Teil vor dem empfangen genau weiss wieviel Daten ankommen. Wenn du also 20 Pakete verschickst, dann muss der entweder vorher wissen dass 20 kommen, oder du musst vor jedem Paket z.B. ein Byte als Marker schicken ob noch ein Paket kommt oder ob es das letzte war. Wenn jedes Paket gleich gross ist reicht das. Wenn die Pakete unterschiedlich gross sind musst du halt einfach vor dem Paket noch zusätzlich die Paketgrösse mitschicken.

    Davon abgesehen brauchst du eben solche send_n und recv_n Funktionen, da send() und recv() selbst nur soviel Daten liefern/annehmen wie gerade verfügbar sind bzw. für was gerade Platz in den Sendepuffern ist.

    Die Implementierung würde ich nicht genau so machen wie du es machst, da du z.B. einmal einen Fehlercode zurückgibst (EINTR, -1), und sonst zurückgibst wieviel Daten nicht übergragen wurden. Nicht schön IMHO.

    Im übrigen würde ich auch einen Returnwert von 0 als Fehler behandeln, denn 0 kannst du AFAIK eigentlich nur bekommen wenn der Socket geschlossen wurde. (EDIT: natürlich vorausgesetzt du verwendest blocking Sockets. Solltest du auch erstmal wenn du mit Sockets noch keine Erfahrung hast, und sollte auch Default sein)

    Auf jeden Fall ist es soweit ich dein Problem verstehe hier nicht nötig irgendwelche Bestätigungen zu schicken, oder gar irgendwelche Sleeps oder sonstwas einzubauen. Glaube es mir, ich hab' selbst schon Netzwerkcode geschrieben über den täglich mehrere GB Daten übertragen werden, und da ist kein einziges Sleep drin. Und auch an vielen Stellen keine Bestätigungen (wenn sie nicht notwending sind).

    Bestätigungen schicken macht eigentlich nur dann Sinn wenn es an dem Punkt wichtig ist dass der Client eine bestimmte Aktion bereits ausgeführt hat, bevor der Server etwas macht -- oder eben umgekehrt. Wenn du z.B. wichtige Daten aus einer Journaltabelle überträgst, dann lässt du dir natürlich von der Gegenseite eine Bestätigung schicken, bevor du die auf deiner Seite löscht (bzw. als "übertragen" markierst), klar. Wenn du aber nur das Ergebnis einer ad-hoc Abfrage zurückschickst ist das nicht notwendig -- wenn der Client wirklich abgeschmiert ist oder die Connection zusammengebrochen ist, kann er die Anfrage (wenn er die Ergebnisse dann noch braucht) ja einfach nochmal schicken.



  • hustbaer schrieb:

    Im übrigen würde ich auch einen Returnwert von 0 als Fehler behandeln, denn 0 kannst du AFAIK eigentlich nur bekommen wenn der Socket geschlossen wurde.

    Bei send() ist 0 glaube ich ein gültiger Rückgabewert (Allerdings wohl nur wenn 0-Bytes gesendet werden?)

    Er hat ja solche send_n/recv_n Funktionen, ich sehe in denen auch leider keinen Fehler 😕

    Ich frage mich gerade, ob unter Unix nicht nen nread=0 erlaubt wäre? Oder heisst das da auch "Verbindung getrennt" ?

    else if (nread == 0) break;
    


  • recv = 0 bedeutet socket beendet, sonst blockiert recv solange bis daten kommen oder wirft glaube nen timeout in wsagetlaserror() (ausgenommen nonblocking mode)



  • Ich habe auch mal so eine Empfangsschleife gebaut und auch seltsammte Effekte festgestellt die auf manchen Implementierungen und Compilern auftreten.

    Wenn man dein Programm betrachtet, dürfte ja nichts fehlen, da du in einer Schleife abfragst wieviel empfangen wurde

    if((nread = recv(fd, ptr, nleft, 0)) < 0)
    

    und dann berechnest, wieviel nachgeladen werden muß und an welche Stelle

    nleft = nleft - nread; 
    ptr = ptr + nread;
    

    Dabei tritt ein Fehler auf, da fread anscheinend bei manchen Compilern immer mindestens um die groesse des Struct weiterrückt.

    Ich meine Folgendes.
    Du hast z.B. ein Strukt der groesse 12 Byte. Sagen wir mal 4 Byte wurden empfangen.
    Jetzt berechnest du, dass 8 Byte nachgeladen werden müssen und 4 wurden schon geladen.
    Da aber ein Strukt 12 Byte groß ist versucht recv die 8 Byte an Position
    4 + 12= 16 Byte nachzuladen, weil es um die Groesse des Struct vorrückt. Somit wird fremder Speicherbereich überschrieben und das Strukt ist unvollständig.
    (Oder es sieht so aus, dass der Rest im nächsten Struct kommt)

    Das erklährt deine Beobachtung:
    [quote] Beim Server kommt irgendwie nicht immer alles an bzw. ein Paket kommt nicht vollständig an und ein Rest des Paketes kommt hinterher[quote]

    Wenn du ein Sleep(500) einbaust, wird sichergestellt, dass soviel da ist um ein
    Struct voll zu beschreiben und der Fehler passiert nicht. Dies kann aber aufgrund der Groesse nicht im Sinn des Erfinders sein.

    Eine Lösung währe es erst alles in ein array von chars einzulesen (Wieder in der Schleife mit der selben char Arraylänge wie dein Struct) und dann anschließend in die gewünschte Struct zu casten. Dann verrechnet sich recv nicht bei der Speicherposition.

    Jetzt werden bestimmt einige sagen:"So nen quatsch, rechv rückt nicht um die Länge des Struct vor"
    Aber ich habe genau DISES schon nachvollziehen können und es passiert wirklich. Außerdem beschreibt es GEANU den hier geschilderten Fehler!

    Das komische ist, das der Effekt bei manchen Compilern auftritt, bei anderen nicht.



  • @MisterX :

    Bei fwrite/fread gibst du die Grösse der "Einheit" mit, und zusätzlich nochmal die Anzahl der "Einheiten". Bei send/recv ist das nicht so -- da gibts nur die Anzahl der Bytes.
    Woher soll send oder recv wissen dass der Zeiger der da übergeben wird auf ein struct zeigt?
    Ne, daran *kann* es nicht liegen. Und wenn du 100 mal sagst es ist so sag ich 101 mal es kann nicht sein.

    Es kann höchstens daran liegen dass man vielleicht die Regeln der C bzw. C++ Zeigerarithmetik nicht verstanden hat. Wer natürlich nen Zeiger auf die Struktur verwendet, und auf den dann "+= n" macht, der muss sich nicht wundern wenn der Zeiger um n Strukturen und nicht n chars weiterwandert. Wer aber "+= n" auf nen char Zeiger macht wird den char Zeiger um n chars weiterrücken.

    Obwohl... ich kenne einen "C Compiler" der das falsch umgesetzt hat, also Zeiger immer um n chars weitergerückt hat, egal welchen Typs der Zeiger war. Bloss das ist 15 Jahre her, und das Teil war damals schon Schrott.

    Das alles stellt aber im hier geposteten Code kein Problem dar, da hier brav mit char Zeigern gearbeitet wird. Blubb.

    p.S.: du hast nicht zufällig noch Code der davon betroffen ist? Und den Namen je eines Compilers wo es geht bzw. nicht geht? Wäre interessant...

    p.p.S.: recv rückt *garnicht* vor. recv gibt bloss zurück wieviel gelesen wurde. Vorrücken muss man selbär. Nur so nebenbei.



  • mal abgesehen von euren einwänden (die ich nicht im detail gelesen habe), wenn du ein struct von 12 bytes hast heißt das nicht unbedingt das das nachfolgende struct an position 13 anfängt, stichwort "speicherausrichtung" ...
    meine frage daher welchen compiler verwendest du?
    versuch es mal mit 4 dummy bytes in deiner struct am ende damit die structgröße durch 8 teilbar ist oder (falls du borland verwendest)

    #pragma pack(push, 1)
    
    //dein struct
    
    #pragma pack(pop)
    

    ich kann mich noch an schulzeit erinnern, da gab es irgendwie ein __attribute_packed oder so ähnlich was man bei der struct definition einfügen kann um ähnliche effekte zu erzielen

    EDIT ich arbeite hier mit structs und anderen daten im größenverhältnis bis 2000 bytes und hab absolut keine probleme, ich versteh nich was da mit sleep helfen soll, ... es hilft einzig und allein nur das du pro sekune nur 2 pakete empfangen kannst -.-



  • @Ceos: wenn man über sizeof(struct) geht ist es egal wie die Struktur gepackt wird. Natürlich muss das Packing beim Client und beim Server gleich sein, klar.



  • naja, was ich meine, wenn er um BYTES inkrementiert zwischen den structs aber verlorene bytes liegen kann doch gut sein das bis zu 7 byte verloren gehn



  • Jo, OK. Aber wer tut schon sowas böses 😉



  • ich weis zwar nichtmehr wo genau aber ich hatte etwas ähnliches (ich glaub es war ne diskrepanz zwischen 2 projekten die über netzwerk kommunizierten) in dem moment hast natürlich du recht, wenn zwischen beiden programmen die speicherausrichtung stimmt müsste es normal funktionieren



  • EDIT ich arbeite hier mit structs und anderen daten im größenverhältnis bis 2000 bytes und hab absolut keine probleme, ich versteh nich was da mit sleep helfen soll, ... es hilft einzig und allein nur das du pro sekune nur 2 pakete empfangen kannst -.-

    Das das slepp(500) hilft untersctützt wieder meine Theorie, dass es in dieser einleseschleife schiefgeht. Denn wenn man ne halbe sekund wartet ist die Wahrscheinlichkeit, das das paket mitlerweile ganz da ist doch ziemlich groß.
    Dann wird es auf einen Schlag in die Struckt geschreiebn und diese Einleseschleife, mit dem Falschen "vorrücken" wird nicht bbenutzt. Denn die Daten sind ja auf einen Schlag vorhanden.

    Das mit dem Warten auf das sleep ist natürlich keine Lösung die man anstreben sollte. Teste doch wirklich mal Alles erst in nem char Array einzulesen (in der Schleife) und erst dann in die Struckt zu casten. Wenns nicht hilft ist Meine Theorie falsch, aber testen kannstes ja mal. (Vorallem da es keine bessere Lösungstrategie hier gibt)

    Obwohl... ich kenne einen "C Compiler" der das falsch umgesetzt hat, also Zeiger immer um n chars weitergerückt hat, egal welchen Typs der Zeiger war. Bloss das ist 15 Jahre her, und das Teil war damals schon Schrott.

    PS. Mir ist der Fehler mit dem Visual C++ 6.0 aufgefallen, in der ursprünglichen Version ohne SDK updates.
    (Der war zwar auch schrottig, ist aber noch keine 15 jahre her 😃 )



  • (Vorallem da es keine bessere Lösungstrategie hier gibt)

    Sorry,
    ich hatte die Idee mit den 4 Dummy Bytes überlesen. Teste erst mal das und wenns nicht hilft meine Idee. Denn das mit den 4 Dummy Bytes ist wohl erst mal weniger Arbeit 😃


Anmelden zum Antworten