2 votes

Messages incomplets (Client TCP/IP C#)

Tout d'abord, je ne suis absolument pas un programmeur réseau. Ce que j'essaie de faire, c'est une communication TCP/IP très simple entre un serveur Java et un client C#.

Serveur Java:

public void run(){   
try {
       // Ouvrir le socket serveur
       _server = new ServerSocket(SERVER_PORT);
       _client = _server.accept();
                System.out.println("ComInterface: client connecté.");
                // Attendre un flux de données client
                while(true){

                    // Recevoir un message du client
                    BufferedReader is =
                            new BufferedReader(new InputStreamReader(_client.getInputStream()));
                    msg = is.readLine();

                    // Traiter le message
                    if(msg!=null){
                        System.out.println("ComInterface: Message Reçu : <" + msg + ">.");
                        processMessage(msg); // Méthode indépendante
                    }
                    else{
                        System.out.println("ComInterface: le client a fermé la connexion.");
                        _client.close();
                        _client = _server.accept();
                        System.out.println("ComInterface: client connecté.");
                    }

                }

            } catch (IOException e) {
                e.printStackTrace();
            }
}

public void sendMessage(String msg){
        try {

            // Flux sortant
            DataOutputStream os = new DataOutputStream(_client.getOutputStream());

            os.writeBytes((String)(msg+"\n"+(char)13));
            os.flush();
            System.out.println("ComInterface: Message <" + msg + "> envoyé");

        } catch (IOException e) {
            // Bloc catch généré automatiquement
            e.printStackTrace();
        }   
    }

Et voici le client C#:

public class ComInterface : MonoBehaviour
    {
        public const String SERVER_IP = "127.0.0.1"; // Localhost
        public const int PORT = 1100; // Port par défaut 
        public const int READ_BUFFER_SIZE = 5000; // 4.8828125 kilo-octets

        private TcpClient _client;
        private ASCIIEncoding _asen;
        private byte[] _readBuffer;
        private String _msg;

        public Boolean connected { get; internal set; } // le setter est destiné à un usage interne uniquement

        /**
         * Initialiser les variables internes (buffer, socket...)
         */
        public ComInterface()
        {
            connected = false;
            _client = new TcpClient();
            _asen = new ASCIIEncoding();
            _readBuffer = new Byte[READ_BUFFER_SIZE];
            _msg = String.Empty;
        }

        /**
         * Se connecter au serveur à SERVER_IP:PORT
         * Renvoie vrai si la connexion a réussi, ou faux en cas d'échec.
         */
        public Boolean Connect()
        {
            try
            {

                _client.Connect(SERVER_IP, PORT);
                connected = true;
                Array.Clear(_readBuffer, 0, _readBuffer.Length);
                Debug.Log("TCPClient:  Connecté au serveur");
                // Démarrer une lecture asynchrone en invoquant ReceiveComMessage
                _client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _client.GetStream());
            }
            catch (Exception ex)
            {
                Debug.Log("TCPClient:  Impossible de se connecter au serveur - " + ex.Message);
                connected = false;
            }
            // Renvoyer l'état de la connexion
            return connected;
        }

/**
         * Recevoir un message de Communicator
         */
        private void ReceiveComMessage(IAsyncResult ar)
        {
            int BytesRead;
            String msg;
            try
            {
                BytesRead = _client.GetStream().EndRead(ar);
                if (BytesRead < 1)
                {
                    // si aucun octet n'a été lu, le serveur est fermé.  
                    Debug.Log("TCPClient:  Le serveur est fermé (BytesRead<1)");
                    this.Disconnect();
                    return;
                }
                // Convertir le tableau d'octets dans lequel le message a été enregistré,
                msg = Encoding.ASCII.GetString(_readBuffer);
                Debug.Log("C# Message: \"" + msg + "\""); // Exemple de sortie dans le journal ci-dessous
                BytesRead = 0;
                Array.Clear(_readBuffer, 0, _readBuffer.Length);

                // Démarrer une nouvelle lecture asynchrone dans readBuffer.
                _client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _readBuffer);

            }
            catch (Exception ex)
            {
                Debug.Log("TCPClient:  Le serveur est fermé (Exception) : " + ex.Message + " voir " + ex.StackTrace);
                this.Disconnect();
            }

Le principal problème est que tous les messages arrivent incomplets. Voici la trace du journal:

C#: Message "{
C#: Message ""sender":"Bob"",
C#: Message ""recipient":",
etc...

Au lieu par exemple de

C#: Message "{"sender":"Bob","recipient":[1,2,3]}"

Je suis un peu perdu et j'aurais besoin d'aide pour résoudre cela. Merci beaucoup!

3voto

Erich Mirabal Points 6029

TCP est une connexion orientée flux, pas orientée message. Il n'a pas de concept de message. Lorsque vous écrivez votre chaîne sérialisée, il ne voit qu'une séquence de octets sans signification. TCP est libre de fragmenter ce flux en plusieurs fragments et ils seront reçus par le client dans ces morceaux de taille fragmentée. Il vous appartient de reconstruire l'ensemble du message à l'autre extrémité.

Dans votre scénario, on enverrait généralement un préfixe de longueur de message. De cette façon, le client lit d'abord le préfixe de longueur pour savoir quelle est censée être la taille du message entrant.

Je recommanderais sérieusement d'utiliser quelque chose comme les Buffers de protocole de Google comme moyen de déclarer vos messages et de les diffuser ensuite avec l'option de préfixe de taille. La bonne chose est que vous définissez votre ensemble de messages une fois, puis utilisez les outils disponibles pour générer automatiquement du code C++, Java, C#, etc. à partir des définitions de message. Cela aidera à avoir un ensemble de messages cohérent qui fonctionne entre les langues.

2voto

Andre Calil Points 5437

Un message (n'importe quelle donnée, en fait), lorsqu'il est envoyé via un socket, est divisé en plusieurs paquets. Lorsque vous imprimez chaque paquet reçu, vous ne voyez pas l'ensemble de votre message.

Vous devriez définir une chaîne de fin de message (quelque chose comme ".#."). Jusqu'à ce que vous receviez cette séquence, vous continuez à concaténer les messages que vous recevez.

C'est ce que font les protocoles de session (c'est-à-dire, les protocoles qui fonctionnent par-dessus TCP).

J'espère que cela vous aide.

Cordialement, Calil

0voto

Theo Points 1247

Jetez un œil à cet exemple...

Serveur TCP Java...

import java.net.*;
import java.io.*;

public class TcpServer
{
    public static void main(String h[])
    {
        try
        {
            ServerSocket serverSocket = new ServerSocket(1100);
            Socket socket = serverSocket.accept();
            System.out.println("Client Accepté");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            System.out.println("Reçu: " + bufferedReader.readLine());
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            printWriter.println("Bonjour Theo. Bienvenue dans la programmation des sockets.");
        } catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

Client TCP C#...

using System;
using System.IO;
using System.Net.Sockets;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var client = new TcpClient("localhost", 1100);
            var stream = client.GetStream();
            var streamWriter = new StreamWriter(stream);
            streamWriter.WriteLine("Je m'appelle Theo");
            streamWriter.Flush();
            var streamReader = new StreamReader(stream);
            Console.WriteLine("Reçu: " + streamReader.ReadLine());
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        Console.WriteLine("Appuyez sur une touche pour continuer.");
        Console.ReadKey();
    }
}

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