2016-09-13 1 views
0

Premier arrière-plan. J'ai un appareil photo auquel mon application est connectée via un câble Ethernet et il effectue des opérations en fonction des commandes que j'envoie et répond avec des commandes.Traitement rapide des paquets à partir du flux

Si je prends la commande de déclenchement de caméra comme exemple. La caméra prendra une photo si je l'envoie 'T1'. Toutes les commandes utilisent un début et la fin de carbonisation pour marquer le début et la fin du paquet si le paquet complet j'envoie ressemblera à ceci

(char) 0x02T1 (char) 0x03

avec (char) 0x02 comme le début du paquet et (char) 0x03 comme la fin du paquet.

L'appareil photo prendra alors une photo et enverra la même commande pour indiquer que c'est fait. Vous pouvez également configurer l'appareil photo pour renvoyer certaines données lorsqu'il a pris la photo. Dans mon cas, j'ai l'appareil photo inspecter quelques zones et je veux des valeurs de l'inspection.

Ainsi, le retour de la caméra serait quelque chose comme « T1 », puis dire « 1,1,500,20,300 »

Sur le problème. J'utilise un TcpClient et un NetworkStream pour la communication mais j'ai du mal à traiter les paquets du côté réception. J'ai essayé différentes méthodes mais elles semblent lentes ou ne garantissent pas que je reçois toutes les données.

Premièrement, j'ai besoin de quelque chose qui va lire toutes les données et m'assurer d'avoir toutes les données.

Deuxièmement, j'ai besoin de quelque chose qui peut traiter les données et les diviser en paquets et le faire aussi vite que possible.

Ceci est juste une méthode que j'ai essayé et le crédit total va à la personne qui a fait le StreamReaderExtensions. Je suis sûr que je l'ai trouvé quelque part ici.

internal static class StreamReaderExtensions 
{ 
    public static IEnumerable<string> ReadUntil(this StreamReader reader, string delimiter) 
    { 
     List<char> buffer = new List<char>(); 
     CircularBuffer<char> delim_buffer = new CircularBuffer<char>(delimiter.Length); 

     while (reader.Peek() >= 0) 
     { 
      char c = (char)reader.Read(); 
      delim_buffer.Enqueue(c); 
      if (delim_buffer.ToString() == delimiter) 
      { 
       if (buffer.Count > 0) 
       { 
        yield return new String(buffer.ToArray()); 
        buffer.Clear(); 
       } 
       continue; 
      } 
      buffer.Add(c); 
     } 
    } 

    private class CircularBuffer<T> : Queue<T> 
    { 
     private int _capacity; 

     public CircularBuffer(int capacity) 
      : base(capacity) 
     { 
      _capacity = capacity; 
     } 

     new public void Enqueue(T item) 
     { 
      if (base.Count == _capacity) 
      { 
       base.Dequeue(); 
      } 
      base.Enqueue(item); 
     } 

     public override string ToString() 
     { 
      List<String> items = new List<string>(); 
      foreach (var x in this) 
      { 
       items.Add(x.ToString()); 
      }; 
      return String.Join("", items); 
     } 
    } 
} 

et ma méthode de traitement. _stream est TcpClient.GetStream()

public static class Constants 
{ 
    public const char StartOfPacket = (char)0x02; 
    public const char EndOfPacket = (char)0x03; 
} 

private IEnumerable<string> ProcessResponse() 
{ 
    var streamReader = new StreamReader(_stream); 
    var packets = streamReader.ReadUntil(new string(new char[] {Constants.EndOfPacket})); 
    return packets; 
} 
+0

Vous devriez lire par caractère. Il suffit de lire un gros bloc et de vérifier la taille reçue. Analysez le bloc jusqu'à ce que vous trouviez le délimiteur et supprimez le bloc analysé. Répétez jusqu'à ce que vous ayez des demi-paquets. –

+0

@JeroenvanLangen demi-paquets? – Gaz83

+0

un message pourrait être divisé en plusieurs reçus/paquets. C'est parce que c'est un flux au lieu d'un datagramme. Si vous recevez seulement 1 caractère à la fois, il est toujours splitup dans plusieurs reçoit, –

Répondre

0

Je fis un code pseudo pour, je ne l'ai pas testé, mais il peut vous donner une direction. Cela ne peut pas être statique

char[] _buffer = new char[4 * 1024 * 1024]; 
int currentPosition; 

public bool ReadNext(StreamReader reader, char delimiter, out string value) 
{ 
    // read as many chars from the stream. 
    int charsRead = reader.ReadBlock(_buffer, currentPosition, _buffer.Length - currentPosition); 

    // keep track of the current position. 
    currentPosition += charsRead; 

    // find the first delimiter. 
    for (int i = 0; i < currentPosition; i++) 
    { 
     if (_buffer[i] == delimiter) 
     { 
      // a complete packet was found, copy it to a string. 
      value = new string(_buffer, 0, i); 
      // move the rest of the data back to the start of the buffer. 
      Array.Copy(_buffer, i, _buffer, 0, _buffer.Length - i); 
      // decrease the current position also. 
      currentPosition -= i; 
      // return true because you have data. 
      return true; 
     } 
    } 

    // no complete packet was found. 
    value = String.Empty; 
    return false; 
} 

Utilisation:

string data; 

while(true) 
{ 
    if(ReadNext(reader, (char)0x03, out data)) 
     MessageBox.Show(data); 
} 

Comme je l'ai dit non testé, c'est juste un brainfart ... Vous devrez peut-être ajouter le chèque de le début du paquet.

+0

Merci pour cela. Je vais lui donner un bon coup d'oeil et l'essayer. – Gaz83

+0

Je viens de passer à l'essai et pour une raison quelconque, je reçois un "System.IO.Exception "lors de l'exécution du' ReadBlock' L'exception interne indique "Une tentative de connexion a échoué parce que la partie connectée n'a pas répondu correctement après une période de temps ....." Écrire dans le même flux fonctionne très bien – Gaz83

+0

bloqué par un pare-feu? (de l'autre côté) –