Probleme lors du "_Close" d'un controle winsock.

Résolu
Messages postés
27
Date d'inscription
jeudi 29 janvier 2004
Statut
Membre
Dernière intervention
8 mai 2009
-
Messages postés
27
Date d'inscription
jeudi 29 janvier 2004
Statut
Membre
Dernière intervention
8 mai 2009
-
Bonjour, j'essaie de déveloper une application serveur pouvant connecté plusieurs client (plus de 2). Lorsqu'un client se déconnecte du serveur, tous les autres clients connectés doivent être prévenu par le serveur. Mon probleme survient à l'instruction "_Close" de mes controles winsock. J'explique:

Un client se déconnecte et l'instruction "Winsock_Close(Index as integer)" se déclanche.
Dans cette instruction, j'aimerai qu'à la fois on ferme le controle winsock de celui qui quitte et envoyer un Winsock.SendData à un des autres clients pour signaler le départ. Je n'en préviens qu'un seul et les SendData pour les autres sont envoyé dans l'accusés de réception du premier.

Le probleme est que l'instruction n'arrive pas à envoyer plusieurs instructions Winsock en une fois. Je dois d'abord faire mon .SendData puis le .Close (ou l'inverse). Si je met les 2 instructions en meme temps, l'une ou l'autre est ignorée. Mon _Close devient un champ de bataille de IF, de Else et ok=true pour essayer de dissocier les 2 instructions. Mais malgré tout ca ne marche pas.

Par contre, quand j'execute le programme serveur en "Pas à pas", tout semble fonctionner.. mais en execution normale, le serveur ignore la 2em instruction _close qu'il est sencé recevoir. (exemple: 3 utilisateurs connectés. j'enleve le n°2 et les 2 autres sont prevenu... puis j'enleve le n°1 mais là personne n'est prevenu, meme le serveur n'a pas recu de _close de la part du n°1)

Je crois que j'organise mal ma procédure _Close mais je ne sais vraiment pas comment faire mieux.
Je vais poster le code source de mon _Close mais ca risque de vous piquer les yeux tellement c'est en désordre

9 réponses

Messages postés
14008
Date d'inscription
samedi 29 décembre 2001
Statut
Modérateur
Dernière intervention
28 août 2015
81
Donc, résoud déjà tes problèmes d'organisation avant de voir pourquoi les messages ne semblent pas envoyés.
Il se résoudra peut-être seul une fois que les bases seront corectes !
Messages postés
27
Date d'inscription
jeudi 29 janvier 2004
Statut
Membre
Dernière intervention
8 mai 2009

Private Sub TCPServeur_Close(Index As Integer) 'Un client se déconnecte du portail:

If SecondTour = False Then 'On vérifie si on est deja passé pour l'envoit du message.
    
    For i = 1 To UBound(User)    'On vérifie que l'instruction close provient bien d'un
        If User(i).ID = Index Then  'des utilisateurs connecté au serveur sinon l'instruction
            Ok = True                      'est ignorée.
        End If
    Next i
    
    If Ok = True Then 'Le client sortant existe dans le tableau des utilisateurs:
        UserQuit = Index 'On mémorise le n° du client sortant...
        UserCount = UBound(User) 'et le nombre d'utilisateur devant recevoir le message.
        
        If UBound(User) > 1 Then 'Si il y'a au moins un utilisateur à prévenir:
            'Si le dernier utilisateur est le client sortant:          

            If User(UBound(User)).ID = UserQuit Then
                UserCount = UserCount - 1 'On passe au dessus.
            End If
            TCPServeur(User(UserCount).ID).SendData UserIndex(UserQuit) & Chr(5) 'On averti le dernier connecté.

        Else 'Il n'y a personne d'autre à prévenir:
            
        'Le serveur l'enleve de la liste et un message est signalé dans la zone de discussion:
            ListNick.RemoveItem (UserIndex(UserQuit) - 1)
            TextChat.Text = TextChat.Text + User(UserIndex(UserQuit)).Nom & " a quitté le salon de discussion." + vbCrLf
            For n = 1 To UBound(User) 'Le serveur l'enleve du tableau d'utilisateur et le redimentione:
                If User(n).ID = Index Then
                    ReDim Preserve User(UBound(User) - 1)
                    Exit For
                End If
            Next n
        End If
        SecondTour = True 'On peut maintenant repasser pour le _Close
    Else: Exit Sub 'Le client sortant n'existe pas dans le tableau, on ignore l'appel.
    End If

Else 'On en est au deuxieme passage, il ne reste plus qu'à fermer le controle Winsock.
    SecondTour = False
    TCPServeur(Index).Close
    DoEvents
End If
End Sub

Note: UserIndex est une fonction qui renvoie l'index d'un élément de mon tableau User sur base de l'index du controle Winsock sur lequel il est lié. C'est pour m'assurer que j'envois correctement le n° du client qui quitte aux autres. Mes autres variables sont bien définie, leurs valeurs sont toujours exactes. Je crois que c'est la structure qui est mal faite.
Messages postés
14008
Date d'inscription
samedi 29 décembre 2001
Statut
Modérateur
Dernière intervention
28 août 2015
81
Salut
Si, les Winsock sont capables d'envoyer plusieurs messages à la suite.
C'est côté réception que tu dois mal gérer les chaines qui arrivent.
Est-ce que chacun de tes messages ont un caractère de "fin de trame", c'est à dire un caractère que tu ne trouveras pas dans des chaines classiques et permettant de s'assurer de la fin d'un message ?
Perso, j'utilise Chr(0) pour terminer chaque message.
Ce Chr(0) est facile à repérer à la réception et permet ainsi de découper la chaine reçue en plusieurs messages si ceux-ci arrivent en même temps.
C'est la raison pour laquelle en pas-à-pas, tout fonctionne puisqu'il y a un délai manuel entre les trames.

