2016-02-22 25 views
12

Ho un codice simile a questo e non so come gestire la parte che non verrà mai eseguita poiché una parte di questo codice viene eseguita in loop infinito durante l'attesa delle connessioni e quando terminare il programma, esce solo da lì.come gestire il codice che non viene mai eseguito

main(){ 

// do some stuff.... 

    while(1) { 
     int newFD = 
      accept(sockFD, (struct sockaddr *)&client_addr, &client_addr_size); 
     if(newFD == -1) { 
      std::cerr << "Error while Accepting on socket" << std::endl; 
      continue; 
     } 

     if(!fork()) { 

      close(sockFD); // close child's sockfd - not needed here 

      // lalala do stuff send message here     

      close(newFD); // finally close its newFD - message sent, no use 
      return 0; 
     } 
     close(newFD); // close parent's newFD - no use here 
    } 

    // now execution never reaches here 
    close(sockFD);  // so how to handle this? 
    freeaddrinfo(res); // and this? 

    return 0; 
} 
+0

L'ho lasciato così com'è e suppongo che OS debba occuparsene? – buggy3

+5

In questo caso, desidero collegare il segnale di terminazione e utilizzarlo per uscire in modo pulito dal programma. In generale, tuttavia, per il codice che non viene mai eseguito, eliminarlo. Lascia che il controllo della tua versione tenga traccia del vecchio codice morto. – Niall

+1

La tua domanda su come gestire la chiusura del socket o cosa fare con il codice stesso? – rhughes

risposta

15

È possibile, e probabilmente dovrebbe aggiungere un gestore di uscita se il codice deve essere utilizzato da altre persone o ti vogliono solo più pulito. Nel tuo gestore di uscita puoi attivare un flag che interrompe il ciclo while(). Il seguente codice funzionerà al 100% per questo caso d'uso ed è affidabile e multipiattaforma, ma se vuoi fare cose più complicate dovresti usare le funzioni specifiche di thread safe specifiche o qualcosa come Boost o C++ 11

Prima dichiarare due variabili globali, renderle volatili in modo che il compilatore ci costringerà sempre a leggere o scrivere il suo reale valore di memoria. Se non lo dichiariamo volatile, allora è possibile che il compilatore possa mettere il suo valore in un registro che lo renderà non funzionante. Con set volatile legge la posizione di memoria su ogni loop e funziona correttamente, anche con più thread.

volatile bool bRunning=true; 
volatile bool bFinished=false; 

e al posto del tuo while(1) {} ciclo, modificarlo a questo

while(bRunning) 
{ 
    dostuff 
} 
bFinished=true; 

Nel vostro gestore di uscita semplicemente impostare bRunning=false;

void ExitHandler() 
{ 
    bRunning=false; 
    while(bFinished==false) { Sleep(1); } 
} 

Lei non ha specificato un sistema operativo ma sembra come se fossi basato su Linux, per impostare un gestore su Linux hai bisogno di questo.

E su Windows quando si è un'app per console è la seguente.

BOOL WINAPI ConsoleHandler(DWORD CEvent) 
{ 
    switch (CEvent) 
    { 
     case CTRL_C_EVENT: 
     case CTRL_BREAK_EVENT: 
     case CTRL_CLOSE_EVENT: 
     case CTRL_LOGOFF_EVENT: 
     case CTRL_SHUTDOWN_EVENT: 
      bRunning = false; 
      while (bFinished == false) Sleep(1); 
      break; 
    } 
    return TRUE; 
} 

int main() 
{ 
    SetConsoleCtrlHandler(ConsoleHandler, TRUE); 
    while(bRunning() 
    { 
     dostuff 
    } 
    ...error_handling... 
} 

Avviso la necessità di testare e attendere bFinished qui. Se non lo fai su Windows, l'app potrebbe non avere il tempo di spegnersi in quanto il gestore di uscita viene chiamato da un thread specifico del sistema operativo separato. Su Linux questo non è necessario ed è necessario uscire dal gestore per continuare il thread principale.

Un'altra cosa da tenere in considerazione è che, per impostazione predefinita, Windows ti consente di spegnere ~ 5 secondi prima che termini. Ciò è sfortunato in molti casi e, se è necessario più tempo, è necessario modificare le impostazioni del registro (cattiva idea) o implementare un servizio che si adatti meglio a tali cose. Per il tuo caso semplice andrà bene.

+0

'fork()' in MS Windows? Io non la penso così ;) –

+4

Hehe. Ho appena aggiunto il codice di Windows perché è pertinente per gli altri che possono cercare la stessa risposta, ma su Windows. Spero sia utile! –

+4

Non '(PHANDLER_ROUTINE) ConsoleHandler', è solo per chiedere guai. Non dovresti avere bisogno di un cast. Inoltre, usare volatile e sperare per il meglio non è neanche buono. Piuttosto, usa la famiglia di funzioni 'Interlocked *'. Infine, non è il gestore C di controllo eseguito nel contesto di uno dei thread esistenti? Sarebbe fatale se tu aspettassi esattamente quel thread per terminare quindi ... –

10

Per queste cose, il sistema operativo si occuperà di rilasciare correttamente le risorse all'arresto. Tuttavia, più in generale, è ancora necessario assicurarsi che le risorse allocate non si accumulino durante l'esecuzione del programma, anche se vengono automaticamente recuperate dal sistema operativo, poiché tale perdita di risorse influenzerà ancora il comportamento e le prestazioni del programma.

Ora, per quanto riguarda le risorse disponibili, non c'è motivo di non trattarle come tutte le risorse in C++. La regola accettata è quella di associarli a un oggetto che li rilascerà nel loro distruttore, vedere anche l'idioma RAII. In questo modo, anche se in un secondo momento qualcuno ha aggiunto una dichiarazione break, il codice si comporterebbe comunque correttamente.

BTW: Il problema più serio che vedo qui è la mancanza di una corretta gestione degli errori in generale.

+3

potresti commentare la parte 'gestione errori'? Mi piacerebbe migliorare il mio codice in base ai tuoi suggerimenti poiché non ho mai fatto questo tipo di lavoro prima. –

+1

Esempio semplice: 'fork()'. Se guardi la pagina di manuale, ci sono tre diversi gruppi di valori che restituisce, ma tu fai solo una distinzione tra due di essi. Se 'fork()' fallisce, vorrei semplicemente 'lanciare std :: runtime_error (" fork() failed ")' come concetto di gestione degli errori di default, quindi l'errore non passa inosservato. Questo vale anche per altre funzioni qui, basta leggere la documentazione corrispondente per scoprire come segnalano gli errori. Puoi anche pubblicare il tuo codice per la revisione su codereview.stackexchange.com per ottenere ulteriori suggerimenti. –

+0

@UlrichEckhardt fa buoni punti e raccomando il RAII per la gestione degli errori il più possibile. Si consiglia di guardare in ASIO per il networking invece delle funzioni raw del socket, poiché entrerà nello standard C++ 17 e permetterà alla tua app di essere multipiattaforma senza alcuno sforzo da parte tua. I socket di Windows e i socket di Linux hanno alcune differenze che rendono il porting più difficile di quanto dovrebbe essere! –