2009-08-12 6 views
1

Hey, j'ai un problème séparant les paquets en utilisant un protocole binaire personnalisé. Actuellement, le code côté serveur ressemble à ceci.TCP encadrant avec le protocole binaire

public void HandleConnection(object state) 
    { 
     TcpClient client = threadListener.AcceptTcpClient(); 
     NetworkStream stream = client.GetStream(); 
     byte[] data = new byte[4096]; 

     while (true) 
     { 
      int recvCount = stream.Read(data, 0, data.Length); 
      if (recvCount == 0) break; 
      LogManager.Debug(Utility.ToHexDump(data, 0, recvCount)); 
      //processPacket(new MemoryStream(data, 0, recvCount)); 
     } 
     LogManager.Debug("Client disconnected"); 
     client.Close(); 
     Dispose(); 
    } 

Je regarde les décharges hexadécimaux des paquets, et parfois tout le paquet vient d'un seul coup, disons que les 20 octets. D'autres fois, il est fragmenté, comment dois-je tamponner ces données pour pouvoir le passer correctement à ma méthode processPacket(). J'essaye d'employer seulement un en-tête d'opcode d'octet simple, dois-je ajouter quelque chose comme un (ushort) contentLength à l'en-tête aussi? J'essaye de rendre le protocole aussi léger que possible, et ce système n'enverra pas de très gros paquets (< 128 octets).

Le code côté client que je suis en train de tester est le suivant.

public void auth(string user, string password) 
    { 
     using (TcpClient client = new TcpClient()) 
     { 
      client.Connect(IPAddress.Parse("127.0.0.1"), 9032); 
      NetworkStream networkStream = client.GetStream(); 

      using (BinaryWriter writer = new BinaryWriter(networkStream)) 
      { 
       writer.Write((byte)0); //opcode 
       writer.Write(user.ToUpper()); 
       writer.Write(password.ToUpper()); 
       writer.Write(SanitizationMgr.Verify()); //App hash 
       writer.Write(Program.Seed); 
      } 
     } 
    } 

Je ne sais pas si cela pourrait être ce déconner vers le haut, et le protocole binaire ne semble pas avoir beaucoup d'informations sur le web, en particulier lorsque C# est impliqué. Tout commentaire serait utile. =)

Résolu avec ceci, je ne sais pas si c'est correct, mais il semble donner à mes gestionnaires juste ce dont ils ont besoin.

public void HandleConnection(object state) 
    { 
     TcpClient client = threadListener.AcceptTcpClient(); 
     NetworkStream stream = client.GetStream(); 
     byte[] data = new byte[1024]; 

     uint contentLength = 0; 
     var packet = new MemoryStream(); 
     while (true) 
     { 
      int recvCount = stream.Read(data, 0, data.Length); 
      if (recvCount == 0) break; 

      if (contentLength == 0 && recvCount < headerSize) 
      { 
       LogManager.Error("Got incomplete header!"); 
       Dispose(); 
      } 

      if(contentLength == 0) //Get the payload length 
       contentLength = BitConverter.ToUInt16(data, 1); 

      packet.Write(data, (int) packet.Position, recvCount); //Buffer the data we got into our MemStream 
      if (packet.Length < contentLength + headerSize) //if it's not enough, continue trying to read 
       continue; 

      //We have a full packet, pass it on 
      //LogManager.Debug(Utility.ToHexDump(packet)); 
      processPacket(packet); 

      //reset for next packet 
      contentLength = 0; 
      packet = new MemoryStream(); 
     } 
     LogManager.Debug("Client disconnected"); 
     client.Close(); 
     Dispose(); 
    } 

Répondre

3

Vous devriez juste le traiter comme un flux. Ne comptez pas sur un comportement de segmentation particulier.

La quantité de données dont vous avez besoin est-elle toujours la même? Si ce n'est pas le cas, vous devriez changer le protocole (si vous le pouvez) pour préfixer le "morceau" logique de données avec la longueur en octets.

Dans ce cas, vous utilisez BinaryWriter d'un côté, afin de fixer un BinaryReader au NetworkStream retourné par TcpClient.GetStream() semble comme approche la plus facile. Si vous voulez vraiment capturer toutes les données pour un morceau à la fois, vous devriez revenir à mon idée de préfixer les données avec sa longueur. Puis boucle juste jusqu'à ce que vous ayez toutes les données.

(Assurez-vous que vous avez suffisamment de données pour lire la longueur si! Si le préfixe de votre longueur est de 4 octets, vous ne voulez pas lire 2 octets et manquer le 2 prochain ...)

+0

Hmm, Je vais ajouter un contentLength à l'en-tête, merci. J'utilise un lecteur binaire dans processPacket(), mais il est évident que cela échoue horriblement quand il est seulement passé un paquet partiel. – Endian

+0

@Endian: Pourquoi ne pas simplement passer le flux réseau au lieu d'un paquet? –

+0

@Jon: Paquet plus dans le sens d'un morceau que mon binaryReader peut lire sans obtenir un buffer insuffisant. J'ai modifié le post original avec ce que je fais maintenant, après votre suggestion et cela semble fonctionner, merci pour l'aide. :) – Endian

Questions connexes