Frage zu Doppelzeiger (IO-Funktion + GetQueuedCompletionStatus)


  • Mod

    Jodocus schrieb:

    Geht auch ohne Vererbung mit dem Makro CONTAINING_RECORD() 😉

    Ich habe doch geschrieben, dass es ohne Vererbung geht und auch besser wäre...
    Ich verstehe Deinen Einwurf nicht.
    Zudem löst es nicht das Grundproblem, dass der OP nämlich einen Zeiger auf einen Zeiger benötigt!



  • > dass der OP nämlich einen Zeiger auf einen Zeiger benötigt!

    Hä? Den braucht er doch überhaupt nicht.
    Mein Post war kein Einwand, sondern eine gängige Möglichkeit, aus einem Member das Container-Objekt zu ermitteln (was auch funktioniert, wenn OVERLAPPED nicht der erste Member der Struktur ist). 😕


  • Mod

    Jodocus schrieb:

    Hä? Den braucht er doch überhaupt nicht.

    Nicht?

    Dann schau Dir mal die Definition von GetQueuedCompletionStatus an.
    http://msdn.microsoft.com/en-us/library/aa364986(VS.85).aspx

    Nach meinem Dafürhalten ist "LPOVERLAPPED *lpOverlapped" ein Zeiger auf einen Zeiger, und damit hat der OP anscheinend Probleme...
    Just my 2 cents!



  • Ich glaube, ich verwende lieber den completion key (unsigned int*) 😃
    Sollte eigentlich genau so klappen. Man hat dann halt keine verschiedenen Kontexte für zB. AcceptEx und WSARecv, aber dann packt man halt alles nötige in eine einzige ClientContext-Struktur.
    Oder warum sollte man das nicht machen?



  • Huch, da hab ich das LP überlesen, sorry Martin. 😃
    Anyway, das Problem ist ja nicht der Doppelzeiger, mit dem hat er ja eigentlich fast nichts zu tun, bisauf das Übergeben der Adresse.

    @ TS: Das ist doch ganz einfach:

    enum operation_type {
      accept, send, recv, ...
    };
    
    // POD
    struct container {
       operation_type type;
       WSAOVERLAPPED overlapped;
    };
    
    container c = new container;
    std::memset(c, sizeof *c, 0);
    c->type = accept;
    AcceptEx(..., c, ...);
    
    ...
    
    WSAOVERLAPPED* ol_ptr = 0;
    GetQueuedCompletionStatus(..., &ol_ptr, ...);
    container* con_ptr = CONTAINING_RECORD(ol_ptr, container, overlapped);
    event_workers[con_ptr->type](..., con_ptr, ...);
    

    Mit dem CONTAINING_RECORD-Makro erhälst du den Container, der das OVERLAPPED-Objekt enthält (intern natürlich per Casts). In einem Array von Funktoren kannst du dann entsprechende Funktionen sammeln, die über eine Operation-ID dispatcht werden.

    > Ich glaube, ich verwende lieber den completion key (unsigned int*) 😃

    Den solltest du benutzen, aber nicht für Per-I/O-Daten. Der Completion-Key ist für gewöhnlich der Socket-Deskriptor, auf dem die Operation stattgefunden hat. 😉

    Edit: Hoffentlich bist du auch über den korrekten Umgang mit AcceptEx() aufgeklärt.



  • WSAOVERLAPPED overlapped; <- müsste dann aber die erste Membervariable sein, damit es funktioniert



  • Nö, der Offset wird intern abgezogen. 😉
    Wird bei mir jedenfalls so definiert:

    #define CONTAINING_RECORD(address,type,field) ((type* )( (PCHAR)(address) - (ULONG_PTR)(&((type )0)->field)))
    


  • Dann darfst du aber bei AcceptEx nicht direkt c übergeben sondern &c.overlapped



  • Hmm, guck ich mir mal an.

    Jodocus schrieb:

    Den solltest du benutzen, aber nicht für Per-I/O-Daten.

    Warum nicht? Kann auch gleich eine ganze Struktur übergeben.
    Und ich brauche ja pro Client nicht mehr als einen Kontext und einen WSARecv-Aufruf.
    Dann bei GetQueuedCompletionStatus() habe ich den Clientkontext verfügbar. Ist doch genau so wie mit dem OVERLAPPED *.

    Jodocus schrieb:

    Edit: Hoffentlich bist du auch über den korrekten Umgang mit AcceptEx() aufgeklärt.

    Vielleicht kannst du dazu noch kurz was erwähnen? 🙂 (Außer natürlich, dass man sich unbedingt den Funktionszeiger holen sollte. Und vllt. noch setsockopt mit SO_UPDATE_ACCEPT_CONTEXT. Und natürlich immer schön null-initialisierte OVERLAPPED-Strukturen zu verwenden 🙄 🙂 ).



  • > Dann darfst du aber bei AcceptEx nicht direkt c übergeben sondern &c.overlapped

    Logo. 😉

    > Warum nicht? Kann auch gleich eine ganze Struktur übergeben.

    Weil eine Verbindung nicht viel mit einer Operation zu tun hat. Per-Handle-Data und Per-I/O-Data sind doch in diesem Kontext verschieden.
    Für Per-Handle-Data speichert man so Sachen wie Socket-Deskriptor, gesendete/empfangene Bytes, Adresse etc., hingegen benutzt die Per-I/O-Data für Sachen wie Fehlercode, Puffer, Operationstyp etc. Wenn du das alles in eine einzige monolithische Struktur presst, hast du oft Informationen an manchen Stellen, die du gar nicht brauchst.
    Du kannst es natürlich machen, technisch spricht auch nichts dagegen. Es ist eben üblich, I/O-Completionports auf diese Weise zu handhaben. (jaja, ich weiß, Dogmatismus :D)

    > Vielleicht kannst du dazu noch kurz was erwähnen?

    Benutzt du den übergebenen Puffer, um gleich auch Daten vom Client zu empfangen?



  • Alles klar, werd ich bedenken. Danke!

    Jodocus schrieb:

    Benutzt du den übergebenen Puffer, um gleich auch Daten vom Client zu empfangen?

    Nope. Hab ich lieber gelassen, wegen stale clients.
    Macht für mich auch irgendwie überhaupt keinen Sinn, bei AcceptEx schon was zu empfangen. Wobei... soll das etwa für "Autorisierungen" sein?



  • > Wobei... soll das etwa für "Autorisierungen" sein?

    Nö, für Performance. Du kannst im Kernelmode gleich 2 Operationen ausführen, anstatt explicit WSARecv() zu benutzten (du sparst dir einen switch in den Usermode). Aber schon AcceptEx() brauchst du nur bei äußerster Performance. Was programmierst du überhaupt?

    > Nope. Hab ich lieber gelassen, wegen stale clients.

    Dafür gibt es ja Lösungsmöglichkeiten. 😉



  • Jo, aber wenn's nicht sein muss...

    Eigentlich nur nen kleinen Server/Client Core für ein, zwei kleinere zukünftige Projekte.
    Aber das Prinzip completion ports gefällt mir am besten. Deshalb möchte ich es verwenden und mich nicht mit select-Gefrickel o.Ä. herumschlagen und auch gleich was lernen.



  • Naja, nachdem du dir das angeguckt hast, kannst du auch gleich zu boost::asio greifen. 🙂


Anmelden zum Antworten