Laden...

Double, Byte über TCP in Array auslesen

Erstellt von saku vor 11 Jahren Letzter Beitrag vor 11 Jahren 3.844 Views
S
saku Themenstarter:in
4 Beiträge seit 2012
vor 11 Jahren
Double, Byte über TCP in Array auslesen

Hi Community,

ich habe derzeit ein Programm (nicht zu ändern), welches mir byte und double - Werte über eine TCP-Verbindung schickt.

Diese Daten möchte ich auf der Empfängerseite auslesen und anzeigen lassen.
Solange ich nur einen Datentyp (z.B. Double) habe, hat das bereits auch schon geklappt, indem ich mir ein Empfangsbufferarray definiere und dieses auslese (oder gibt es eine bessere Methode?)

Da bin ich folgendermaßen vorgegangen:


System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;



namespace Empfänger
{
    class TCP_Server
    {
        public static void Main(string[] args)
        {

            int b = 8;
            byte[] buffer = new byte[b]; //Empfangsbuffergröße 8
            
            var TS_IP = IPAddress.Parse("127.0.0.1"); 
            int TS_Port = 5480;

            TcpListener tcpServer = new TcpListener(TS_IP, TS_Port);


            //TCP-Server initialisieren
            tcpServer.Start();

            Console.WriteLine("TCP-Empfänger gestartet auf IP: {0} und Port: {1}", TS_IP, TS_Port);

 
            using (Socket socket = tcpServer.AcceptSocket())
            {
                while (true)
                {
                    if (socket.Available > 0)
                    {
                       
                        socket.Receive(buffer, SocketFlags.None); //Empfangen in Buffer
                       
                         
                            if (BitConverter.IsLittleEndian)
                                Array.Reverse(buffer);

                            double d = BitConverter.ToDouble(buffer, 0);
                            Console.WriteLine("Wert beträgt: {0}  " , d.ToString());
                       
                     
                        Console.WriteLine("Daten empfangen.\n");
                    }
                    else
                        Thread.Sleep(10);
                }
            }


        }

    }
}

Wie gehe ich nun aber vor, wenn ich mehrere Datentypen versende, diese auslesen möchte um sie entsprechend wieder in Double oder Byte-Werte zu dekodieren?

Ich hatte schon was von Marshalling oder Structures gelesen, weiß aber nicht ob mich das wirklich ans Ziel führt.

Angenommen ich wüsste, dass die ersten 8 Byte meines Arrays z.B. mein gewünschter Double-Wert sind, und 2 weitere Bytes, 2 einzelne Byte-Werte. Wie könnte ich denn getrennt auf diese zugreifen?

Viele Grüße

D
216 Beiträge seit 2009
vor 11 Jahren

Du benutzt folgende Hilfsmethoden:

public static double ReadDouble(Stream s) {
    byte[] b = new BinaryReader(s).ReadBytes(8);
    if(BitConverter.IsLittleEndian)
        Array.Reverse(b);
    return BitConverter.ToDouble(b, 0);
}

public static byte ReadByte(Stream s) {
  return new BinaryReader(s).ReadByte();
}

Dann kannst du einfach folgendes machen:

using (TcpClient client = tcpServer.AcceptClient())
using (NetworkStream s = client.GetStream())
{
    double wert1 = ReadDouble(s);
    byte wert2 = ReadByte(s);
    byte wert3 = ReadByte(s);
}

Darth Maim

W
872 Beiträge seit 2005
vor 11 Jahren

Also erstmal zu Deinem besseren Verstaendnis:
Marshalling wuerde ich grundsaetzlich versuchen zu vermeiden, da Du dann nicht den Debugger benutzen kannst und auch manuell den Speicher verwalten muesstest. Der Name "unsafe" Code kommt nicht von ungefaehr.
Du machst es schon richtig, dass Du einen Buffer ausliest und dann mit Read darauf zugreist, dabei ist es je nach Struktur dann moeglich, dass Du mit einem Offset arbeitest.
Was aus Deinem Text nicht hervorgeht, ist mehr die Frage, wie dynamisch die Struktur sind und wie Du die Laenge des Buffers festlegst.
Ich hoffe doch, dass Du keine Unions hast...

