Problème de serveur TcpListener ! [Résolu]

madkeyboard 100 Messages postés mercredi 27 avril 2011Date d'inscription 21 décembre 2012 Dernière intervention - 4 avril 2012 à 11:57 - Dernière réponse : madkeyboard 100 Messages postés mercredi 27 avril 2011Date d'inscription 21 décembre 2012 Dernière intervention
- 6 avril 2012 à 12:51
Bonjour à tous !

J'ai un problème qui me dépasse complètement...
Voilà la situation : je travaille sur une application industrielle, qui doit permettre de relier un ordinateur à un autre relié à un centre d'usinage.

J'ai développé 1 application "cliente" sur le premier pc, et une application "serveur" sur le pc relié à la machine (procédure de palpage).
J'utilise différents threads pour les différentes étapes de la procédure industrielle dont je m'occupe.
Lorsque la machine travaille, j'ai 2 ports de communications ouverts (4500 et 4501) sur la même IP.
J'en viens à mon problème : quand une procédure est terminée et que j'en redémarre une, au moment où la machine doit travailler, le serveur refuse de démarrer le TcpListener du port 4501 (que je referme à la fin de chaque procédure), avec ce message :

"Une seule utilisation de chaque adresse de socket (protocole/adresse réseau/port) est habituellement autorisée".
(voir dans le code en rouge, notamment le 2ème élément rouge dans le serveur, là où se situe l'erreur !!)

Pourtant, j'ai fermé les communications à tout va, j'ai dépisté les moindres lignes de code, j'ai même refait des tests avec des applications séparées... Rien à faire !!! Il me plante à chaque fois :'(


Je ne vais vous envoyer tout le code, qui est très lourd, mais quelques éléments sur cette partie de la communication (ce qui fait un bon roman quand même, désolé d'avance :s) :


Du côté du client :

//Application Client :

public string ReqPalpage(string stIP, int iPort)
        {
            string stReturnError = "Aucune erreur";
            byte[] dataAck = new byte[1024];

            TcpClient monClient = null;        //Ici la connexion principale, port 4500, qui me permet de communiquer toutes les requêtes au serveur

            try
            {
                try           //"monClientPalpage", ci-dessous, est aussi un TcpClient, même Ip que "monClient", port 4501
                {             //Ce sera la communication sur ce port (4501) qui posera des problèmes au serveur !
                    monClientPalpage.Close();              //On ferme la connexion avec le client du palpage, au cas où cette connexion aurait été mal fermée lors d'un précédent palpage ! (à cause d'une erreur ou autre...)
                }
                catch (Exception)
                { }

                monClient = new TcpClient(stIP, iPort);                                 //On instancie le client

                monClient.ReceiveTimeout = 5000;                                        //Timeout reception 5s.

                monClient.GetStream().Write(Encoding.UTF8.GetBytes("PALPAGE"), 0, 7);   //On envoie la requête de palpage (procédure de la machine outil)

                monClient.GetStream().Read(dataAck, 0, 10);


                monClient.Close();              //On ferme la connexion avec le client "principal", car on utilisera une autre connexion pour la suite de cette méthode.
                                                //On libère ainsi l'utilisation de cette connexion-ci pour les autres requêtes, envoyées en parallèle (depuis le thread du graphe d'état).


                if (Encoding.UTF8.GetString(dataAck, 0, 10) != "OK_PALPAGE")        //Le serveur renvoi cette réponse en acquittement de la requête
                {
                    stReturnError = "Erreur : réponse du serveur incohérente";
                }


//Toutes les lignes suivantes servent à réceptionner les messages du serveur pour suivre la progression de la procédure de la machine : J'ouvre puis referme la connexion pour chaque
//nouveau message car je crois me souvenir que j'avais des problèmes en laissant ouvert la connexion pendant toute la durée (cela fait longtemps, je ne sais plus exactement pourquoi !)

                else
                {
                    OnCallMaJUC("TEXTE", "\nTransfert des fichiers vers la CN. Veuillez patienter...");

                    monClientPalpage = new TcpClient(stIP, iPort + 1);                          //On instancie le client avec un nouveau numéro de port, afin que les 2 applications puissent toujours communiquer en parralèlle du palpage sur le 1er numéro de port
                    monClientPalpage.ReceiveTimeout = 0;                                        //Timeout reception infini.

                    monClientPalpage.GetStream().Read(dataAck, 0, 1024);

                    if (Encoding.UTF8.GetString(dataAck, 0, 15) == "RUN_DEPARTCYCLE")
                    {
                        OnCallMaJUC("TEXTE", "Fichiers transférés !\n\n///VOUS POUVEZ LANCER LE DEPART CYCLE" + @"\\");

                        monClientPalpage.Close();
                        monClientPalpage = new TcpClient(stIP, iPort + 1);

                        monClientPalpage.GetStream().Read(dataAck, 0, 1024);

                        if (Encoding.UTF8.GetString(dataAck, 0, 15) == "PALPAGE_RUNNING")
                        {
                            OnCallMaJUC("TEXTE", "\nPalpage en cours. Veuillez patienter...");

                            monClientPalpage.Close();
                            monClientPalpage = new TcpClient(stIP, iPort + 1);

                            monClientPalpage.GetStream().Read(dataAck, 0, 1024);

                            if (Encoding.UTF8.GetString(dataAck, 0, 15) == "DONE_PALPAGE   ")
                            {
                                OnCallMaJUC("TEXTE", "\nPalpage accompli avec succès.");
                            }
                            else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_PALPAGE   ")
                            {
                                stReturnError = "Erreur : Problème lors de la lecture des variables externes de la CN !";
                            }
                            else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_EXCEPTION_")
                            {
                                stReturnError = "Erreur palpage (exception sur serveur) : " + Encoding.UTF8.GetString(dataAck, 15, dataAck.Length - 15);
                            }
                            else
                            {
                                stReturnError = "Erreur : réponse du serveur incohérente";
                            }
                        }
                        else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_EXCEPTION_")
                        {
                            stReturnError = "Erreur palpage (exception sur serveur) : " + Encoding.UTF8.GetString(dataAck, 15, dataAck.Length - 15);
                        }
                        else
                        {
                            stReturnError = "Erreur : réponse du serveur incohérente";
                        }
                    }
                    else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_TRANSFERT1")
                    {
                        stReturnError = "Erreur : Le serveur n'a pas pu charger le fichier FCTABIN.XPI dans la CN";
                    }
                    else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_TRANSFERT2")
                    {
                        stReturnError = "Erreur : Le serveur n'a pas pu charger le fichier FormCont.XPI dans la CN";
                    }
                    else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_CONNECTION")
                    {
                        stReturnError = "Erreur : Le serveur n'a pas pu se connecter à la CN";
                    }
                    else if (Encoding.UTF8.GetString(dataAck, 0, 15) == "FAIL_EXCEPTION_")
                    {
                        stReturnError = "Erreur palpage (exception sur serveur) : " + Encoding.UTF8.GetString(dataAck, 15, dataAck.Length - 15);
                    }
                    else
                    {
                        stReturnError = "Erreur : réponse du serveur incohérente";
                    }
                }
            }
            catch (Exception exception)
            {
                stReturnError = "\n" + exception.Message;
            }


            try            //A la fin, on ferme la communication "temporaire" (port 4501). Utilisation du try pour éviter de générer une erreur si l'on arrive ici alors que la connexion n'a pas été initiée (exception générée au début de la fonction, par ex)
            {
                monClientPalpage.Close();
            }
            catch (Exception) { }


            return stReturnError;
        }




Et du côté serveur (là ou j'ai l'erreur !) :

//Application serveur :



//METHODE PRINCIPALE (Main de ce thread)
        //Méthode qui tourne dans un thread en parallèle à l'IHM qui attend une demande de connexion pour réceptionner la requête du client
        private void AttenteRequête()
        {
            Serveur = new TcpListener(IpConnection, iPortPC);
            ServeurPalpage = new TcpListener(IpConnection, iPortPC + 1);
            byte[] dataIN = new byte[4096];
            string stTrameReq = "";

            Serveur.ExclusiveAddressUse = true;
            Serveur.Start();


            OnCallMaJIHM("TEXTE", "\n============ SERVEUR EN ECOUTE ============\n");

            do
            {

                if (Serveur.Pending())
                {

                    try
                    {
                        LeClient = Serveur.AcceptTcpClient();

                        dataIN.Initialize();                //On réinitialise les données reçues et la trame décodée.
                        stTrameReq = "";

                        LeClient.ReceiveTimeout = 5000;        //On fixe le temps d'attente de réception de données à 5s. Le temps dépassé, une exception est générée


                        if (LeClient.GetStream().Read(dataIN, 0, dataIN.Length) != 0)           //Lecture de la requête. Si requête vide (0 octets transmis) : erreur
                        {
                            stTrameReq = Encoding.UTF8.GetString(dataIN);


                            if (stTrameReq.Remove(4) == "PING")
                            {
                                LeClient.GetStream().Write(Encoding.UTF8.GetBytes("OK_PING"), 0, 7);
                            }
                            else if //... etc... Plusieurs requêtes possibles !

                            else if (stTrameReq.Remove(7) == "PALPAGE")        //La requête qui nous intéresse
                            {
                                KillThreadPalpage();

                                try
                                {
                                    ClientPalpage.Close();
                                }
                                catch (Exception)
                                { }

                                LeClient.GetStream().Write(Encoding.UTF8.GetBytes("OK_PALPAGE"), 0, 10);            //Aquittement requête
                                OnCallMaJIHM("ETAT UC", "Requête : Palpage");

                                StartThreadPalpage();                                  //On démarre le Thread gérant le palpage. Le thread s'arrêtera à la fin du palpage. On peut continuer à envoyer des requêtes au serveur pendant le palpage.
                            }

                            else if //... etc...

                            else
                            {
                                LeClient.GetStream().Write(Encoding.UTF8.GetBytes("WRONG_TRAME"), 0, 11);           //Réponse si requête inatendue

                                OnCallMaJIHM("TEXTE_ITA", "\nRequête du client inatendue !\nAttente d'une requête adéquoite");
                            }

                        }
                        else
                        {
                            OnCallMaJIHM("TEXTE", "Erreur requête : demande du client incohérente");
                        }



                        try                 //On ferme la connexion
                        {
                            LeClient.Close();
                        }
                        catch (Exception)
                        { }

                    }
                    catch (Exception exception)
                    {
                        OnCallMaJIHM("TEXTE", "\n" + exception.Message);

                        try
                        {
                            LeClient.Close();
                        }
                        catch (Exception)
                        { }
                    }

                }


                Thread.Sleep(200);

            } while (Thread.CurrentThread.IsAlive);

            Serveur.Stop();
        }





        public void StartThreadPalpage()
        {
            ThreadPalpage = new Thread(new ThreadStart(Palpage));
            ThreadPalpage.Start();              //On lance le thread exécutant le palpage
        }

        private void Palpage()      //Fonction lancée par le threadPalpage, pour exécuter le transfert du fichier puis le palpage
        {
            string stErreur = "";

            ServeurPalpage.ExclusiveAddressUse = true;

//!!!!!  
            ServeurPalpage.Start();    //ICI SE SITUE L'EXCEPTION !!! => impossible de démarrer le serveur lors d'une 2ème procédure !!
//!!!!!  

            stErreur = TranfsertVersCN();               //Dans cette fonction, on transfert des fichiers vers la machine

            if (stErreur == "Aucune Erreur")
            {
                ExePalpageEtRecupCoord();               //Cette fonction gère le déroulement de la procédure, et notament communique avec le client pour transmettre le déroulement et les messages d'erreur !
            }
            else
            {
                OnCallMaJIHM("TEXTE", "\nErreur lors du transfert des fichiers vers la CN.\n=> " + stErreur);      //nota : on renvoi aussi les messages d'erreurs dans l'interface de cette application (serveur), en plus de les envoyer au client !
            }

            try
            {
                ClientPalpage.Close();
            }
            catch (Exception)
            { }

            ServeurPalpage.Stop();
        }





A noter aussi : je n'ai rien mis à propos des formulaires, tout fonctionnant bien, cela importe peu !
Sachez seulement que toutes les fonctions ci-dessus sont exécutées dans des Threads différents de ceux des formulaires ! (2 threads différents dans le cas du serveur : le thread qui attend les requêtes, et le thread qui gère le déroulement de la procédure de palpage !)


Voila, j'espère que cela ne vous embêtera pas d'analyser ce problème, le code étant un peu lourd !!!

Merci d'avance pour votre aide et pour vos idées !!


Mad Keyboard
Afficher la suite 

Votre réponse

2 réponses

Meilleure réponse
Tupad 239 Messages postés lundi 5 décembre 2005Date d'inscription 27 août 2012 Dernière intervention - 4 avril 2012 à 12:33
3
Merci
Problème déjà posé sur le forum:
http://www.csharpfr.com/forum/sujet-REUTILISER-SOCKET_885009.aspx

Merci Tupad 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 101 internautes ce mois-ci

Commenter la réponse de Tupad
Meilleure réponse
madkeyboard 100 Messages postés mercredi 27 avril 2011Date d'inscription 21 décembre 2012 Dernière intervention - 6 avril 2012 à 12:51
3
Merci
Salut !

Merci de ta réponse, je n'avais pensé à chercher du côté des sockets, je m'étais cantonné aux TcpListener/TcpClient...
La solution proposé ne fonctionne pas vraiment, mais le problème est bien identique !
J'ai réussi à résoudre mon souci !


Bref, pour ceux qui aurait le même problème :

J'ai utilisé avant de démarrer mon serveur la ligne de code :
ServeurPalpage.ExclusiveAddressUse = true;

Or j'ai lu un truc sur msdn :
Remarques à l'attention des appelants

La méthode Stop ferme également le Socket sous-jacent et crée un nouveau Socket pour TcpListener. Si vous définissez des propriétés sur le Socket sous-jacent avant d'appeler la méthode Stop, ces propriétés ne seront pas reprises sur le nouveau Socket.


En bref, je suppose que cela signifie que si l'on donne l'exclusivité de la connexion pour une adresse IP/port donné, si l'on ferme la com et qu'on la rouvre, le nouveau socket créé ne pourra s'initialisé, la connexion étant bloquée par l'ancien socket fermé !

Il m'a suffit de virer cette ligne de code, et le tour est joué !


Voila, merci pour ton aide Tupad !

Mad Keyboard

Merci madkeyboard 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 101 internautes ce mois-ci

Commenter la réponse de madkeyboard

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.