...e chi non usa la ifdef dovrà vedersela con me... |
Allora, il pezzo di codice (estratto da quel post lì) che dobbiamo modificare è questo:
... // accetta connessioni da un client entrante printf("%s: attesa connessioni entranti...\n", argv[0]); socklen_t socksize = sizeof(struct sockaddr_in); struct sockaddr_in client; // (remote) client socket info int client_sock; if ((client_sock = accept(my_socket, (struct sockaddr *)&client, &socksize)) < 0) { // errore accept() printf("%s: accept failed (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } ...E la nuova versione con compilazione condizionale via #ifdef sarà la seguente:
... // accetta connessioni da un client entrante (in non blocking mode: my_socket è SOCK_NONBLOCK) printf("%s: attesa connessioni entranti...\n", argv[0]); socklen_t socksize = sizeof(struct sockaddr_in); struct sockaddr_in client; // (remote) client socket info int client_sock; #ifdef OLD_LINUX if ((client_sock = accept(my_socket, (struct sockaddr *)&client, &socksize)) < 0) { #else if ((client_sock = accept4(my_socket, (struct sockaddr *)&client, &socksize, SOCK_NONBLOCK)) < 0) { #endif // errore accept() printf("%s: accept failed (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } #ifdef OLD_LINUX else { // accept eseguita: set socket a non-blocking int flags; if ((flags = fcntl(client_sock, F_GETFL, 0)) >= 0) { if (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) < 0) { // errore accept() printf("%s: fcntl failed (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } } } #endif ...Ok, come vedete è ampiamente commentato e quindi è auto-esplicativo, per cui non mi dilungherò sulle singole istruzioni e/o gruppi di istruzioni (leggete i commenti! sono li per quello!), ma aggiungerò, solo, qualche dettaglio strutturale.
Nelle parti incluse negli #ifdef OLD_LINUX c'è la versione di codice che NON usa la accept4(), ed è, ovviamente, un po' più complicata dell'altra, visto che ci tocca usare fcntl() per rendere non-bloccante il socket creato, mentre la accept4() lo crea direttamente passandogli, come detto, il flag SOCK_NONBLOCK. Comunque, come potete notare, il codice risultante è abbastanza chiaro e leggibile e, tutto sommato, non ha un brutto aspetto (lo stile prima di tutto!).
Qualcuno potrebbe dire: la versione sotto #ifdef funziona anche con un Linux recente, quindi perché non lasciare solo quella e togliere le #ifdef? NO! NO! e ancora NO! Non dobbiamo scrivere codice old-style per essere retro-compatibili: bisogna sempre cercare di scrivere in maniera moderna, e in caso di necessità (come nell'esempio) il vecchiume lo mettiamo in #ifdef. E quando sarà il momento (quando, per esempio, non ci servirà più un doppio ambiente operativo) faremo pulizia e lasceremo solo un bel codice moderno.
Ovviamente la compilazione condizionale la realizzeremo inserendo (o non inserendo) una istruzione -D OLD_LINUX nella linea del nostro makefile che genera i file oggetto, oppure direttamente nella linea di comando se non usiamo un makefile. Bene, finalmente abbiamo scritto un unico codice per due ambienti operativi diversi: missione compiuta!
Solo un ultima precisazione sul nostro socket server modificato (e che non centra niente con gli ifdef): se il non-blocking socket ci serve per eseguire delle recv() non bloccanti nella fase successiva a quella di accept, è molto più semplice modificare opportunamente il loop di ricezione e passare il flag MSG_DONTWAIT alla recv() nell'argomento flags (il quarto). Così la recv() non blocca in entrambi gli ambienti operativi, e tutto senza usare le #ifdef. Ma questa è un altra storia...
Ciao e al prossimo post!
Nessun commento:
Posta un commento