2010-03-08 11 views
0

Je souhaite utiliser TcpClient et TcpListener pour envoyer un fichier mp3 sur un réseau. J'ai implémenté une solution à l'aide de sockets, mais il y a eu quelques problèmes, donc j'étudie une nouvelle/meilleure façon d'envoyer un fichier.Tableau d'octets bousillés après l'utilisation de TcpClient et TcpListener

créer un tableau d'octets qui ressemble à ceci: length_of_filename | nom | fichier

Cela devrait ensuite être transmis en utilisant les classes mentionnées ci-dessus, mais sur le côté serveur du tableau d'octets que je lis est complètement foiré et Je ne suis pas sûr pourquoi.

La méthode que j'utilise pour envoyer:

public static void Send(String filePath) 
    { 
     try 
     { 
      IPEndPoint endPoint = new IPEndPoint(Settings.IpAddress, Settings.Port + 1); 
      Byte[] fileData = File.ReadAllBytes(filePath); 
      FileInfo fi = new FileInfo(filePath); 

      List<byte> dataToSend = new List<byte>(); 
      dataToSend.AddRange(BitConverter.GetBytes(Encoding.Unicode.GetByteCount(fi.Name))); // length of filename 
      dataToSend.AddRange(Encoding.Unicode.GetBytes(fi.Name)); // filename 
      dataToSend.AddRange(fileData); // file binary data 


      using (TcpClient client = new TcpClient()) 
      { 
       client.Connect(Settings.IpAddress, Settings.Port + 1); 

       // Get a client stream for reading and writing. 
       using (NetworkStream stream = client.GetStream()) 
       { 
        // server is ready 
        stream.Write(dataToSend.ToArray(), 0, dataToSend.ToArray().Length); 
       } 
      } 

     } 
     catch (ArgumentNullException e) 
     { 
      Debug.WriteLine(e); 
     } 
     catch (SocketException e) 
     { 
      Debug.WriteLine(e); 
     } 
    } 
} 

Ensuite, du côté du serveur, il se présente comme suit:

private void Listen() 
    { 
     TcpListener server = null; 
     try 
     { 
      // Setup the TcpListener 
      Int32 port = Settings.Port + 1; 
      IPAddress localAddr = IPAddress.Parse("127.0.0.1"); 

      // TcpListener server = new TcpListener(port); 
      server = new TcpListener(localAddr, port); 

      // Start listening for client requests. 
      server.Start(); 

      // Buffer for reading data 
      Byte[] bytes = new Byte[1024]; 
      List<byte> data; 

      // Enter the listening loop. 
      while (true) 
      { 
       Debug.WriteLine("Waiting for a connection... "); 
       string filePath = string.Empty; 

       // Perform a blocking call to accept requests. 
       // You could also user server.AcceptSocket() here. 
       using (TcpClient client = server.AcceptTcpClient()) 
       { 
        Debug.WriteLine("Connected to client!"); 
        data = new List<byte>(); 

        // Get a stream object for reading and writing 
        using (NetworkStream stream = client.GetStream()) 
        { 
         // Loop to receive all the data sent by the client. 
         while ((stream.Read(bytes, 0, bytes.Length)) != 0) 
         { 
          data.AddRange(bytes); 
         } 
        } 
       } 

       int fileNameLength = BitConverter.ToInt32(data.ToArray(), 0); 
       filePath = Encoding.Unicode.GetString(data.ToArray(), 4, fileNameLength); 
       var binary = data.GetRange(4 + fileNameLength, data.Count - 4 - fileNameLength); 

       Debug.WriteLine("File successfully downloaded!"); 

       // write it to disk 
       using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Append))) 
       { 
        writer.Write(binary.ToArray(), 0, binary.Count); 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex); 
     } 
     finally 
     { 
      // Stop listening for new clients. 
      server.Stop(); 
     } 
    } 

Quelqu'un peut-il voir quelque chose que je manque/faire le mal?

Répondre

4

La corruption est causée par le code suivant sur le serveur:

// Loop to receive all the data sent by the client. 
while ((stream.Read(bytes, 0, bytes.Length)) != 0) 
{ 
    data.AddRange(bytes); 
} 

stream.Read rempliront pas toujours le tampon bytes. Il ne sera pas rempli si le socket TCP n'a plus de données disponibles, ou lors de la lecture du dernier segment du message (sauf s'il s'agit d'un multiple exact de la taille de la mémoire tampon). L'appel data.AddRange ajoute tout de bytes (en supposant qu'il est toujours plein). Par conséquent, cela finira parfois par ajouter des données de l'appel précédent au stream.Read. Pour y remédier, vous devez stocker le nombre d'octets renvoyés par Read et ajouter que ce nombre d'octets:

int length; 

while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) 
{ 
    var copy = new byte[length]; 
    Array.Copy(bytes, 0, copy, 0, length); 
    data.AddRange(copy); 
} 

Notez que vous pouvez restructurer votre code pour améliorer l'utilisation de la performance et de la mémoire (et faire probablement plus facile à lire en conséquence). Au lieu de lire toutes les données en mémoire sur le client avant d'envoyer, vous pouvez simplement écrire directement sur le NetworkStream. Sur le serveur, vous n'avez pas besoin de copier tout le contenu du flux dans la mémoire. Vous pouvez lire la longueur du nom de fichier 4 octets et le décoder, puis lire et décoder le nom du fichier et enfin copier le reste du flux directement à un FileStream (le BinaryWriter est inutile).

Il est également important de noter que vous créez le fichier de sortie avec FileMode.Append. Cela signifie que chaque fichier envoyé sera ajouté à la copie précédente du même nom. Vous souhaiterez peut-être utiliser FileMode.Create à la place, qui écrase si le fichier existe déjà.

+0

Le code suivant pourrait-il donner le même résultat? while ((stream.Read (octets, 0, octets.length))! = 0) { data.AddRange (octets); bytes = nouvel octet [bytes.Length]; } Pour que "octets" soit réinitialisé dans chaque boucle, aucune donnée précédente n'est conservée. –

+0

@J Pollack Ce code souffrira d'un problème similaire au code d'origine. La seule différence est que lorsque 'bytes' n'a pas été rempli par' stream.Read', l'appel à 'data.AddRange' ajoutera des zéros supplémentaires au lieu des données d'un appel' stream.Read' plus ancien. –

+0

Ross vous avez absolument raison. J'ai espéré que la partie vide du dernier tampon partiellement rempli n'est pas ajoutée par data.AddRange (bytes). C'était évidemment une pensée trop optimiste.Merci d'expliquer. Si vous traitez avec des chaînes, il peut être traité avec easy: data + = Encoding.UTF8.GetString (octets, 0, longueur); –