J'ai une application de discussion qui a un thread séparé pour écouter des messages entrants. Ce mode de travail a un gros problème. La plupart du temps, la boucle bloquera sur recv () code> afin que le fil du récepteur ne quitte pas. Quel serait un moyen adéquat de faire face à ce problème sans forcer la résiliation du fil après quelques secondes? P> P>
4 Réponses :
Si vous fermez la prise dans un autre fil, RECV () quittera. P>
Est-ce que le thread-coffre-fort d'appeler fermeture () code> sur une prise lorsqu'un thread séparé bloque sur un
RECV () code> pour la même prise?
Réponse courte: oui. Réponse longue: Généralement oui, mais la sécurité du fil dépend de la pile du système d'exploitation et de socket. Certaines opérations peuvent causer des problèmes, mais vous ne voudrez généralement pas les faire de toute façon (lit parallèle sur la même prise par exemple)
appeler Fermer code> sur la prise de tout autre thread fera l'échec de l'appel
recv code> instantanément. p>
Oui sous Windows, pas sous Linux.
Fermez la prise avec Ceci imprime "RECV renvoyé 0" sur mon système, indiquant que le récepteur a vu un arrêt ordonné. Commentaire sur terme à long terme, OP doit corriger la conception, soit en utilisant Arrêt () Code> Pour le fermer pour tous les récepteurs.
Shutdown () Code> Et regardez-le pendre pour toujours. p>
Sélectionnez CODE> ou comprenant un message explicite d'arrêt dans le protocole. P>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
/* Free on my system. YMMV */
int port = 7777;
int cd;
void *f(void *arg)
{
/* Hack: proper code would synchronize here */
sleep(1);
/* This works: */
shutdown(cd, SHUT_RDWR);
close(cd);
return 0;
}
int main(void)
{
/* Create a fake server which sends nothing */
int sd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sa = { 0 };
const int on = 1;
char buf;
pthread_t thread;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(port);
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
/* Other error reporting omitted for clarity */
if (bind(sd, (const struct sockaddr*)&sa, sizeof sa) < 0) {
perror("bind");
return EXIT_FAILURE;
}
/* Create a client */
listen(sd, 1);
cd = socket(AF_INET, SOCK_STREAM, 0);
connect(cd, (const struct sockaddr*)&sa, sizeof sa);
accept(sd, 0, 0);
/* Try to close socket on another thread */
pthread_create(&thread, 0, f, 0);
printf("recv returned %d\n", recv(cd, &buf, 1, 0));
pthread_join(thread, 0);
return 0;
}
Est-ce différent de faire fermer (socketfd) code>?
@Pieter - Oui. Fermer () Ne pas interrompre un fil bloqué dans une lecture. L'arrêt () ferme pour d'autres lecteurs, même dans d'autres processus.
@EJP - voir exemple code. RECV retourne 0 indiquant la fermeture ordonnée.
Re: y compris un message explicite de cesser. Je ne peux pas, car je mettant en œuvre le sous-protocole DCC déjà existant. Suis-je correct pour supposer que, bien que l'approche Shutdown () Code> répondrait à ma question de manière plus littérale, je devrais utiliser
SELECT () CODE> avec un certain délai d'attente si je veux Pour le faire correctement?
Eh bien, «le faire correctement» est une question de conception, ce qui n'est vraiment qu'une autre façon de dire que c'est une question d'opinion. Certes, l'arrêt () sur l'extrémité du lecteur résoudra votre problème et peut être acceptable avec un commentaire. Il se sent un peu sale car moralement, vous n'auriez pas dû s'engager à bloquer pour toujours sur une lecture sans savoir qu'elle serait terminée. Alors quelles sont les alternatives? Si toutes les prises sont transportées sur / depuis l'interface graphique, je les resserrais tous sur le thread principal dans une seule boucle de sélection qui inclut le (s) socket (s) d'écoute).
En supposant que vous êtes attaché à un fil de lecteur par connexion, vous avez encore des choix. DRRAVES SUGGESTION DE CALLATION SELECT () sur le socket unique pour tester la lisibilité fonctionne. Je n'ai pas moi-même utilisé So_Recvtimeo mentionné dans son commentaire ci-dessous, mais cela semble prometteur. Chacun de ceux-ci a l'inconvénient d'attendre jusqu'à ce qu'un délai d'expiration expire avant de sonder le drapeau de cesser. Cela peut ne pas comporter votre candidature.
Si vous pouvez compter sur le comportement des pairs correctement, vous pourriez envisager d'arrêter (shut_wr) - c'est-à-dire l'arrêt de l'écrivain. Le pair devrait lire EOF et fermer la fin de la prise. Ensuite, votre RECV () détectera à son tour EOF et retournera 0. Cela semble une utilisation moralement moins répréhensible de l'arrêt ().
Je vais aller avec la solution shut_wr. Merci. Que voulez-vous dire avec "pair comportement correctement"? Vous voulez dire si le pair utilise un logiciel qui ne ferme pas la fin de la prise lorsque RECV () code> retourne
0 code>?
Vous pouvez utiliser SELECT () pour attendre les données entrantes et éviter le blocage dans RECV (). SELECT () va également bloquer, mais vous pouvez l'avoir du temps après un intervalle de réglage de sorte que la boucle tandis que la boucle de temps puisse continuer et vérifier les signaux de quitter le fil principal:
while (main thread not calling for receiver to quit) { if (tcpCon.hasData(500)) { // Relies on select() to determine that data is // available; times out after 500 milliseconds string message = tcpCon.tcpReceive(); // Relies on the recv() function processIncomingMessage(message); } }
Plus simplement, vous pouvez utiliser un délai de lecture pour obtenir le même effet.
@EJP: Comment feriez-vous cela sans quelque chose comme SELECT () CODE>?
@Void: Il se réfère à l'option SO_RecVTimeo qui peut être définie avec Setsockopt (). Il affectera un délai d'attente à RECV () qui retournera -1 si la période de délai d'expiration s'élève et aucune donnée n'a été reçue, avec Erno Set to Egain.
@dgraves: ah, bien sûr! Cela fait longtemps que j'ai vu ça. Bon appel EJP. Merci d'avoir refusé cela, Dgraves.
Notez que toutes les plates-formes ne prennent pas en charge so_rcvtimeo code>.
voté pour rouvrir cela parce que le duplicata lié concerne Winsock (Sockets Windows) et les réponses ici concerne les sockets sous Linux