2011-08-19 2 views
3

Cette question est similaire à Proper way to close a blocking UDP socket. J'ai un thread en C qui lit à partir d'un socket UDP. La lecture est bloquante. Je voudrais savoir s'il est possible de quitter le thread, sans se fier au retour de recv()? Par exemple puis-je fermer le socket à partir d'un autre thread et attendre en toute sécurité le thread de lecture de socket pour quitter? N'a pas vu de réponse haute votée sur ce fil, c'est pourquoi je le demande à nouveau.Essayer de quitter une socket UDP bloquante lire

Répondre

4

Cela dépend vraiment du système sous lequel vous travaillez. Par exemple, si vous utilisez un système compatible POSIX et que votre thread est annulable, l'appel recv() sera interrompu lorsque vous annulez le thread car il s'agit d'un point d'annulation. Si vous utilisez une implémentation de socket plus ancienne, vous pouvez définir un gestionnaire de signal pour votre thread pour quelque chose comme SIGUSR1 et espérer que personne d'autre ne le veut et ne le signale, car recv() va interrompre un signal. Votre meilleure option est de ne pas bloquer, si possible.

+0

L'annulation semble être une idée prometteuse. Y a-t-il de la documentation disponible sur les points d'annulation pour un thread? J'ai vu [link] (http://www.mkssoftware.com/docs/man3/pthread_cancel.3.asp) Il ne liste pas les appels liés à la socket. Ou 'recv()' utilise-t-il un autre appel comme 'read()' par exemple – jogabonito

+0

@jogabonito Si votre système est compatible Posix et utilise des sockets compatibles Posix, ce sera le cas. [posix cancel points] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_05_02) –

3

Je ne pense pas que la fermeture d'une socket impliquée dans une opération de blocage soit un moyen sûr de mettre fin à l'opération. Par exemple, kernel.org met en garde contre sombrement:

Il est probablement imprudent de fermer les descripteurs de fichiers alors qu'ils peuvent être utilisés par appels système dans d'autres threads du même processus. Comme un descripteur de fichier peut être réutilisé, certaines conditions de course obscures peuvent provoquer des effets secondaires inattendus.

  • Au lieu de cela, vous pouvez utiliser un signal et faire échouer avec recvEINTR (assurez-vous SA_RESTART n'est pas activé). Vous pouvez envoyer un signal à un fil spécifique avec pthread_kill

  • Vous pouvez activer SO_RCVTIMEO sur la prise avant de commencer le recv appel

Personnellement, je l'habitude d'essayer de rester à l'écart de tout le signal méchancetés, mais c'est une option viable.

+0

+1 pour l'avertissement kernel.org .. – jogabonito

2

Vous avez quelques options pour cela. Un signal interrompt l'opération de lecture, donc tout ce que vous devez faire est de vous assurer qu'un signal se déclenche. L'opération recv doit échouer avec le numéro d'erreur EINTR.

L'option la plus simple consiste à configurer une minuterie pour interrompre votre propre processus après un certain délai, par ex. 30 secondes:

itimerval timer 
timeval time; 
time.tv_sec = 30; 
time.tv_usec = 0; 
timer.it_value = time; 
if(setitimer(ITIMER_REAL, &timer, NULL) != 0) 
    printf("failed to start timer\n"); 

Vous obtiendrez un SIGALRM après l'heure indiquée, ce qui interrompt votre opération de blocage, et vous donner la chance de répéter l'opération ou de quitter.

2

Vous ne pouvez pas libérer une ressource partagée alors qu'un autre thread l'utilise ou pourrait l'utiliser. En pratique, vous constaterez que vous ne pouvez même pas écrire du code pour faire ce que vous suggérez.

Pensez-y. Quand vous allez appeler close, comment pouvez-vous savoir que l'autre thread est effectivement bloqué dans recv? Que se passe-t-il si vous êtes sur le point d'appeler recv, mais qu'un autre thread appelle socket et obtient le descripteur que vous venez de fermer?Maintenant, non seulement ce thread ne détectera aucune erreur, mais il appellera recv sur le socket incorrect!

Il existe probablement un bon moyen de résoudre votre problème externe, la raison pour laquelle vous devez quitter une lecture de socket UDP bloquante. Il y a aussi plusieurs hacks laids disponibles. L'approche de base consiste à rendre le socket non bloquant et, au lieu de lire un socket UDP bloquant, à fausser un blocage avec select ou poll. Vous pouvez ensuite abandonner cette boucle de plusieurs façons:

Une façon est d'avoir select expirer et de vérifier un indicateur 'abort' lorsque select renvoie.

Une autre façon est également select sur l'extrémité de lecture d'un tuyau. Envoyer un seul octet à la conduite pour abandonner le select.

1

Si le système posix de complient, vous pouvez essayer de suivre votre fil: pthread_create avec une fonction qui rend votre recv et pthread_cond_signal juste après, puis retourne.
L'unité d'exécution appelle pthread_cond_timedwait avec le délai d'attente souhaité et termine l'unité d'exécution appelée si elle est définie sur timed_out.