D'autre part, un Winsock s'adresse aux communications du PC. Après avoir envoyer un message, il faut bien sûr laisser le temps au système d'envoyer ces données sur l'interface matériel des réseaux.
Si tu fais un SendData suivi immédiatement d'un Close, c'est sûr, le message ne sera pas envoyé.
--> Ajoute un DoEvents après chaque SendData

Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés

Champion du monde de boule de cristal - 2005
Le savoir est la seule matière qui s'accro
Messages postés
27
Date d'inscription
jeudi 29 janvier 2004
Statut
Membre
Dernière intervention
8 mai 2009

J'ai mis un Doevents apres la moindre ligne de mon programme contenant l'instruction senddata. Et ca cause des problemes dés la connexion de clients. Le premier s'affiche, mais les suivants sont innapercu. Et je ne pense pas que le probleme se situe au niveau des applications client. Ils n'ont qu'un seul controle winsock à gerer et ils n'envoient jamais qu'une seule instruction à la fois.

Sinon, oui, j'utilise bien un caractère fin de trame pour differencier mes messages. J'en utilise plusieurs en fait:
chr(0) pour l'authorisation ou le refus de connexion sur le serveur.
chr(1) pour l'envoit de la liste des utilisateurs déjà connectés.
chr(2) pour envoyer le nom d'un utilisateur qui vient d'arriver.
chr(3) pour envoyer le nom d'un utilisateur qui se déconnecter.
chr(4) pour envoyer un message tapper par quelqu'un.
Je ne compte pas en utiliser beaucoup plus.
Messages postés
14008
Date d'inscription
samedi 29 décembre 2001
Statut
Modérateur
Dernière intervention
28 août 2015
81
Ok pour tes applications client, mais es-tu sûr de bien faire la recherche de plusieurs messages à la suite lors d'une seule opération DataArrival ? c'est ça que je veux te dire.

D'autre part, quand un User se déconnecte, tu veux le supprimer de ton tableau. Ok
Tel que tu le fais, ça doit déconner :
            For n = 1 To UBound(User) 'Le serveur l'enleve du tableau d'utilisateur et le redimentione:
                If User(n).ID = Index Then
                    ReDim Preserve User(UBound(User) - 1)    <--- là, ça va pas
                    Exit For
                End If
            Next n
Supposons :
- Ton tableau possède 4 Users
- Le User 1 se déconnecte
- Tel que tu le fais en redimensionnant ton tableau, tu vires le User 4 mais le User 1 est toujours là !
= mélange

Non, tu ne peux pas toucher à la taille du tableau avant d'avoir décalé toutes les données
Une fois que tu as trouvé le User en 2 par exemple, il faut :
Faire une boucle (à l'intérieur de la 1ere) commençant à x et finissant à Maxi-1 et faire User(x) = User(x + 1)
Ensuite seulement tu pourras retoucher à la taille de ton tableau.
Messages postés
14008
Date d'inscription
samedi 29 décembre 2001
Statut
Modérateur
Dernière intervention
28 août 2015
81
Beaucoup de choses qui ne vont pas, après relecture (et j'ai pas le temps de détailler ce soir).
Refflechis à ce que tu as écrit :
Par exemple, le 1er test qui passe le Ok à True
Pourquoi voudrais tu recevoir une demande de déconnexion d'un User que tu ne connaitrais pas ? bizarre et inutile.

Utilise aussi le Pas-à-pas de débuggage :
Mets ton curseur sur la première ligne de ton code et tape sur F9 : la ligne change de couleur et le programme s'arrêtera là.
Quand ça arrivera, utilise F8 pour avancer de ligne en ligne.
Dans la fenêtre de debug (Ctrl-G), affiche le contenu de tes variables ... et suit ce qui se passe dans ton programme.
F5 pour laisser le programme s'exécuter normalement ensuite.
Messages postés
27
Date d'inscription
jeudi 29 janvier 2004
Statut
Membre
Dernière intervention
8 mai 2009

J'ai eu le cas (apres des déconnexions de clients) que le serveur continuait malgré tout à recevoir des instructions _Close de la part de client déja déconnecté. Par exemple, le client utilisant le Winsock(1) s'en va. Il envoit son Winsock(1)_Close sur le serveur, comme prévu. Et il est traité chez le serveur qui passe sur un Winsock(1).Close.  Jusque là tout à l'air normal.. je déconnecte encore un autre client (celui là utilisait le winsock(2)) j'en connecte un dernier.. et là le serveur plante car il à reçu à nouveau une instruction _Close à l'index 1 meme si le client qui l'utilisait cet index à été déconnecté bien avant.

Comme je n'ai pas réeussi à empecher ces demandes close mystérieuse d'arriver, j'ai ajouté cette partie du code pour ignorer les demandes de close qui ne proviennent pas des utilisateurs actuellement connectés.
Messages postés
14008
Date d'inscription
samedi 29 décembre 2001
Statut
Modérateur
Dernière intervention
28 août 2015
81
Comme je te l'ai dit, ta méthode de suppression de clients dans ton tableau ne fonctionne pas.
Messages postés
27
Date d'inscription
jeudi 29 janvier 2004
Statut
Membre
Dernière intervention
8 mai 2009

C'est bon, j'ai repris toute ma procédure _Close depuis le debut et je l'ai organisée autrement. Je fais le .close en premier, je redimensionne mon tableau (avec une autre methode; cette fois ci je décale tout les éméments en écrasant celui qui quitte avant de redimensionner), puis j'envois le message à ceux qui restent. Le tout dans la meme procédure. Tout marche convenablement, maintenant.