2010-01-25 5 views
6

Je travaille sur une application qui contient plusieurs sockets serveur exécutés chacun dans un thread unique.
Un utilitaire externe (script) est appelé par l'un des threads. Ce script appelle un utilitaire (client) qui envoie un message à l'une des sockets du serveur. Initialement, j'utilisais system() pour exécuter ce script externe, mais nous ne pouvions pas l'utiliser car nous devions nous assurer que les sockets du serveur étaient fermés dans l'enfant qui était forké pour exécuter le script externe.
Je m'appelle maintenant fork() et execvp() moi-même. Je fork() puis dans l'enfant, je ferme toutes les sockets du serveur, puis appelez execvp() pour exécuter le script.) renvoie 0 dans l'application du serveur client C

Maintenant, tout cela fonctionne très bien. Le problème est que parfois le script signale des erreurs à l'application du serveur. Le script envoie ces erreurs en appelant une autre application (client) qui ouvre un socket TCP et envoie les données appropriées. Mon problème est que l'application cliente obtient une valeur de 0 renvoyée par l'appel système socket().

REMARQUE: cela se produit UNIQUEMENT lorsque l'application script/client est appelée à l'aide de ma fonction forkExec(). Si l'application script/client est appelée manuellement, l'appel socket() s'exécute de manière appropriée et tout fonctionne correctement. Sur la base de ces informations, je suppose que c'est quelque chose dans mon code fork() execvp() ci-dessous ... Des idées?

void forkExec() 
{  
    int stat; 

    stat = fork(); 
    if (stat < 0) 
    { 
     printf("Error forking child: %s", strerror(errno)); 
    } 
    else if (stat == 0) 
    { 
     char *progArgs[3]; 

     /* 
     * First, close the file descriptors that the child 
     * shouldn't keep open 
     */ 
     close(ServerFd); 
     close(XMLSocket); 
     close(ClientFd); 
     close(EventSocket); 
     close(monitorSocket); 

     /* build the arguments for script */ 
     progArgs[0] = calloc(1, strlen("/path_to_script")+1); 
     strcpy(progArgs[0], "/path_to_script"); 
     progArgs[1] = calloc(1, strlen(arg)+1); 
     strcpy(progArgs[1], arg); 
     progArgs[2] = NULL; /* Array of args must be NULL terminated for execvp() */ 

     /* launch the script */ 
     stat = execvp(progArgs[0], progArgs); 
     if (stat != 0) 
     { 
      printf("Error executing script: '%s' '%s' : %s", progArgs[0], progArgs[1], strerror(errno)); 
     } 
     free(progArgs[0]); 
     free(progArgs[1]); 
     exit(0); 
    } 

    return; 
} 

code de l'application client:

static int connectToServer(void) 
{ 
int socketFD = 0; 
int status; 
struct sockaddr_in address; 
struct hostent* hostAddr = gethostbyname("localhost"); 

socketFD = socket(PF_INET, SOCK_STREAM, 0); 

Les retours d'appel ci-dessus 0.

if (socketFD < 0) 
{ 
    fprintf(stderr, "%s-%d: Failed to create socket: %s", 
           __func__, __LINE__, strerror(errno)); 
    return (-1); 
} 

memset(&address, 0, sizeof(struct sockaddr)); 
address.sin_family = AF_INET; 
memcpy(&(address.sin_addr.s_addr), hostAddr->h_addr, hostAddr->h_length); 
address.sin_port = htons(POLLING_SERVER_PORT); 

status = connect(socketFD, (struct sockaddr *)&address, sizeof(address)); 
if (status < 0) 
{ 
    if (errno != ECONNREFUSED) 
    { 
     fprintf(stderr, "%s-%d: Failed to connect to server socket: %s", 
        __func__, __LINE__, strerror(errno)); 
    } 
    else 
    { 
     fprintf(stderr, "%s-%d: Server not yet available...%s", 
        __func__, __LINE__, strerror(errno)); 
     close(socketFD); 
     socketFD = 0; 
    } 
} 

return socketFD; 
} 