S
saku Themenstarter:in
4 Beiträge seit 2012
vor 11 Jahren

Vielen Dank für eure Antworten.

Das mit dem Auslesen klappt mittlerweile sehr gut.

.
Was aus Deinem Text nicht hervorgeht, ist mehr die Frage, wie dynamisch die Struktur sind und wie Du die Laenge des Buffers festlegst.
Ich hoffe doch, dass Du keine Unions hast...

Derzeit ist mir noch bekannt, wie groß der Buffer sein muss, da ich genau weiß welche/wieviele Werte ich empfange. Da dies in Zukunft aber sicherlich nicht mehr so offensichtlich sein wird, frage ich mich auch ob man die Buffergröße evtl. variabel anpassen kann. (z.B. so, dass mein Programm als erstes die Anzahl der kommenden Daten sendet und ich diese Info dafür nutze um den Buffer sozusagen in "Echtzeit" anzupassen - meint ihr dass das möglich ist?)

EDIT

Hab es jetzt mal folgendermaßen versucht. Der erste Eintrag des Arrays config ist dabei die Paketgröße, welche gesendet wird.



           
            byte[] config = new byte[1]; //Array um Empfangsbuffergröße zu erhalten
            int b = config[0];
            byte[] buffer = new byte[b];//Empfangsbuffergröße festlegen
            var TS_IP = IPAddress.Parse("127.0.0.1");  // Einstellen der TS Client-IP.
            int TS_Port = 5480;

            TcpListener tcpServer = new TcpListener(TS_IP, TS_Port); //TCP-Empfangen auf IP: "TS_IP" Port: "TS_Port"

            //TCP-Server initialisieren
            tcpServer.Start();

            Console.WriteLine("TCP-Empfänger gestartet auf IP: {0} und Port: {1}", TS_IP, TS_Port);



            using (Socket socket = tcpServer.AcceptSocket())
            {
                while (true)
                {
                    if (socket.Available > 0)
                    {
                        socket.Receive(config);
                        socket.Receive(buffer, SocketFlags.None); //Empfangen in Buffer

                        

                        //Konvertierungen über BitConverter und Ausgabe
                        byte i1 = buffer[0];//hier gibt er mir eine Fehlermeldung aus
                        byte i2 = buffer[1];
                        Console.WriteLine("Packetgröße beträgt : {0}", i1.ToString());
                        Console.WriteLine("Wert 2 : {0}", i2.ToString());
                        double d1 = BitConverter.ToDouble(buffer, 2);
                        Console.WriteLine("Wert 3 beträgt: {0}  ",d1.ToString());
                        double d2 = BitConverter.ToDouble(buffer, 10);
                        Console.WriteLine("Wert 4 {0} ", d2.ToString());
                      
                  

]

Jetzt gibt er mir hier :

byte i1 = buffer[0];

allerdings schon den Fehler aus "Der Index war außerhalb des Arraybereichs.". Hab ich irgendwo einen Denkfehler drin?

Im Prinzip sende ich derzeit ein Paket mit 18 byte. Mein erstes Array config dient dazu die Paketgröße auszulesen( da ich weiß, dass IMMER als erstes die Paketgröße gesendet wird), und mein zweites Array buffer dient dann dazu wirklich alles zu empfangen. Das heisst ich will über mein Config Array meinem Buffer-Array die Größe vorschreiben.

16.842 Beiträge seit 2008
vor 11 Jahren

sozusagen in "Echtzeit" anzupassen - meint ihr dass das möglich ist?)

Grob gesagt arbeiten die meisten Protokolle auf diese Art und Weise. Weit hergeholt zum Beispiel TCP.
So hat das TCP-Paket ein Nutzungsraum von maximal 1500 Byte. Diese Größe ist so gewählt, dass sie in den 64kB Rahmen eines IP-Pakets passt. Flaschenhals ist hier aber nicht das IP-Paket sondern Ethernet, das Standardmäßig 1500 Bytes unterstützt ( um mal hier auf die Rahmengrößen kurz einzugehen ).
Die absolute Größe des zu übertragenen Contents wird im Header mitgeteilt - eben dynamisch.

Wenn Du Dir Dein eigenes Protokoll bauen willst, so musst Du auf solche Feinheiten achten und eventuell evaluieren, ob eine bestehende, standardisierte Übertragungsmöglichkeit nicht besser und einfach umzusetzen wäre.

Edit:

"Der Index war außerhalb des Arraybereichs.". Hab ich irgendwo einen Denkfehler drin?

IndexOutOfRangeException-Klasse
Der Debugger, der in Visual Studio enthalten ist, wird Dir hierbei sicherlich behilflich sein.

S
saku Themenstarter:in
4 Beiträge seit 2012
vor 11 Jahren

Ich wollte mich nochmal zurückmelden 😃

Derzeit versuche ich von C# aus byte und doubles über Socket.Send zu versenden.

Das Ganze klappt soweit auch ganz gut, mit doubles hab ich jedoch noch Probleme. Ich denke dass ich meinen Double-Wert in BigEndian konvertieren müsste und wollte daher eine Reverse-Funktion einbauen.

       namespace Empfänger
{
    class TCP_Server
    {
        public static void Main(string[] args)
        {

......................

                        int c = 10;
                        byte[] senden = new byte[c];
                        sbyte s1 = -126;
                        byte b1 = (byte)s1;
                        byte b2 = 120;
                        double d3 = 17.234;

        
                        senden[0] = b1;
                        senden[1] = b2;
                        senden[2] = ReverseBytes(System.Convert.ToByte(d3));
                        
                     

                        socket.Send(senden);

dafür wollte ich nun folgendes noch integrieren:

   private static byte[] ReverseBytes(byte[] inArray)
   {
      byte temp;
      int highCtr = inArray.Length - 1;

      for (int ctr = 0; ctr < inArray.Length / 2; ctr++)
      {
         temp = inArray[ctr];
         inArray[ctr] = inArray[highCtr];
         inArray[highCtr] = temp;
         highCtr -= 1;
      }
      return inArray;
   }

jedoch bekomme ich wenn ich diesen Quellcode noch in meinen ursprünglichen integriere tausende Fehlermeldungen. Woran liegt das? Sind sicherlich absolute Grundlagen, konnte es aber noch nicht herausfinden.

Viele Grüße

185 Beiträge seit 2005
vor 11 Jahren

warum nimmst du nicht einfach den BitConverter?

Eventuell dann noch Array.Reverse()

Damit sollte es doch funktionieren.

Gruß

W
872 Beiträge seit 2005
vor 11 Jahren

Du findest in dieser Bibliothek einen BitConverter, bei dem Du Little oder BigEndian einstellen kannst - eleganter als Array.Reverse().

S
saku Themenstarter:in
4 Beiträge seit 2012
vor 11 Jahren

Vielen Dank für eure Hilfe. Lag jetzt letztendlich doch nicht an der Little/Big-Endian-Ausrichtung. Hab es vorerst mal so gelöst:

 //Variablen und Arrays zum Senden definieren
                        int c = 2;
                        int c1 = 8;
                       
                        byte[] senden = new byte[c];
                        byte[] senden1 = new byte[c1];
               
                        sbyte s1 = -126;
                        byte b1 = (byte)s1;
                        byte b2 = 120;
                        double d3 = 17.234;
                        

                        //Array befüllen
                        senden[0] = b1;
                        senden[1] = b2;
                        senden1 = BitConverter.GetBytes(d3);
                 
                        
                        
                        //Daten senden
                        socket.Send(senden);
                        socket.Send(senden1);
               

Funktioniert soweit auch ganz gut, nur nehme mit der BitConverter.GetBytes Methode in Kauf, dass ich für Double-Werte ein zusätzliches Array senden muss, da man mit der GetBytes Methode ein Array erhält und dieses nicht in das ursprüngliche Array (in meinem Fall) "senden" schreiben kann. Habt ihr dafür vielleicht eine elegantere Lösung?

W
872 Beiträge seit 2005
vor 11 Jahren

Einfacher ist in so einem Fall, dass Du einen MemoryStream nimmst und den dann am Ende in ein ByteArray umwandelst oder dass Du mit Buffer.BlockCopy aehnlich dem memcpy von C die Bytes hin-und her schiebst