J'utilise actuellement Sélectionner une boucle pour gérer les sockets dans un proxy. L'une des exigences de ce proxy est que si le proxy envoie un message au serveur extérieur et ne reçoit pas de réponse dans un certain temps, le proxy doit fermer cette prise et essayer de se connecter à un serveur secondaire. La fermeture se produit dans un fil séparé, tandis que les blocs de fil de sélection attendent l'activité. P>
J'ai des difficultés à comprendre comment détecter que ce socket est fermé spécifiquement, de sorte que je puisse gérer l'échec. Si j'appelle proximité () dans l'autre thread, je reçois un eBadf, mais je ne peux pas dire quelle prise fermée. J'ai essayé de détecter la prise via l'exception FDSet, pensant que cela contiendrait la prise fermée, mais je ne reçois rien de retour là-bas. J'ai également entendu appeler l'arrêt de l'appel () enverra une nageoire sur le serveur et recevra une rame à dos afin que je puisse le fermer; Mais tout le point est que j'essaie de fermer cela à la suite de ne pas avoir de réponse dans la période de délai d'attente, je ne peux donc pas le faire, non plus. P>
Si mes hypothèses ici sont fausses, faites le moi savoir. Toutes les idées seraient appréciées. P>
EDIT: En réponse aux suggestions sur l'utilisation de la time SELECT: Je dois faire la fermeture de manière asynchrone, car le client se connecter au proxy sera tiré sur et je ne peux pas attendre pour que la sélection soit interrogée. Cela ne fonctionnerait que si j'ai fait de l'heure de sélection très petite, ce qui interrogerait constamment et gaspillerait des ressources que je ne veux pas. P>
7 Réponses :
Utilisez un délai d'attente pour la sélection et si les séquences de lecture en lecture-prêt / prête à l'écriture / écriture / erreurs sont toutes vides (w.r.t cette prise), vérifiez si c'était fermé. P>
Il est difficile de commenter lorsque vous ne voyez qu'une petite partie de l'éléphant, mais peut-être que vous êtes sur des choses compliquées? P>
Vous avez probablement une certaine structure pour garder une trace de chaque prise et ses informations (comme le temps laissé pour recevoir une réponse). Vous pouvez modifier la boucle SELECT () pour utiliser un délai d'attente. En ce sens, vérifiez s'il est temps de fermer la prise. Faites ce que vous devez faire pour la fermeture et ne pas l'ajouter à la FD définit la prochaine fois. P>
Généralement, je marque simplement la prise de fermeture de l'autre thread, puis lorsque SELECT () retourne de l'activité ou du délai d'attente, j'exécute une passe de nettoyage et fermez toutes les connexions mortes et mettez à jour le FD_SET. Le fait de tout autre moyen ouvre des conditions de course dans lesquelles vous avez abandonné la connexion, tout comme sélectionnez () enfin reconnu des données pour cela, vous le fermez, mais l'autre thread tente de traiter les données détectées et obtient bouleversé de trouver la connexion fermée. P>
OH, et le sondage () est généralement meilleur que SELECT () en termes de ne pas avoir à copier autant de données autour. P>
Convenu-- Effectuez la fermeture () dans le fil SELECT (). Si vous devez avoir un autre thread de détecter le délai d'attente, envoyez le fichier SELECT () un message via un tuyau (que vous pouvez placer dans le jeu de sélection) au lieu d'appeler fermer () directement.
Comment faire face à eBadf sur SELECT ():
int fopts = 0; for (int i = 0; i < num_clients; ++i) { if (fcntl(client[i].fd, F_GETFL, &fopts) < 0) { // call close(), FD_CLR(), and remove i'th element from client list } }
Cette méthode comprend une condition de course. Une prise peut être fermée et sa FD soit réutilisée d'une manière différente (par exemple, la connexion du serveur). Le code peut toujours le traiter comme s'il s'agissait d'une connexion cliente.
Si vous utilisez le sondage (2) comme suggéré dans d'autres réponses, vous pouvez utiliser le statut Pollnval, essentiellement EBADF, mais sur une base de descripteur par fichier, et non sur l'intégralité de l'appel du système tel qu'il est pour sélectionner ( 2). P>
Vous ne pouvez pas libérer de ressource dans un fil pendant qu'un autre fil est ou peut l'utiliser. Appelant Il y a deux bonnes solutions à votre problème: p>
a le fil qui appelle a le fil qui détecte l'appel de délai d'attente Fermer code> sur une prise pouvant être utilisée dans un autre thread ne fonctionnera jamais correctement. Il y aura toujours des conditions de course potentiellement désastreuses. P>
SELECT CODE> Utilisez toujours un délai d'attente sans plus grand que le plus long que vous êtes prêt à attendre de traiter un délai d'attente. Lorsqu'un délai d'attente survient, indiquez que certains placez le fil qui appelle
SELECT CODE> remarquera quand il retourne de
Sélectionnez CODE>. Demandez à ce thread le fichier
fermer code> de la prise entre les appels entre
Sélectionnez CODE>. P> LI>
shutdown code> sur la prise. Cela provoquera
SELECT CODE> pour revenir, puis avez ce thread faire le
Fermer code>. P> li>
ol>
Il suffit d'exécuter un "test de test" sur chaque socket qui aurait pu être fermé avec un délai d'attente zéro et vérifiez le résultat de sélection et errno jusqu'à ce que vous ayez trouvé celui qui a été fermé.
La pièce de démonstration suivante commence deux serveur Sockets sur des threads séparés et crée deux sockets clients pour se connecter à l'une ou l'autre des socket de serveur. Ensuite, il commence un autre fil, qui tuera au hasard une des prises de cliente après 10 secondes (il le fermera simplement). La fermeture de l'une ou l'autre des socket client provoque l'échec de l'erreur avec une erreur dans le fil principal et le code ci-dessous testera maintenant lequel des deux sockets a réellement fermé. P> Sortie d'échantillon pour l'exécution de cette option. Code de test deux fois: p> Toutefois, si votre système prend en charge sondage () code>, je vous conseillerais vivement de envisager d'utiliser cette API au lieu de
Sélectionnez () CODE>. Sélectionner est une API plutôt laid et lagacité du passé, laissée là-bas que pour une compatibilité rédaction avec le code existant. Le sondage a une bien meilleure interface pour cette tâche et a un drapeau supplémentaire pour vous indiquer directement qu'une prise est fermée localement:
Pollnval code> sera défini sur
SUVANT code> si cette prise a Été fermé, quels que soient les indicateurs que vous avez demandés sur des événements, car
pollnval code> est une sortie uniquement des indicateurs, cela signifie qu'il est ignoré lors de la définition des événements code>. Si la prise n'était pas fermée localement mais que le serveur distant vient de fermer la connexion, le drapeau
Pollhup code> sera défini dans
. "/ Code> (aussi un drapeau de sortie uniquement). Un autre avantage du sondage est que le délai d'attente est simplement une valeur INT (millisecondes, suffisamment grainé pour les prises de réseau réel) et qu'il n'y a pas de limitations au nombre de sockets pouvant être surveillés ou à sa plage de valeurs numériques. P> < / p>
Cela ne fonctionnera pas. Il n'y a aucun moyen de garantir que la prise sera toujours fermée. Par exemple, un autre thread peut appeler socket code> après la fermeture code> mais avant d'appeler
sélectionner code> et obtenir le même descripteur.
@Davidschwartz "Par exemple, un autre thread peut appeler la prise ...", alors ne faites pas un autre fil le faire. C'est votre application, vous contrôlez qui peut créer des sockets, où et quand.
La raison pour laquelle j'ai écrit "par exemple" est parce que c'est juste une façon dont cela peut aller mal. Il y a d'autres façons. Par exemple, une nouvelle version d'une bibliothèque que vous utilisez peut maintenant créer un fil d'arrière-plan qui appelle socket code> et soudainement votre code casse lorsque quelqu'un mette à niveau cette bibliothèque.
Voir aussi Stackoverflow.com/questions/543541/...
Dupliqué possible de Sortir de Socket Select