Domanda

Qual è il modo normale in cui le persone che scrivono codice di rete in Delphi utilizzano I/O socket asincrono sovrapposto in stile Windows?

Ecco la mia ricerca precedente su questa domanda:

IL Indy i componenti sembrano del tutto sincroni.D'altra parte, mentre l'unità ScktComp utilizza WSAAsyncSelect, fondamentalmente asincronizza solo un'app socket multiplex in stile BSD.Vieni scaricato in un singolo callback di evento, come se fossi appena tornato da select() in un ciclo, e devi eseguire tu stesso tutta la navigazione della macchina a stati.

La situazione .NET è notevolmente migliore, con Socket.BeginRead / Socket.EndRead, dove la continuazione viene passata direttamente a Socket.BeginRead, ed è da lì che si riprende.Una continuazione codificata come chiusura ha ovviamente tutto il contesto di cui hai bisogno, e altro ancora.

È stato utile?

Altri suggerimenti

Ho scoperto che Indy, sebbene all'inizio sia un concetto più semplice, è scomodo da gestire a causa della necessità di interrompere i socket per liberare thread alla terminazione dell'applicazione.Inoltre, la libreria Indy ha smesso di funzionare dopo un aggiornamento della patch del sistema operativo.ScktComp funziona bene per la mia applicazione.

@Roddy - Le prese sincrone non sono ciò che cerco.Masterizzare un intero thread per il bene di una connessione possibilmente di lunga durata significa limitare la quantità di connessioni simultanee al numero di thread che il processo può contenere.Poiché i thread utilizzano molte risorse (spazio di indirizzi dello stack riservato, memoria dello stack impegnata e transizioni del kernel per i cambi di contesto), non si adattano quando è necessario supportare centinaia di connessioni, tanto meno migliaia o più.

Qual è il modo normale in cui le persone che scrivono codice di rete in Delphi usano I/O con la presa asincrona sovrapposta a Windows?

Bene, Indy è ormai da molto tempo la libreria "standard" per l'I/O dei socket - ed è basata sul blocco dei socket.Ciò significa che se desideri un comportamento asincrono, utilizzi thread aggiuntivi per connettere/leggere/scrivere dati.A mio avviso questo è in realtà un grande vantaggio, poiché non è necessario gestire alcun tipo di navigazione della macchina a stati o preoccuparsi di procedure di callback o cose simili.Trovo che la logica del mio thread di "lettura" sia meno ingombrante e molto più portabile di quanto consentirebbero i socket non bloccanti.

Indy 9 per noi è stato per lo più a prova di bomba, veloce e affidabile.Tuttavia il passaggio alla Indy 10 per Tiburon mi desta un po' di preoccupazione.

@Mike:"...la necessità di uccidere i socket per liberare i thread...".

Questo ha fatto andare "eh?" Fino a quando non mi sono ricordato che la nostra libreria di threading utilizza una tecnica basata su eccezioni per uccidere i thread "in attesa" in modo sicuro.Noi chiamiamo QueueUserAPC per mettere in coda una funzione che solleva un'eccezione C++ (NON derivata dalla classe Exception) che dovrebbe essere catturata solo dalla nostra procedura wrapper del thread.Tutti i distruttori vengono chiamati in modo che i thread terminino tutti in modo pulito e riordinati all'uscita.

"Le prese sincrone non sono ciò che cerco."

Capito, ma penso che in questo caso la risposta alla tua domanda originale è che semplicemente non esiste un Delphi idioma per l'IO del socket asincrono perché in realtà è un requisito altamente specializzato e non comune.

Come questione secondaria, potresti trovare interessanti questi collegamenti.Sono entrambi un po' vecchi e più *nxy di Windows.La seconda implica che, nell'ambiente giusto, i thread potrebbero non essere così dannosi come pensi.

Il problema del C10K

Perché gli eventi sono una cattiva idea (per server ad alta concorrenza)

@Chris Miller - Ciò che hai affermato nella tua risposta è effettivamente inesatto.

L'asincrono in stile messaggio di Windows, disponibile tramite WSAAsyncSelect, è in effetti in gran parte una soluzione alternativa per la mancanza di un modello di threading adeguato nei giorni di Win 3.x.

.NET Begin/End, tuttavia, lo è non utilizzando thread aggiuntivi.Utilizza invece l'I/O sovrapposto, utilizzando l'argomento aggiuntivo su WSASend / WSARecv, in particolare la routine di completamento sovrapposto, per specificare la continuazione.

Ciò significa che lo stile .NET sfrutta il supporto I/O asincrono del sistema operativo Windows Evitare bruciando un thread bloccandolo su un socket.

Poiché i thread sono generalmente costosi (a meno che non si specifichi una dimensione dello stack molto piccola in CreateThread), il blocco dei thread sui socket ti impedirà di scalare fino a 10.000 connessioni simultanee.

Questo è il motivo per cui è importante utilizzare l'I/O asincrono se si desidera scalare e anche perché .NET è non, ripeto, lo è non, semplicemente "utilizzando i thread, [...] semplicemente gestiti dal Framework".

