Tengo un código que se ve así y no estoy seguro de cómo manejar la parte que nunca se ejecutará, ya que una parte de este código se ejecuta en un bucle infinito mientras espera las conexiones y cuando termino el programa, solo sale de allí.
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; }Puede, y probablemente debería agregar un controlador de salida si su código va a ser utilizado por otras personas o si usted mismo simplemente lo quiere más limpio. En su controlador de salida, puede alternar una bandera que hace que el ciclo while() termine. El siguiente código funcionará 100% bien para este caso de uso y es confiable y multiplataforma, pero si desea hacer cosas más complicadas, debe usar las funciones específicas del sistema operativo seguras para subprocesos o algo como Boost o C ++ 11
Primero declare dos variables globales, hágalas volátiles para que el compilador siempre nos obligue a leer o escribir su valor de memoria real. Si no lo declaramos volátil, es posible que el compilador pueda poner su valor en un registro, lo que hará que esto no funcione. Con el conjunto volátil, leerá la ubicación de la memoria en cada ciclo y funcionará correctamente, incluso con múltiples subprocesos.
volatile bool bRunning=true; volatile bool bFinished=false; y en lugar de su ciclo while(1) {} , cámbielo a esto
while(bRunning) { dostuff } bFinished=true; En su controlador de salida, simplemente establezca bRunning=false;
void ExitHandler() { bRunning=false; while(bFinished==false) { Sleep(1); } }No especificó un sistema operativo pero parece que está basado en Linux, para configurar un controlador en Linux necesita esto.
void ExitHandler(int s) { bRunning=false; } int main() { struct sigaction sigIntHandler; sigIntHandler.sa_handler = ExitHandler; sigemptyset(&sigIntHandler.sa_mask); sigIntHandler.sa_flags = 0; sigaction(SIGINT, &sigIntHandler, NULL); while(bRunning) { dostuff } ...error_handling... }Y en Windows, cuando eres una aplicación de consola, es lo siguiente.
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... } Observe la necesidad de probar y esperar bFinished aquí. Si no hace esto en Windows, es posible que su aplicación no tenga tiempo suficiente para cerrarse, ya que un subproceso específico del sistema operativo llama al controlador de salida. En Linux, esto no es necesario y debe salir de su controlador para que continúe su hilo principal.
Otra cosa a tener en cuenta es que, de forma predeterminada, Windows solo le da ~ 5 segundos para apagar antes de que termine. Esto es desafortunado en muchos casos y si se necesita más tiempo, deberá cambiar la configuración del registro (mala idea) o implementar un servicio que tenga mejores conexiones con este tipo de cosas. Para su caso simple estará bien.
Para estas cosas, el sistema operativo se encargará de liberar adecuadamente los recursos al apagar. Sin embargo, de manera más general, aún debe asegurarse de que los recursos asignados no se acumulen durante la ejecución del programa, incluso si el sistema operativo los recupera automáticamente, ya que dicha fuga de recursos seguirá influyendo en el comportamiento y el rendimiento de su programa.
Ahora, con respecto a los recursos disponibles, no hay razón para no tratarlos como todos los recursos en C++. La regla aceptada es unirlos a un objeto que los liberará en su destructor, vea también el idioma RAII. De esa manera, incluso si en algún momento posterior alguien agregara una declaración de break , el código aún se comportaría correctamente.
Por cierto: el problema más grave que veo aquí es la falta de un manejo adecuado de errores en general.