Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Pb détection cloture socket en mode non bloquant

16 réponses
Avatar
Sébastien Cottalorda
Salut à tous,

Mon système:
Mandrake 10.1 (kernel 2.6.8.1-12 - Perl 5.8.5-3)

J'utilise un programme qui se connecte, en mode non bloquant, à
plusieurs serveurs TCP.

Voici comment il marche:

#=================================================================
# Programme multi-clients
#
&ouverture_de_toutes_les_sockets_en_mode_non_bloquant();

while (1){
foreach ($select->can_read()){
# recevoir les données des sockets m'ayant envoyé
# quelque chose
}
foreach ($select->can_write()){
# envoyer les données des buffers dans les sockets
# Si problème d'envoi, alors cloturer la socket
# en question
}
foreach ($select->has_exception()){
# NOP
}
}
#==================================================================


Le problème est que à chaque fois qu'une socket meurt (coupée par
l'autre partie), impossible de la détecter.
Je considère donc qu'elle est toujours valide.

Comment puis-je détecter une cloture de socket en mode non bloquant.

Merci d'avance.

Sébastien

10 réponses

1 2
Avatar
Paul Gaborit
À (at) Mon, 25 Jul 2005 16:04:46 +0200,
Sébastien Cottalorda écrivait (wrote):
Comment puis-je détecter une cloture de socket en mode non bloquant.


En général, on détecte la fermeture d'une connexion car elle déclenche
en disant qu'il y a quelque chose à lire et lorsqu'on tente une
lecture, on lit 0 octet.

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/&gt;
Perl en français - <http://perl.enstimac.fr/&gt;

Avatar
Patrick Mevzek
J'utilise un programme qui se connecte, en mode non bloquant, à
plusieurs serveurs TCP.


Cela ne répond pas directement à la question, mais regardez du côté de
IO::Multiplex, voire de POE pour ce genre de choses.
Cela encapsule les petits détails, et cela permet relativement simplement
de gérer de nombreux flux simultanément, tant qu'on ne fait pas trop de
choses bloquantes par ailleurs.

--
Patrick Mevzek . . . . . . Dot and Co (Paris, France)
<http://www.dotandco.net/&gt; <http://www.dotandco.com/&gt;
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>

Avatar
Sébastien Cottalorda
À (at) Mon, 25 Jul 2005 16:04:46 +0200,
Sébastien Cottalorda écrivait (wrote):

Comment puis-je détecter une cloture de socket en mode non bloquant.



En général, on détecte la fermeture d'une connexion car elle déclenche
en disant qu'il y a quelque chose à lire et lorsqu'on tente une
lecture, on lit 0 octet.

Ca y est,


Désolé, c'est le problème avec les copier-coller sans réfléchir.
Je le détectais bien, mais je n'avais pas affiché un log explicite
disant que le client venait de se déconnecter.

Autant pour moi.

Merci encore.

Sébastien


Avatar
Nicolas George
Sébastien Cottalorda wrote in message
<42e4f177$0$6740$:
while (1){
foreach ($select->can_read()){
# recevoir les données des sockets m'ayant envoyé
# quelque chose
}
foreach ($select->can_write()){
# envoyer les données des buffers dans les sockets
# Si problème d'envoi, alors cloturer la socket
# en question
}
foreach ($select->has_exception()){
# NOP
}
}


