2010-02-21 7 views
3

J'ai un programme de réseau client/serveur C# que j'ai écrit en utilisant les classes TCPListener et TCPClient de . Le serveur lit tout du client (de petites quantités de xml) juste bien jusqu'à ce que j'essaie d'envoyer un gros fichier au client.Problème de socket newbie .NET

J'utilise des fonctions de flux pour à la fois client et serveur avec des fonctions de socket non bloquantes. Lorsque je fais un socket.SendFile ("nom de fichier") de nouveau au client, le fichier obtient coupé - J'ai réglé la taille du tampon de réception sur le client à passé 10000 mais il est encore coupé autour de 25k et la communication entre le client et le serveur n'est pas fiable après.

Ma question de base est ce qui se passe si des données sont en quelque sorte laissées dans le tuyau? c'est-à-dire lu par le socket suivant.Lire ... Chaque appel Send nécessite-t-il exactement un et un seul Read? Peut-être que je ne donne pas assez de temps au client pour lire le fichier mais les deux sur la même machine et j'ai essayé de dormir pendant quelques secondes dans divers endroits sans succès.

+3

Line- les pauses rendent le texte plus facile à lire. –

+1

Pouvez-vous fournir le code pour le client et le serveur? – Ikaso

+1

Si vous le pouvez, postez du code. Il est vraiment difficile de répondre à votre question sans voir ce que vous avez mis en place. – driis

Répondre

0

Essayez d'envoyer le tronçon du côté serveur en segments. Tout comme l'autre a dit, l'affichage du code nous serait d'une grande aide.

2

Il est très possible que vous ne puissiez pas lire le message entier via un appel Read (peut-être que toutes les données ne sont pas encore arrivées). Dans la programmation réseau, vous placeriez souvent l'appel à Read dans une boucle while et simplement Read() jusqu'à ce que vous ayez reçu le message entier attendu.

+2

Ajouter un en-tête spécifiant la longueur du message entrant: 'while (readBuffer IAbstract

+0

Ou, si vous voulez juste lire toutes les données envoyées par connexion, utilisez le code http: // stackoverflow .com/questions/2582947/c-performance-methods-of-reception-data-from-a-socket/5504052 # 5504052 – Pat

1

Vous voulez probablement quelque chose comme ceci:

socket.Blocking = false; 


const int ChunkSize = 1492; 
const int ReceiveTimeout = 10000; 
const int SendTimeout = 10000; 

public void Send(Byte[] data) 
{ 
    var sizeBytes = BitConverter.GetBytes(data.Length); 
    SendInternal(sizeBytes); 
    SendInternal(data); 
} 

public Byte[] Receive() 
{ 
    var sizeBytes = ReceiveInternal(4); 
    var size = BitConverter.ToInt32(sizeBytes, 0); 
    var data = ReceiveInternal(size); 
    return data; 
} 

private void SendInternal(Byte[] data) 
{ 
    var error = SocketError.Success; 
    var lastUpdate = Environment.TickCount; 
    var size = data.Length; 
    var count = 0; 
    var sent = 0; 

    while (sent < size) 
    { 
     count = Math.Min(ChunkSize, size - sent); 
     count = socket.Send(data, sent, count, SocketFlags.None, out error); 

     if (count > 0) 
     { 
      sent += count; 
      lastUpdate = Environment.TickCount; 
     } 

     if (error != SocketError.InProgress && error != SocketError.Success && error != SocketError.WouldBlock) 
      throw new SocketException((Int32)error); 
     if (Environment.TickCount - lastUpdate > SendTimeout) 
      throw new TimeoutException("Send operation timed out."); 
     if (count == 0 && !socket.Poll(100, SelectMode.SelectWrite)) 
      throw new SocketException((Int32)SocketError.Shutdown); 
    } 
} 

private Byte[] ReceiveInternal(Int32 size) 
{ 
    var error = SocketError.Success; 
    var lastUpdate = Environment.TickCount; 
    var buffer = new Byte[ChunkSize]; 
    var count = 0; 
    var received = 0; 

    using (var ms = new MemoryStream(size)) 
    { 
     while (received < size) 
     { 
      count = Math.Min(ChunkSize, size - received); 
      count = socket.Receive(buffer, 0, count, SocketFlags.None, out error); 

      if (count > 0) 
      { 
       ms.Write(buffer, 0, count); 
       received += count; 
       lastUpdate = Environment.TickCount; 
      } 

      if (error != SocketError.InProgress && error != SocketError.Success && error != SocketError.WouldBlock) 
       throw new SocketException((Int32)error); 
      if (Environment.TickCount - lastUpdate > ReceiveTimeout) 
       throw new TimeoutException("Receive operation timed out."); 
      if (count == 0 && socket.Poll(100, SelectMode.SelectRead) && socket.Available == 0) 
       throw new SocketException((Int32)SocketError.Shutdown); 
     } 

     return ms.ToArray(); 
    } 
} 
2

1 appel Envoyer pourrait prendre plus d'un appel Lire pour recevoir et 1 appel de lecture peut lire les données envoyées par plusieurs appels d'envoi. TCP fournit simplement un flux, c'est donc à vous de définir comment les données ou les messages que vous envoyez sont partitionnés.

Dans ce cas, vous avez probablement besoin de boucler, en faisant Read jusqu'à la fermeture du flux.

1

Ce que je généralement faire est de créer une structure d'en-tête qui est envoyé

Header Size (int, 4 bytes) 
File Name Offset (int, 4 bytes) 
File Name Size (int , 4 bytes) 
File Data Offset (int, 4 bytes) 
File Data Size (int , 4 bytes) 
[ message data here] 

puis cet en-tête est lu en utilisant soit un BinaryReader ou copier les octets à un struct en utilisant le maréchal. De cette façon, vous savez toujours quelles données arrivent et combien de fois vous devez appeler Read(). Le champ de taille d'en-tête permet également de gérer le protocole (conservez la même structure mais ajoutez-la pour les clients ultérieurs afin de conserver la rétrocompatibilité). Si vous définissez la structure en C# assurez-vous de le faire comme ceci:

[StructLayout LayoutKind.Sequential] 
struct MessageHeader 
{ 
    public int HeaderSize; 
    public int FileNameOffset; 
    public int FileNameSize; 
    public int FileDataOffset; 
    public int FileDataSize; 
} 

Alors Marshal.PtrToStructure vous permettra faire créer une instance de cette structure droite de l'octet [] vous lisez de la prise