Pour votre information
OS: Linux
Arch: ARM32
Noyau: 2.6.26

+1

Pourriez-vous poster le code qui appelle socket() s'il vous plaît? – abc

+1

Aucune idée encore ce qui se passe à votre socket. Peut-être que c'est utile si je souligne que vous pouvez déclarer vos arguments beaucoup plus simple: const char * progArgs [] = {"/ path_to_script", arg, NULL}; - Pas besoin d'allouer et de copier, tout ce dont vous avez besoin est un tableau avec les bons pointeurs au moment où vous appelez execvp. – VoidPointer

Répondre

9
renvoie -1 en cas d'erreur

socket()

Un retour de 0 signifie socket() a réussi et que vous avez donné le descripteur de fichier 0. Je soupçonne que l'un des descripteurs de fichiers que vous fermez a descripteur de fichier. 0 et une fois fermé, l'appel suivant à une fonction qui a alloué un descripteur de fichier renverra fd 0 comme il est disponible

+0

Ouais, c'est ce que je soupçonnais. Mais non, je n'ai rien fait de tel. –

+0

Il renvoie 0 (comme l'indique votre question) ou renvoie-t-il une valeur négative (comme votre code d'échantillon le vérifie)? Pour réitérer, un retour de 0 est complètement légal si fd 0 est disponible. –

+0

Il retourne zéro. Je comprends que 0 est un fd valide. Le problème ici doit avoir quelque chose à voir avec ma fonction forkExec() parce que si je change pour utiliser la socket system() retourne> 0. Malheureusement, je ne peux pas utiliser l'appel system(). Le système –

1

Ne pas oublier un appel à

waitpid() 

Fin de la "mode question évidente". Je suppose un peu ici mais vous ne faites rien avec le pid retourné par l'appel fork(). (-:

+0

Oui, j'ai déjà un gestionnaire SIGCHLD enregistré qui effectue waitpid() pour nettoyer les zombies. Merci quand même :) –

4

Un socket avec la valeur 0 est bien, cela signifie que stdin a été fermé ce qui rendra fd 0 disponible pour la réutilisation -

Les chances sont l'un des fichiers de texte que vous fermez dans le chemin fils forkExec() (XMLSocket/ServerFd) e tc.) était fd 0. Cela démarrera l'enfant avec fd 0 closed, ce qui n'arrivera pas lorsque vous lancerez l'application à partir d'une ligne de commande, car fd 0 sera déjà ouvert en tant que stdin du shell.

Si vous voulez que votre prise de ne pas être 0,1 ou 2 (stdin/out/err) appeler ce qui suit dans votre fonction forkExec() après tout la fin() appelle

void reserve_tty() 
{ 
    int fd; 

    for(fd=0; fd < 3; fd++) 
    int nfd; 
    nfd = open("/dev/null", O_RDWR); 

    if(nfd<0) /* We're screwed. */ 
    continue; 

    if(nfd==fd) 
    continue; 

    dup2(nfd, fd); 
    if(nfd > 2) 
    close(nfd); 

} 

Vérifier prise renvoyant -1 ce qui signifie qu'une erreur est survenue.

+0

Mais POURQUOI stdin est-il fermé? Pourquoi voudrait-on le faire intentionnellement? –

+2

Il est courant (même si souvent ils sont redirigés vers/dev/null comme indiqué dans le code ci-dessus maintenant) et une bonne pratique pour fermer tous les fd n'est pas nécessaire dans un processus background/daemon, que je suppose le processus parent. – nos

0

Comme il est mentionné dans un autre commentaire, vous ne devriez pas fermer 0,1 ou 2 (stdin/out/err), vous pouvez mettre un contrôle pour vous assurer que vous ne les fermez pas et qu'il ne sera pas assigné comme nouveau fd`s lorsque vous demandez une nouvelle socket

Questions connexes