@Roddy - Ho già letto i collegamenti a cui indichi, entrambi fanno riferimento alla presentazione di Paul Tyma "Migliaia di thread e blocco I/O - Il vecchio modo di scrivere server Java è di nuovo nuovo".

Alcune delle cose che non saltano necessariamente fuori dalla presentazione di Paul, tuttavia, sono che ha specificato -Xss:48k alla JVM all'avvio e che sta presupponendo che l'implementazione NIO della JVM sia efficiente affinché sia ​​un valido confronto.

Indy lo fa non specificare una dimensione dello stack altrettanto ridotta e strettamente vincolata.Non ci sono chiamate a BeginThread (la routine di creazione del thread Delphi RTL, che dovresti usare per tali situazioni) o CreateThread (la chiamata WinAPI grezza) nel codice base di Indy.

La dimensione predefinita dello stack è memorizzata nel PE e per il compilatore Delphi il valore predefinito è 1 MB di spazio di indirizzi riservato (lo spazio viene impegnato pagina per pagina dal sistema operativo in blocchi da 4 KB;infatti, il compilatore deve generare codice per toccare le pagine se ci sono più di 4K di elementi locali in una funzione, poiché l'estensione è controllata da errori di pagina, ma solo per la pagina più bassa (di guardia) nello stack).Ciò significa che esaurirai lo spazio degli indirizzi dopo un massimo di 2.000 thread simultanei che gestiscono le connessioni.

Ora puoi modificare la dimensione dello stack predefinita nel PE utilizzando la direttiva {$M minStackSize [,maxStackSize]}, ma ciò influirà Tutto thread, incluso il thread principale.Spero che tu non faccia molta ricorsione, perché 48K o (simile) non è molto spazio.

Ora, se Paul abbia ragione riguardo alla mancata prestazione dell'I/O asincrono per Windows in particolare, non ne sono sicuro al 100%: dovrei misurarlo per esserne certo.Quello che so, tuttavia, è che le argomentazioni secondo cui la programmazione threaded è più semplice della programmazione asincrona basata su eventi, stanno presentando un falsa dicotomia.

Il codice asincrono no Bisogno essere basato sugli eventi;può essere basato sulla continuazione, come in .NET, e se specifichi una chiusura come continuazione, ottieni il mantenimento dello stato gratuitamente.Inoltre, la conversione dal codice in stile thread lineare al codice asincrono in stile passaggio di continuazione può essere resa meccanica da un compilatore (la trasformazione CPS è meccanica), quindi non è necessario alcun costo nemmeno in termini di chiarezza del codice.

Sono disponibili componenti socket IOCP (porte di completamento): http://www.torry.net/authorsmore.php?id=7131 (codice sorgente incluso)

"Di Naberegnyh Sergey N..Server Socket ad alte prestazioni in base alla porta di completamento di Windows e con l'utilizzo delle estensioni di Windows Socket.IPv6 supportato."

L'ho trovato mentre cercavo componenti/librerie migliori per ristrutturare il mio piccolo server di messaggistica istantanea.Non l'ho ancora provato ma sembra buono codificato come prima impressione.

Indy utilizza socket sincroni perché è un modo più semplice di programmare.Il blocco dei socket asincroni era qualcosa di aggiunto allo stack Winsock ai tempi di Windows 3.x.Windows 3.x non supportava i thread e lì non era possibile eseguire l'I/O del socket senza thread.Per ulteriori informazioni sul motivo per cui Indy utilizza il modello di blocco, vedere Questo articolo.

Le chiamate .NET Socket.BeginRead/EndRead utilizzano thread, sono semplicemente gestite dal Framework anziché dall'utente.

@Roddy, Indy 10 è stato fornito in bundle con Delphi da Delphi 2006.Ho scoperto che la migrazione da Indy 9 a Indy 10 è un compito semplice.

Con le classi ScktComp, è necessario utilizzare un server ThreadBlocking anziché un tipo di server NonBlocking.Utilizza l'evento OnGetThread per trasferire il parametro ClientSocket a un nuovo thread di tua ideazione.Dopo aver istanziato un'istanza ereditata di TServerClientThread, creerai un'istanza di TWinSocketStream (all'interno del thread) che potrai utilizzare per leggere e scrivere sul socket.Questo metodo ti evita di tentare di elaborare i dati nel gestore eventi.Questi thread potrebbero esistere solo per il breve periodo necessario per leggere o scrivere, oppure resistere per tutta la durata allo scopo di essere riutilizzati.

L'argomento relativo alla scrittura di un server socket è piuttosto vasto.Esistono molte tecniche e pratiche che potresti scegliere di implementare.Il metodo di lettura e scrittura sullo stesso socket con TServerClientThread è semplice e adatto per applicazioni semplici.Se hai bisogno di un modello per alta disponibilità e alta concorrenza, allora devi esaminare modelli come il modello Proactor.

Buona fortuna!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top