Kurze Win-Sock Frage



  • Hab zwar einige Tutorials zu Window Sockets gelesen, aber einiges ist mir nicht klar:
    Wenn eine Anwendung beliebig senden und empfangen koennen muss, ist es in Ordnung send und recv hintereinander aufzurufen?
    Beide Funktionen blockieren ja. Wenn ich also 2-mal hintereinander senden will, muss ich zwischendruch irgendwas empfangen.
    Soll ich jetzt 2 Threads machen, einen fuers Senden und einen fuers Empfangen ?
    Oder soll der Server einfach irgendwas belangloses schicken wenn gerade keine "echte" Nachricht ansteht ?



  • Nö, du kannst die sockets auf non-blocking umschalten. Guckst du ioctlsocket und FIONBIO. Dann kannste z.B. mit select() warten bis was ankommt, oder einfach recv/send aufrufen wobei dann einfach soviel zurückkommt wie verfügbar ist bzw. soviel "gesendet" wird wie in die buffer reingeht.

    Oder du kannst io completion ports verwenden, guckst du dazu CreateIoCompletionPort. Für das "HANDLE" was man dort angeben muss kannst du einfach den Wert des SOCKET nehmen den du von socket() oder accept() bekommen hast.



  • Hm, select() benutz ich auf dem Server. Der reagiert auf jedes Event, also send und recv und hat somit eine passive Rolle. Von sich aus, sendet oder empfaengt der Server nicht - er wartet auf eine Aktion des Clients.
    Soweit ich das verstehe kann ich dann nicht auch noch select() auf dem Client benutzen, dann wird ja nurnoch selectet und keine Seite kommt dazu irgendwas zu machen. (Sag mir wenn ich da falsch liege)

    /Edit:
    Die Schwaeche die ich bei nicht-blockierenden Aufrufen sehe ist folgende: Wenn ein send/recv nicht blockiert, wird entweder sofort uebertragen oder aber nach einem timeout abgebrochen. Das fuehrt dazu das nur noch periodenweise Verfuegbarkeit entsteht - Server und Client koennten im worst case immer zum falschen Zeitpunkt testen (beide testen gleichzeitig recv.. timeout.. beide testen send, usw). So wie ich das bisher sehe, muss es bei jeder Methode eine Seite geben die blockiert, aber ich lass mich gern von Gegenteil ueberzeugen.

    /Edit2: Verdammt es ist halb vier 🙄



  • Wenn du nonblocking sockets verwendest, dann gibts keine timeouts mehr, dann wird entweder sofort gesendet/empfangen, oder sofort abgebrochen.
    Was du bei der ganzen Sache aber übersiehst: das Programm der Gegenstelle muss nicht in einem recv() call warten damit ich senden kann. Nehmen wir mal an beide Seiten verwenden nonblocking IO. Ich kann dann einfach drauflos senden, und ein gewisser Teil der Daten wird erstmal im Sendepuffer meines PC landen, und von dort irgendwann rausgeschickt und irgendwann im Empfangspuffer der Gegenstelle landen. Wenn ich dann auf meiner Seite weiter senden will, aber bereits alle Puffer voll sind, dann wird send() einfach mit "kein fehler, aber nix übertragen" zurückkommen. In dem Fall kann ich dann select() verwenden, um zu warten bis ich wieder was senden kann.

    Wenn nun, nachdem ich bereits Daten gesendet habe, und diese bereits im Empfangspuffer der Gegenseite stehen, die Gegenseite recv() aufruft, dann werden so viele Daten kopiert wie eben im Puffer stehen (bzw. angefordert wurden, was halt weniger ist). Ruft die Gegenseite solange recv() auf bis der Puffer leer ist, dann kommt wieder "kein fehler, nix empfangen" zurück.

    Stell dir eine Socket Verbindung als "bounded fifo queue" vor, entweder blocking oder non-blocking, je nachdem was man über ioctlsocket einstellt (default ist blocking). Wie gross der Puffer dabei ist sollte irrelevant sein, wichtig ist nur du kannst senden oder empfangen ohne dass die Gegenstelle unbedingt zeitgleich den gegengesetzten Befehl ausführt, und es gehen trotzdem keine Daten verloren, da diese gepuffert werden. Du kannst dich aber bei nonblocking IO nicht darauf verlassen dass eine ganze Nachricht auf einmal eintrifft, ggf. musst du diese selbst Stückweise zusammenbasteln bis sie fertig ist, es kann sein dass du 10kB auf einmal wegschickst, die Gegenseite aber erstmal 1k empfängt, dann nochmal 5, dann mal wieder eines, und dann die restlichen 3.

    Das heisst solange du TCP verwendest. UDP ist wieder eine ganz andere Geschichte, aber ich denke meine und hoffe du verwendest normale TCP sockets ("SOCK_STREAM").

    Ich würde dir empfehlen dich mal ernsthaft schlau zu machen wie berkeley sockets so funktionieren -- Windows sockets sind im Prinzip berkeley sockets mit ein paar kleinen Änderungen und Erweiterungen.

    EDIT: nö, es ist 4 nach 6 😃



  • Ok, jetzt ist mir einiges klarer, Danke! 🙂
    Also angenommen mein Client benutzt recv nonblocking und mein Server selectet alle Sockets zu denen gesendet werden kann: selbst wenn der Client sich im Moment des Aufrufs von select *nicht* in recv befindet, wuerde der Socket selektiert werden, da ich in einen "Systempuffer" schreiben kann, aus dem sich die Anwendung hinterher die Daten holt.

    Wenn ich das richtig verstehe heisst das aber auch, dass blocking-sockets eine Nachricht immer am Stueck empfangen - also genau so wie sie per send() auch verschickt wurden.. Oder koennen sie auch hier "zerteilt" werden ?



  • Du kannst dich aber bei nonblocking IO nicht darauf verlassen dass eine ganze Nachricht auf einmal eintrifft, ggf. musst du diese selbst Stückweise zusammenbasteln bis sie fertig ist, es kann sein dass du 10kB auf einmal wegschickst, die Gegenseite aber erstmal 1k empfängt, dann nochmal 5, dann mal wieder eines, und dann die restlichen 3.

    Das hat nichts mit non-blocking I/O zu tun. Das ist immer bei TCP so. Man muss so programmieren das es auch funktioniert wenn recv nur 1 Byte holt.



  • plotzen schrieb:

    Das hat nichts mit non-blocking I/O zu tun. Das ist immer bei TCP so. Man muss so programmieren das es auch funktioniert wenn recv nur 1 Byte holt.

    Ok, genau da war ich mir unsicher. Durch Testen kam ich zu der Annahme, dass recv exakt die Nachricht empfaengt die mit send gesendet wurde, aber das scheint wohl nicht so zu sein.
    Noch was: Wenn mein Puffer zu klein fuer die Nachricht ist, was passiert mit den restlichen Daten? Gehen die verloren, oder kommen die beim naechsten recv Aufruf in den naechsten Puffer ?



  • Chase schrieb:

    Ok, genau da war ich mir unsicher. Durch Testen kam ich zu der Annahme, dass recv exakt die Nachricht empfaengt die mit send gesendet wurde, aber das scheint wohl nicht so zu sein.

    das ist z.b. bei udp so (paketorientiert). tcp ist eine art 'streaming protocol'. aus sichtweise der anwendung sind es byte-ströme, keine pakete. das kannste dir ungefähr so vorstellen als würdest du dateien auf der festplatte lesen oder schreiben. das programm, das eine datei öffnet und was rausliest, weiss nicht, wieviele bytes ein anderes programm da reingeschrieben hat. datenträger sind auch 'paketorientiert' (sektoren etc.) aber das filesystem nacht daraus datenströme...

    Chase schrieb:

    Noch was: Wenn mein Puffer zu klein fuer die Nachricht ist, was passiert mit den restlichen Daten? Gehen die verloren, oder kommen die beim naechsten recv Aufruf in den naechsten Puffer ?

    der tcp-stack hat intern einen eigenen buffer. wenn der volläuft, sagt dein tcp der gegenstelle, dass sie langsamer/weniger senden soll bis hin zum stillstand. wenn dein programm aus diesem puffer was ausliest, wird der platz wieder frei und es können ein paar neue bytes empfangen werden.



  • Ist es "legitim" staendig zwischen blocking und nonblocking zu wechseln ? Also wenn ich sende moechte ich das meine Anwendung blockt (daher wartet bis auch wirklich alles raus ist) und sich dann erst darum kuemmert ob es was zu empfangen gibt.

    Dummerweise hab ich noch ein kleines Problem mit einem nicht blockierenden receive: Irgendwie wird immer die gleiche Nachricht aus dem Puffer geholt, dieser wird aber nicht geleert. Kann mir das jemand erklaeren ?



  • Dummerweise hab ich noch ein kleines Problem mit einem nicht blockierenden receive: Irgendwie wird immer die gleiche Nachricht aus dem Puffer geholt, dieser wird aber nicht geleert. Kann mir das jemand erklaeren ?

    das kann nur passieren wenn du bei recv MSG_PEEK angibst



  • Bei Flags hab ich bisher einfach NULL angegeben und hat auch so geklappt.



  • wenn im buffer immer das gleiche steht hat wohl recv nichts gelesen. hast du den rückgabewert überprüft? vielleicht ist er SOCKET_ERROR.



  • Nein, der Rueckgabewert ist != SOCKET_ERROR. Der Buffer wird auch jedesmal genullt, es wird tatsaechlich immer wieder das Gleiche gelesen, der Server sendet auch sicher nur einmal.

    Edit:
    Dummer Fehler: Es hat sich eine unsigned Variable zum Speichern des Rueckgabewertes von recv eingeschlichen 🙄



  • Was spricht denn gegen 2 Threads. Einen zum Senden und einen zum Empfangen.
    Dann kann man sich diese ganze Frickelei mit nicht blockierenden Aufrufen sparen.
    (Das Blockieren ist ja grade das Gute, das dann keine cpu power sinnlos verbraten wird)



  • plotzen schrieb:

    Du kannst dich aber bei nonblocking IO nicht darauf verlassen dass eine ganze Nachricht auf einmal eintrifft, ggf. musst du diese selbst Stückweise zusammenbasteln bis sie fertig ist, es kann sein dass du 10kB auf einmal wegschickst, die Gegenseite aber erstmal 1k empfängt, dann nochmal 5, dann mal wieder eines, und dann die restlichen 3.

    Das hat nichts mit non-blocking I/O zu tun. Das ist immer bei TCP so. Man muss so programmieren das es auch funktioniert wenn recv nur 1 Byte holt.

    Ok, sorry, mein Fehler. Ja, kann bei sockets auch bei blocking io passieren.

    EDIT: @MisterX: gegen 2 threads spricht das was immer gegen threads spricht, nämlich dass threads kompliziert sind und man schnell fehler machen kann ohne es gleich zu bemerken.



  • hustbaer schrieb:

    EDIT: @MisterX: gegen 2 threads spricht das was immer gegen threads spricht, nämlich dass threads kompliziert sind und man schnell fehler machen kann ohne es gleich zu bemerken.

    Naja, einen seperaten Thread braucht man meistens ja sowieso. Nicht jede Anwendung beruht vollkommen auf Events, die Hauptanwendung muss laufen koennen ohne von der Netzkommunikation abhaengig zu sein. Ich wollte nur irgendwie vermeiden zwei Threads (also 3 mit main) benutzen zu muessen. Meine Methode (send blockiert, recv blockiert nicht) klappt auf den ersten Blick ganz gut..



  • Chase schrieb:

    Meine Methode (send blockiert, recv blockiert nicht) klappt auf den ersten Blick ganz gut..

    naja, hoffentlich hast du keine gui-anwendung. wenn 'send' erstmal blockiert, sind deine fensterchen eingefroren...



  • net schrieb:

    Chase schrieb:

    Meine Methode (send blockiert, recv blockiert nicht) klappt auf den ersten Blick ganz gut..

    naja, hoffentlich hast du keine gui-anwendung. wenn 'send' erstmal blockiert, sind deine fensterchen eingefroren...

    Seperater Thread :p



  • Hm. Wenn man mit non-blocking io, completion ports etc. arbeitet kommt man eigentlich oft mit einem thread aus.


Anmelden zum Antworten