Ça ne répond pas à la question (de toutes façons c'est déjà fait), mais
j'aimerais savoir ce qu'est ce $select, pour me soulager l'esprit : ce genre
de boucle ressemble furieusement à du busy-wait.

Avatar
Sébastien Cottalorda
Sébastien Cottalorda wrote in message
<42e4f177$0$6740$:

while (1){
foreach ($select->can_read()){
# recevoir les données des sockets m'ayant envoyé
# quelque chose
}
foreach ($select->can_write()){
# envoyer les données des buffers dans les sockets
# Si problème d'envoi, alors cloturer la socket
# en question
}
foreach ($select->has_exception()){
# NOP
}
}



Ça ne répond pas à la question (de toutes façons c'est déjà fait), mais
j'aimerais savoir ce qu'est ce $select, pour me soulager l'esprit : ce genre
de boucle ressemble furieusement à du busy-wait.
Salut,


$select est l'objet que j'utilise avec le module IO::Select comme ceci.

création de l'objet
$select = IO::Select->new();

puis, pour chaque socket que je veux gérer avec cet objet, je fais:
$client = IO::Socket->new(...);
$select->add($client);

si une socket se déconnecte, je l'enlève de l'objet $select:
$select->remove($client);

En mode BLOQUANT, je peux attendre jusqu'à ce qu'une des sockets ait
quelque chose à me dire (jusqu'à $timeoutreceiving secondes, si la
socket n'est pas prête à répondre, on passe à la suivante)
foreach ($select->can_read($timeoutreceiving)){
# recevoir les données des sockets m'ayant envoyé
# quelque chose
}
dans cet exemple, dans $_, va se trouver successivement tous les handles
de socket m'ayant envoyé quelque chose. Je n'ai plus qu'à traiter les
réceptions.

Toujours en mode BLOQUANT, je peux ensuite gérer les émissions (chaque
handle est vérifié avec timeoutsending sec de timeout, au delà, on passe
au suivant)
foreach ($select->can_write($timeoutsending)){
# envoyer les données des buffers dans les sockets
# Si problème d'envoi, alors cloturer la socket
# en question
}
dans cet exemple, dans $_, va se trouver successivement tous les handles
de socket vers lesquels je peux envoyer des données (à priori : tous).


Dans le cas précis que j'évoquais, je suis en mode non bloquant, cela
signifie que si aucune socket n'a quelque chose à me dire, et si je n'ai
rien à envoyer, j'entre alors dans une boucle while(1) très rapide.
Il ne semble pas, toutefois, que mon CPU monte à 100% pour autant.

Sébastien


Avatar
Paul Gaborit
À (at) Tue, 26 Jul 2005 09:27:31 +0200,
Sébastien Cottalorda écrivait (wrote):
Dans le cas précis que j'évoquais, je suis en mode non bloquant, cela
signifie que si aucune socket n'a quelque chose à me dire, et si je
n'ai rien à envoyer, j'entre alors dans une boucle while(1) très
rapide.


Utiliser le mode non-bloquant n'a de sens que si on a quelque chose
d'autre à faire pendant ce temps (par exemple, un gros calcul ou la
gestion d'une interface utilisateur). Si la seule chose à faire est de
tester que "1" est vrai et de regarder à nouveau si il y a quelque
chose à lire ou à écrire autant faire tout cela en mode bloquant...

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/&gt;
Perl en français - <http://perl.enstimac.fr/&gt;

Avatar
Sébastien Cottalorda
À (at) Tue, 26 Jul 2005 09:27:31 +0200,
Sébastien Cottalorda écrivait (wrote):

Dans le cas précis que j'évoquais, je suis en mode non bloquant, cela
signifie que si aucune socket n'a quelque chose à me dire, et si je
n'ai rien à envoyer, j'entre alors dans une boucle while(1) très
rapide.



Utiliser le mode non-bloquant n'a de sens que si on a quelque chose
d'autre à faire pendant ce temps (par exemple, un gros calcul ou la
gestion d'une interface utilisateur). Si la seule chose à faire est de
tester que "1" est vrai et de regarder à nouveau si il y a quelque
chose à lire ou à écrire autant faire tout cela en mode bloquant...

Je suis d'accord, dans mon cas, je programme une interruption ALRM qui

provoque le remplissage des buffers d'emission.

Si je devais attendre au moins une réception (en mode bloquant) pour
pouvoir envoyer les données sur les sockets, je ne serais pas sorti de
l'auberge :-)


Avatar
Paul Gaborit
À (at) Tue, 26 Jul 2005 12:14:17 +0200,
Sébastien Cottalorda écrivait (wrote):
Si je devais attendre au moins une réception (en mode bloquant) pour
pouvoir envoyer les données sur les sockets, je ne serais pas sorti de
l'auberge :-)


Mais le principe est d'attendre en mode bloquant dans les deux sens :
en lecture (si les programmes à l'autre bout envoient quelque chose)
mais aussi en écriture (si on a quelque chose à envoyer bien sûr et si
les buffers d'émission ne sont pas pleins).

Même la gestion d'une interface utilisateur peut utiliser le mode
bloquant puisque la réception des événement utilisateurs se fait
souvent via une connexio (c'est le cas de X-Window).

C'est l'intérêt d'Unix où tout est fichier...

Le seul cas où le mode non-bloquant est intéressant est le cas d'un
calcul long pour éviter de bloquer les communications.

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/&gt;
Perl en français - <http://perl.enstimac.fr/&gt;

Avatar
Nicolas George
Sébastien Cottalorda wrote in message
<42e5e5db$0$6522$:
$select est l'objet que j'utilise avec le module IO::Select comme ceci.


Je viens de survoler la doc de ce module, et au premier regard, j'ai
l'impression que l'API est assez foireuse. Et pendant que j'écrivais ce
message, j'ai regardé les sources, et ça confirme : ce module est une
connerie.

Le mode de fonctionnement normal de select sous Unix est le suivant :

- je calcule sur quels fd je veux écrire, je renseigne les champs du fdset
en conséquence ;

- je calcule sur quels fd je veux lire, je renseigne les champs du fdset en
conséquence ;

- je calcule dans combien de temps j'aurai quelque chose d'autre à faire ;

- je lance select avec le fdset calculé en lecture, le fdset calculé en
écriture (qui sont souvent distincts), et le timeout calculé ;

- je regarde la sortie de select, et j'agis en conséquence.

Là, le module fournit des méthodes distinctes pour la lecture et l'écriture,
ce qui est un contresens complet. Sa seule vague utilité, c'est la méthode
statique select, qui fournit une interface un peu plus sympathique que la
primitive select, mais pas tellement.

Dans le cas précis que j'évoquais, je suis en mode non bloquant, cela
signifie que si aucune socket n'a quelque chose à me dire, et si je n'ai
rien à envoyer, j'entre alors dans une boucle while(1) très rapide.
Il ne semble pas, toutefois, que mon CPU monte à 100% pour autant.


C'est pourtant certainement ce qu'il fait, puisque perl passe son temps à
appeler select et passer à la suite.

Accessoirement, j'ai lu un peu plus loin dans le thread qu'il était question
d'utiliser alert pour ajouter des données en sortie : c'est considéré de nos
jours comme une pratique assez mauvaise, à cause de l'API mal conçue des
signaux Unix.

La bonne manière de procéder pour faire ça est :

- déterminer sur quelles sockets il y a des données à envoyer ;

- déterminer (avec Time::HiRes par exemple) dans combien de temps aura lieu
la prochaine interruption ;

- bloquer sur un select sur toutes les sockets en lecture, celles auxquelles
on veut parler en écriture, et un timeout correspondant à la prochaine
interruption.

Cette recommandation n'est d'ailleurs pas spécifique à perl : c'est général
à la programmation Unix quand on choisit de multiplexer les entrées-sorties
à la main (par opposition à des entrées-sorties asynchrones ou du
multithreadé ou du multiprocessus).

Avatar
Paul Gaborit
À (at) Tue, 26 Jul 2005 14:03:26 +0000 (UTC),
Nicolas George <nicolas$ écrivait (wrote):
Je viens de survoler la doc de ce module, et au premier regard, j'ai
l'impression que l'API est assez foireuse. Et pendant que j'écrivais ce
message, j'ai regardé les sources, et ça confirme : ce module est une
connerie.


Heu... Un module écrit par Graham Barr est rarement (jamais?) une
connerie !

[...]
Là, le module fournit des méthodes distinctes pour la lecture et l'écriture,
ce qui est un contresens complet. Sa seule vague utilité, c'est la méthode
statique select, qui fournit une interface un peu plus sympathique que la
primitive select, mais pas tellement.


Les méthodes distinctes sont utiles lorsqu'on n'attend que des
lectures, que des écritures ou que des exceptions.

La fonction 'select' gère le cas général et permet de récupérer
imméditatement les objets à traiter. Elle est vraiment *beaucoup* plus
simple d'usage que la fonction 'select' primitive.

En fait, ce module, bien utilisé, me semble très pratique et encapsule
les choses proprement (la manipulation des fd et des vecteurs de bits
n'est pas vraiment simples à faire). Mais, effectivement, il n'empêche
pas de faire n'importe quoi.

Pour le reste, je suis tout à fait d'accord avec vos remarques...

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/&gt;
Perl en français - <http://perl.enstimac.fr/&gt;

1 2