Fragen? Antworten! Siehe auch: Alternativlos
Der Unix-Ansatz ist, daß man die Sockets alle auf "non-blocking" setzt. Wenn man dann read aufruft, aber nichts da ist zum Lesen, blockt der Prozeß nicht, sondern man kriegt "probier später nochmal" als "Fehler"-Meldung. Dann benutzt man ein Event Notification API, um sich sagen zu lassen, auf welchen Sockets gerade Daten reingekommen sind.
Unter Windows ist der Ansatz umgekehrt: es gibt keinen Non-Blocking Mode für Sockets. Man startet ein read mit fixem Zielpuffer, und das Event Notification Framework von Windows sagt einem dann nicht, wenn es was zu lesen gibt, sondern wenn es was zu lesen gab, d.h. zu dem Zeitpunkt ist das schon gelesen. Das Problem dabei: Wie macht man Timeout-Handling? Man kann nicht unterwegs fragen, wie weit der Datei-Rausschieben-Job schon gekommen ist! Also kann man auch nicht wissen, ob die Verbindung seit 10 Minuten hängt, oder ob da nur ein sehr langsames Modem auf der Gegenseite ist.
Und dann schreiben die Man-Pages was von Background-Threads, … also kurz gesagt: der Windows-Ansatz ist: "gib mal her, der Kernel macht das schon". Nur leider macht der Kernel das nicht besonders gut. Kernel-Threads, das erweckt eine Vision von einem Thread pro ausstehendem I/O-Request, anstelle einer anständigen State Machine.
Ein anderes Problem ist, wie man Handles alloziert. Man macht einen Socket oder eine Datei auf, und kriegt ein Handle vom System. Unter Unix ist das traditionelle Problem, daß das ein Integer ist, und das man die kleinste unbelegte Zahl als Integer kriegt. Linux und die BSDs haben sich da unglaubliche Algorithmen überlegt, um das schnell zu erledigen (einfach ist es jedenfalls nicht). Windows liefert einfach nicht den kleinsten Integer aus, sondern irgendeinen. Man sollte denken: Problem erledigt. Aber dann stößt man auf Funktionen wie AcceptEx, wo man einen Deskriptor wiederverwenden kann, weil es offenbar unter Windows immer noch immens teuer ist, ein Handle zu allozieren. WTF?!
Schlimmer noch: der Normalfall bei TransmitFile (dem sendfile-Ersatz unter Windows) ist, daß das ganze File am Stück gesendet wird, und man kann dann per Flag sagen: "mach danach mal die Verbindung zu". Kurzum: Timeout-Handling, nein danke. Und dieses Handle-Wiederverwerten legt die Vermutung nahe, daß typische Server, selbst wenn sie dieses Ultra-Skalierbarkeits-API verwenden, trotzdem nur n Verbindungen parallel haben können, weil man die Handles unter Windows halt vorher als Pool alloziert und mehr geht dann halt nicht. Oder wie?
Man darf gespannt sein, wie sich das dann unter realen Gesichtspunkten mißt. Aber ich mache mir da keine großen Hoffnungen, wenn ich Formulierungen wie "The server optimizes the TransmitFile function for high performance. Workstations optimize the function for minimum memory and resource utilization." lese. Ich hab hier "nur" Windows XP Professional zum Testen, und da performt das ja offensichtlich absichtlich scheiße, damit die Leute lieber die Advanced Datacenter Ultra Monster HeadShot Edition kaufen. Überhaupt ist TransmitFile der Kludge des Jahres. Man sendet immer Chunks aus einer Datei, als Länge maximal 32 Bit. Dateien mit mehr als 4 Gigabyte schickt man dann, indem man halt mehrfach TransmitFile aufruft. Womit wir dazu kommen, wie man sagt, ab welchem Offset in der Daten TransmitFile schicken soll. TransmitFile sendet ab der aktuellen Byteposition im File, aber die Man Page rät einem, man möge die Overlapped-Struktur (die für alle anderen Fälle opak ist und interne Fortschrittsinformationen für asynchrones I/O für den System-Thread ablegt, der es bearbeitet) manipulieren, um das Offset zu setzen. Es ist Pfusch wie dieser, der das alles nicht schön aussehen läßt. Na mal gucken.