2 votes

C - Comment limiter le nombre de connexions entrantes dans un serveur lors de l'utilisation de select() ?

Je suis encore novice en matière de programmation en socket C mais j'ai pu créer quelques programmes clients et serveurs simples. Je programme un serveur qui écoute les connexions TCP, son rôle est de répondre aux demandes des clients et de fermer la communication lorsque le client envoie une séquence spéciale d'octets (ou lorsqu'il se déconnecte, bien sûr).

J'ai commencé à coder le serveur en utilisant le accept() à l'intérieur d'une boucle sans fin : le serveur attend un client, l'accepte(), fait tout ce qu'il faut, ferme() le descripteur de socket à la fin et recommence en attendant d'accepter un nouveau client.

Comme je veux servir un seul client à la fois, j'ai appelé la fonction listen de cette façon : listen(mysocket, 1);

Tout fonctionnait plutôt bien, mais un nouveau problème est apparu. La partie serveur expliquée ci-dessus fonctionne dans un thread séparé (appelons-le thread #2) et le thread principal (thread #1) doit pouvoir lui dire de se terminer. J'ai donc créé une variable globale, si cette variable est mise à 1 (par le thread #1) le thread #2 doit se terminer. Le problème est que le thread #2 reste bloqué lorsque la variable globale est activée. accept() est appelée, elle ne peut donc pas vérifier périodiquement la variable globale.

J'avais clairement besoin d'une valeur de temporisation pour cette fonction : "s'il n'y a pas de connexion à accepter, vérifiez la valeur de la variable globale, continuez à attendre une nouvelle connexion si elle vaut 0 ou terminez si elle vaut 1".

J'ai alors cherché une solution sur Google et j'ai trouvé que la select() La fonction fait la chose dont j'ai besoin. C'est un peu différent cependant, j'ai découvert pour la première fois la fonction fd_set et toutes les macros FD_*. J'ai modifié la partie serveur pour qu'elle fonctionne avec la fonction select() et tout fonctionne très bien, mais voici le dernier problème, celui que je ne parviens pas à résoudre. Si on appelle la fonction listen de cette façon : listen(socket, 1); mais le serveur accepte et sert toujours plusieurs connexions en même temps. Cela dépend-il du fait que select() fonctionne avec les fd_set ? J'utilise quelques exemples que j'ai trouvés sur le web et, lorsqu'une connexion est acceptée, elle crée un nouveau descripteur de socket qui va dans l'ensemble avec tous les autres. J'aimerais accepter la connexion d'un seul client, j'ai écrit un code simple qui reconnaît si le client qui se connecte doit être servi ou non, mais y a-t-il un moyen de le déconnecter côté serveur ? Je sais que je dois utiliser la fonction close() pour fermer un descripteur de socket, mais lorsque vous utilisez la fonction select() Je travaille avec des fd_set et je ne sais pas vraiment quoi faire pour les fermer. Ou bien, existe-t-il un moyen de limiter le nombre de descripteurs de socket dans un ensemble ? J'ai trouvé la macro FD_SETSIZE, mais je n'ai pas réussi à la faire fonctionner et je ne suis même pas sûr qu'elle règle le problème.

Merci pour votre temps !

5voto

Greg Hewgill Points 356191

El listen() a une fonction backlog qui détermine le nombre de requêtes entrantes qui peuvent être en attente avant qu'elles ne soient traitées. puede être refusés. Cette disposition est formulée avec soin afin que l'implémentation du système d'exploitation puisse prendre en charge les éléments suivants plus que ce que vous spécifiez dans le listen() appel. Il se peut que vous ne soyez pas en mesure de contrôler le nombre exact de connexions en attente.

Si vous ne devez prendre en charge qu'un seul client à la fois, acceptez une deuxième connexion mais indiquez au nouveau client que la connexion n'est pas disponible pour le moment, puis fermez la nouvelle connexion. Cette méthode présente également l'avantage de vous permettre d'expliquer au client pourquoi la connexion n'est pas disponible.

0voto

wildplasser Points 17900

Vous pouvez aussi mettre la socket listen() dans le fd_set. (D'après votre question, je ne comprends pas si vous le faites déjà) Si le select indique que la socket listen est lisible, vous pouvez appeler accept sur la socket listen. accept retourne exactement un fd. Vous pouvez aussi mettre le fd retourné dans le fd_set. Les threads sont plus ou moins orthogonaux à ceci. (les appels système sont atomiques), mais vous pouvez bien sûr faire des erreurs.

Le paramètre backlog pour listen est plus ou moins sans rapport ; il spécifie simplement combien de embryonnaire que le système doit maintenir simultanément (l'établissement de la connexion utilise des ressources, même avant que le socket ne devienne utilisable, comme indiqué par listen qui renvoie le nouveau fd). embryonnaire les connexions sont nées comme le nouveau socket fd.

La macro getdtablesize() est un vestige des temps anciens. Dans les implémentations modernes, elle est basée sur getrlimit().

Le problème sous-jacent de FD_SETSIZE est que fd_set doit être une valeur lval, de sorte qu'elle puisse être assignée. Il doit donc être de taille fixe. (c'est probablement un tableau à l'intérieur d'une structure) Pour éviter les structures de taille fixe, vous pourriez utiliser poll() au lieu de select() (ou utiliser plusieurs fd_sets plus petits ;-)

-1voto

Benjamin Weiss Points 1086

Si vous utilisez l'API Windows, vous pouvez essayer les sections critiques.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X