2011-03-12 3 views
2

J'ai un programme client-serveur.Données non reçues correctement par TcpClient

J'envoie des données comme ceci:

private void Sender(string s,TcpClient sock) 
{ 
    try 
    { 
     byte[] buffer = Encoding.UTF8.GetBytes(s); 
     sock.Client.Send(buffer); 
    }catch{} 
} 

et du côté client recevant comme ceci:

byte[] buffer = new byte[PacketSize]; 
int size = client.Client.Receive(buffer); 
String request = Encoding.UTF8.GetString(buffer, 0, size); 

Le problème est que les données ne sont pas entièrement reçu toujours, il est parfois seulement une partie de ce que j'ai envoyé. PacketSize est 10240 ce qui est plus que les octets que j'envoie. J'ai également défini SendBufferSize et ReceiveBufferSize des deux côtés.

Le pire, c'est que parfois les données sont entièrement reçues!

Quel pourrait être le problème?

Répondre

7

La valeur size renvoyée par TcpClient.Receive n'est pas la même que la longueur de la chaîne mise en mémoire tampon que vous avez envoyée. C'est parce qu'il n'y a aucune garantie que lorsque vous appelez Receive une fois que vous récupérerez toutes les données que vous avez envoyées avec l'appel Send. Ce comportement est intrinsèque au fonctionnement de TCP (c'est un protocole de données basé sur un flux, et non sur un message).

Vous ne pouvez pas résoudre le problème en utilisant des tampons plus gros, comme les tampons peuvent vous fournir seulement limite la quantité de données que Receive rendements. Même si vous fournissez un tampon de 1 Mo et qu'il y a 1 Mo de données à lire, Receive peut légitimement retourner n'importe quel nombre d'octets (même juste 1).

Ce que vous devez faire est de vous assurer que vous avez tamponné toutes les données avant d'appeler Encoding.GetString. Pour ce faire, vous devez savoir combien de données il y a en premier lieu. Donc, à tout le moins, vous devez écrire la longueur des octets de chaîne lors de l'envoi:

byte[] buffer = Encoding.UTF8.GetBytes(s); 
    byte[] length = BitConverter.GetBytes(buffer.Length); 
    sock.Client.Send(length); 
    sock.Client.Send(buffer); 

Lors de la réception, vous devrez d'abord lire la longueur (qui a une taille fixe connue: 4 octets), puis commencer en mémoire tampon le reste des données dans un tampon temporaire jusqu'à ce que vous ayez length octets (cela peut prendre un nombre quelconque d'appels Receive, vous aurez donc besoin d'une boucle while). C'est seulement alors que vous pouvez appeler Encoding.GetString et récupérer votre chaîne d'origine.

Explication du comportement que vous observez:

Même si la pile réseau du système d'exploitation fait à peu près aucune garantie, dans la pratique il vous donnera généralement les données d'un paquet TCP Apporte avec un Receive appel . Étant donné que le MTU (taille de paquet maximale) pour TCP autorise environ 1500 octets pour la charge utile, le code naïf fonctionnera correctement tant que les chaînes sont inférieures à cette taille. Plus que cela et il sera divisé en plusieurs paquets, et un Receive retournera seulement une partie des données.

+1

+1. Et n'oubliez pas d'utiliser un timeout sur le client/flux tcp, sinon votre application va se bloquer si l'application qui envoie les données se comporte mal/a des problèmes de réseau. – stefan

+0

@stefan: Il serait encore mieux d'utiliser les méthodes 'Async' à la place. Mais commençons par les notions de base :) – Jon

+0

@Jon il peut être préférable d'utiliser Async oui, mais cela dépend vraiment de l'application. – stefan

Questions connexes