IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Problème d'envoi/réception de string en situation client/serveur avec TcpClient [Débutant]


Sujet :

C#

  1. #1
    Membre averti
    Inscrit en
    Juin 2011
    Messages
    258
    Détails du profil
    Informations forums :
    Inscription : Juin 2011
    Messages : 258
    Points : 334
    Points
    334
    Par défaut Problème d'envoi/réception de string en situation client/serveur avec TcpClient
    Bonjour,

    Je rencontre un souci avec mon application. Un serveur doit recevoir des instructions de clients et leur renvoyer le résultat.

    Coté client, le code (j'ai retiré tous les tests pour simplifier):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Thread threadEnvoiDemande = new Thread(gestionEnvoisDemandes);
    threadEnvoiDemande.Start();
     
    private void gestionEnvoisDemandes() {
    	while(condition) {
    		//attente d'un évènement
    		NetworkStream stream = _connexionServeur.GetStream(); //_connexionServeur est un TcpClient
    		using (BinaryWriter writer = new BinaryWriter(stream)) {
    			JObject message = miseEnFormeDemande(param);
    			writer.Write(message.ToString());
    		}
    	}
    }
    Coté serveur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    TcpListener _listener = new TcpListener(IPAddress.Any, 1337);
    _listener.Start();
     
    Thread threadListener = new Thread(listenForClients);
    threadListener.Start();
     
    private void listenForClients() {
        while (!_conditionArret) {
            //TODO: event wait
            if (_listener.Pending()) { //Si des connexions sont en attente
     
                _listener.BeginAcceptTcpClient(connexionAcceptee, _listener);
            }
     
        }
    }
     
    private void connexionAcceptee(IAsyncResult result) {
        TcpClient client = _listener.EndAcceptTcpClient(result);
     
        //initialisation du thread attendant les connexions clientes
        Thread threadClient = new Thread(() => gestionConnexionClient(client));
        threadClient.Start();
    }
     
    private void gestionConnexionClient(TcpClient client) {
        NetworkStream stream = client.GetStream();
        BinaryReader reader = new BinaryReader(stream);
        if (reader.PeekChar() == -1) return; //toujours égal à -1
    }
    Le problème: Coté serveur, la ligne: if (reader.PeekChar() == -1) return; est toujours égale à true, donc le stream semble vide.

    Précisions:
    - j'utilise le même stream coté client et serveur. Je me connecte au serveur depuis le client, créé un stream et récupère le stream coté serveur avec le TcpClient.GetStream().
    - j'utilise le même port coté client et serveur
    - je teste en local pour le moment, 127.0.0.1:1337 en adresse de connexion au serveur

    N'hésitez pas à me demander des précisions.

    Merci

  2. #2
    Membre averti
    Inscrit en
    Juin 2011
    Messages
    258
    Détails du profil
    Informations forums :
    Inscription : Juin 2011
    Messages : 258
    Points : 334
    Points
    334
    Par défaut
    J'ai ajouté un .Flush() après le Write et mis des AcceptTcpClient au lieu de BeginAccept. Ca ne fonctionnait toujours pas. Puis j'ai enlevé la condition, et ça fonctionne. Comment le peek peut être == à -1 pendant que le reader a un string qui attend? :/

  3. #3
    Invité
    Invité(e)
    Par défaut
    Bonjour Garheb,

    Ton système est très bien ficelé.

    Cependant, il aurait fallu que tu trouves un autre moyen que "PeekChar" pour savoir si c'est le message attendu. En effet, la doc de "PeekChar" précise bien :
    Return Value
    Type: System.Int32
    The next available character, or -1 if no more characters are available or the stream does not support seeking.
    En français : ça retourne -1 dans l'un des 2 cas :
    - Il n'y a pas d'autres caractères disponible
    OU
    - Le flux ne supporte pas la recherche

    Et, d'après la doc de "NetworkStream.CanSeek", il se trouve que "CanSeek" renvoie toujours "false" autrement dit le flux ne supporte pas la recherche.

    [EDIT] Petite explication sur "la recherche" : en fait c'est la possibilité de se positionner à un point bien précis dans un flux. Mais pour que cela ait un sens, il faut que la longueur totale du flux soit connu. C'est le cas avec un "FileStream" par exemple. Mais ce n'est pas le cas pour "NetworkStream" ou l'on lit les octets au fil de l'eau et par définition il peut toujours y avoir d'autres éléments disponibles donc elle n'a pas de longueur définie.
    La méthode "PeekChar" lit le caractère suivant et revient après un caractère en arrière. C'est donc pour cela que ce n'est pas possible avec un "NetworkStream".
    Dernière modification par Invité ; 07/04/2014 à 16h47.

  4. #4
    Membre averti
    Inscrit en
    Juin 2011
    Messages
    258
    Détails du profil
    Informations forums :
    Inscription : Juin 2011
    Messages : 258
    Points : 334
    Points
    334
    Par défaut
    Merci beaucoup! Je rechange les AcceptTcpClient en BeginAcceptTcpClient du coup.

    J'aimerais aussi savoir si, pour une communication entre un client et un serveur qui est récurrente, un message sera passé toutes les x ms, je devrais garder le stream ouvert, ou en créer un à chaque message. Garder ouvert me semble plus logique, c'est ce que je fais actuellement, mais je n'en suis pas sur.

    Quelques autres questions: est-ce que le BinaryReader.getString() attendra d'avoir reçu le string entier pour le lire? Ou bien il prend ce qu'il a reçu et prendra le reste pour le prochain getString()? Est-ce que le binaryReader est capable de savoir quand il a reçu tout le string qu'il attendait?

    Dans le cas d'un envoi récurrent sur le même stream, du coup est-ce qu'il pourra se débrouiller tout seul pour savoir que le string qu'il reçoit ne fait pas partie du string avant celui-ci?

    Enfin, est-ce qu'un système d'accusé de réception serait pertinent? Si le message reçu est incorrect, envoyer une requête de demande de renvoi, sinon une confirmation de réception. Sachant que le principal but de ces échanges client-serveur est de connecter un serveur à plusieurs clients, et à leur envoyer des informations toutes les x ms (des valeurs sur un automate). Le système devra donc supporter la charge.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par garheb Voir le message
    J'aimerais aussi savoir si, pour une communication entre un client et un serveur qui est récurrente, un message sera passé toutes les x ms, je devrais garder le stream ouvert, ou en créer un à chaque message. Garder ouvert me semble plus logique, c'est ce que je fais actuellement, mais je n'en suis pas sur.
    En effet cela n'a pas de sens de fermer le NetworkStream puisque c'est l'objet "TcpClient" qui te le fournit. Tant que le client est connecté, il y a un flux...

    Citation Envoyé par garheb Voir le message
    est-ce que le BinaryReader.getString() attendra d'avoir reçu le string entier pour le lire? Ou bien il prend ce qu'il a reçu et prendra le reste pour le prochain getString()? Est-ce que le binaryReader est capable de savoir quand il a reçu tout le string qu'il attendait?
    Ah oui, alors cela va compléter ce que j'ai mis précédemment. C'est vrai que tu ne peux pas utiliser la méthode "PeekChar" du BinaryReader. En revanche, tu devrais utiliser la méthode "DataAvailable" à la place pour savoir si du contenu est disponible. Ensuite, tu peux effectivement utiliser la méthode "ReadString" du "BinaryReader" car d'après la doc des classes "BinaryReader" et "BinaryWriter", la chaîne récupérée est accompagnée de sa longueur. Donc le "ReadString" va s'en sortir (et puisqu'en face tu utilise WriteString alors il n'y aura pas de soucis).
    Après, dans tous les cas, il faut bien attraper l'exception "IOException" car c'est l'exception qui aura lieu s'il y a une déconnexion du serveur.
    Pour reprendre ta fonction "gestionConnexionClient" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    private void gestionConnexionClient(TcpClient client)
    {
        NetworkStream stream = client.GetStream();
        try
        {
            while (true)
            {
                if (Stream.DataAvailable)
                {
                     BinaryReader reader = new BinaryReader(stream);
                     string chaineRecup = reader.ReadString();
                }
                Thread.Sleep(10); //Pour ne pas que le processeur soit tout le temps à fond la caisse...
            }
        }
        catch(IOException)
        {
            //Déconnexion du serveur!
        }
    }
    Citation Envoyé par garheb Voir le message
    Dans le cas d'un envoi récurrent sur le même stream, du coup est-ce qu'il pourra se débrouiller tout seul pour savoir que le string qu'il reçoit ne fait pas partie du string avant celui-ci?
    Comme je disais plus haut, "WriteString" de "BinaryWriter" écrit aussi la longueur de la chaîne donc du coup "ReadString" de "BinaryReader" le sait donc pas de soucis.

    Citation Envoyé par garheb Voir le message
    Enfin, est-ce qu'un système d'accusé de réception serait pertinent?
    En effet ça peut être pertinent, tout à fait.

  6. #6
    Membre averti
    Inscrit en
    Juin 2011
    Messages
    258
    Détails du profil
    Informations forums :
    Inscription : Juin 2011
    Messages : 258
    Points : 334
    Points
    334
    Par défaut
    Génial, merci pour tes réponses. Par contre pour le sleep je vais essayer de voir pour faire quelque chose de moins "hasardeux" car les performances sont importantes, je vais donc voir si il est possible d'utiliser un ManualEventReset sur un évènement pour que tout se fasse avec un temps d'attente pertinent.

    Edit: en fait il n'y a peut-être pas besoin d'un sleep si le .ReadString() attend que le string ait été reçu, non? du coup si c'est ça pas besoin de sleep. Je boucle sur ReadString et ça devrait attendre tout seul (si j'ai bien compris comment fonctionne le BinaryReader)

  7. #7
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par garheb Voir le message
    Génial, merci pour tes réponses. Par contre pour le sleep je vais essayer de voir pour faire quelque chose de moins "hasardeux" car les performances sont importantes, je vais donc voir si il est possible d'utiliser un ManualEventReset sur un évènement pour que tout se fasse avec un temps d'attente pertinent.
    En fait, je mettais un "Sleep" ici car j'ai remarqué que dans ce genre de "boucle continue", si je ne met pas de "Sleep" le processeur est à fond la caisse, mais je ne me rappelle plus s'il y a le même problème dans le contexte d'une lecture réseau... Peut-être que ce n'est pas nécessaire de mettre "Sleep". Dans tous les cas seul le test nous le dira.

    Citation Envoyé par garheb Voir le message
    Edit: en fait il n'y a peut-être pas besoin d'un sleep si le .ReadString() attend que le string ait été reçu, non? du coup si c'est ça pas besoin de sleep. Je boucle sur ReadString et ça devrait attendre tout seul (si j'ai bien compris comment fonctionne le BinaryReader)
    Alors un petit piège ici. "BinaryReader" va encapsuler ton "NetworkStream". Si tu fais une opération de "Read" directement à partir du NetworkStream, effectivement il y aura une attente jusqu'à ce que quelque chose soit dispo dans le flux... Mais si tu utilises le "ReadString" du BinaryReader, et que rien n'est disponible, tu vas avoir une exception "EndOfStreamException".
    Cela dit, c'est pour éviter tout ce schmilblick que j'ai suggéré que tu mettes juste avant la lecture le "DataAvailable". Car lorsque "DataAvailable" vaut "true" on est tranquille il y a quelque chose de dispo dans le flux.
    Mais cela ne nous dispense quand même pas d'attraper l'exception "IOException" dans tous les cas.

  8. #8
    Membre averti
    Inscrit en
    Juin 2011
    Messages
    258
    Détails du profil
    Informations forums :
    Inscription : Juin 2011
    Messages : 258
    Points : 334
    Points
    334
    Par défaut
    Merci pour toutes ces infos, parfait!

    Donc je vais faire quelque chose comme:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    while(condition) {
        if(stream.DataAvailable) {
        	reader.ReadString();
        } else {
        	Thread.sleep(20);
        }
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème d'envoi de fax sous Linux CentOS 5.4 avec AvantFAX et HylaFAX
    Par Kiyoshi_Aiko dans le forum RedHat / CentOS / Fedora
    Réponses: 3
    Dernier message: 03/02/2011, 14h59
  2. Réponses: 3
    Dernier message: 16/09/2010, 20h38
  3. Problème d'envoi / réception de mails avec Exchange
    Par cynoq dans le forum Exchange Server
    Réponses: 0
    Dernier message: 09/07/2010, 17h37
  4. [Toutes versions] Problème d'envoi / réception de mails avec Exchange
    Par cynoq dans le forum Outlook
    Réponses: 0
    Dernier message: 09/07/2010, 11h49
  5. Réponses: 1
    Dernier message: 02/08/2009, 21h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo