none
Wie sende/empfange ich eine Datenstruktur per tcpclient/Networkstream? RRS feed

  • Frage

  • Hallo, ich habe mal wieder ein grundsätzliches Problem - stehe das erste Mal vor dieser Aufgabe.

    Ich möchte eine Datenstruktur über das Netzwerk (TcpClient) versenden und auf den anderen Seite empfangen.

    Hier die Struktur und ein Prinzip-Beispielprogramm:
    Module Test
        Dim DOCCM_IP As String = "127.0.0.1"
        Dim DOCCM_Port As Integer = 50000
    
        Structure stDOCCMAgent
            Dim UID As String
            Dim Firma As enFirma
            Dim Clan As String
            Dim Name As String
            Dim Level As Integer
            Dim Pos As stZiel
        End Structure
    
        Structure stDOCCMShips
            Dim UID As String
            Dim Clan As String
            Dim Name As String
            Dim Firma As enFirma
            Dim XPos, YPos As Integer
            Dim LastUpdate As Date
        End Structure
    
        Structure stDOCCMGate
            Dim Art As enGateArt 'Aussehen des Gates
            Dim Name As String 'Name eines Gates
            Dim X As Integer 'X-Position des Gates
            Dim Y As Integer 'Y-Position des Gates
        End Structure
    
        Structure stDOCCMStation 'Dates einer Station
            Dim Art As enGateArt 'Aussehen der Station
            Dim Name As String 'Name einer Station
            Dim X As Integer 'X-Position der Station
            Dim Y As Integer 'Y-Position der Station
        End Structure
    
    
        Structure stDOCCMMap
            Dim Name As String
            Dim Gates As List(Of stDOCCMGate)
            Dim Stations As List(Of stDOCCMStation)
        End Structure
    
        Structure stDOCCM
            Dim Map As stDOCCMMap
            Dim Agent As stDOCCMAgent
            Dim Ships As Dictionary(Of String, stDOCCMShips)
        End Structure
    
        Dim DOCCM As stDOCCM
    
    
        Sub Test
           DOCCM.Map.Gates = New List(Of stDOCCMGate)
           DOCCM.Map.Stations = New List(Of stDOCCMStation)
           DOCCM.Ships = New Dictionary(Of String, stDOCCMShips)
         
           DOCCMtcpClient = New TcpClient()
           DOCCMtcpClient.Connect(DOCCM_IP, DOCCM_Port)
           DOCCMNetworkStream = DOCCMtcpClient.GetStream()
    
           Dim sendBytes As [Byte]()
           Do
              'Fülle DOCCM mit Daten ....
              With DOCCM
                 .Map.Name = DOD.MapName
                 .Map.Gates.Clear()
                 .Map.Stations.Clear()
                 .Ships.Clear()
    
                  For Each Gate In Map.Gates
                    If Left(Gate, 1) = "P" Then 'Gate
                       Dim GT As stDOCCMGate = Nothing
                       Dim GateNr As Integer = CInt(Strings.Right(Gate, Gate.Length - 1)) 'GateNummer
                       GT.Art = Gates(GateNr).Art
                       GT.Name = Gates(GateNr).Name
                       GT.X = Gates(GateNr).X
                       GT.Y = Gates(GateNr).Y
     
                       .Map.Gates.Add(GT)
                    Else
                      Dim StationNr As Integer = CInt(Strings.Right(Gate, Gate.Length - 1)) 'StationsNummer
                      Dim ST As stDOCCMStation = Nothing
    
                      ST.Art = Stationen(StationNr).Art
                      ST.Name = Stationen(StationNr).Name
                      ST.X = Stationen(StationNr).X
                      ST.Y = Stationen(StationNr).Y
    
                      .Map.Stations.Add(ST)
                    End If
                  Next
    
                  With .Agent
                    .UID = Account.UID
                    .Clan = DOD.OwnShip.Clan
                    .Firma = DOD.OwnShip.Firma
                    .Level = DOD.OwnShip.Level
                    .Name = DOD.OwnShip.Name
                    .Pos.X = DOD.OwnShip.XPos
                    .Pos.Y = DOD.OwnShip.YPos
                  End With
    
                  If ForeignShips.Count > 0 Then
                     For Each FS In ForeignShips.Values
                       Dim Ship As stDOCCMShips = Nothing
    
                       Ship.UID = FS.UID
                       Ship.Clan = FS.Clan
                       Ship.Firma = CType(FS.Firma, enFirma)
                       Ship.LastUpdate = FS.LastUpdate
                       Ship.Name = FS.Name
                       Ship.XPos = FS.XPos
                       Ship.YPos = FS.YPos
    
                      .Ships.Add(FS.UID, Ship)
                    Next
                 End If
              End With
    
              sendBytes = ?????? 'Hier müsste DOCCM in ein Byte-Array convertiert werden
              NetworkStream.Write(sendBytes, 0, sendBytes.Length)
           Loop While DatenVorhanden
    
           DOCCMNetworkStream.Dispose()
           DOCCMtcpClient.Close()
        End Sub
    End Module
    

    
    
    
    Ich habe nun keine Ahnung, wie ich die Gesamtstruktur DOCCM in ein Byte-Array umwandeln kann, so das ich die Struktur auf dem Empfängerseite in einem Stück "entnehmen" kann.
    Ich befürchte, das das nicht so einfach geht wie ich gehofft habe. Welche Möglichkeiten habe ich?
    Bisher habe ich in eigenen Client/Server-Anwendungen immer nur Zeicheketten versendet/empfangen.
    Muss ich meine DOCCM-Struktur nun auch in ihre Elemente zerlegen und in geeigneter Form als Zeichenketten versenden?


    Sonntag, 7. Februar 2010 09:13

Antworten

  • Hallo,

    das beste wäre Du wandelst Deine Strukturen für den Transfer in Xml-Daten um,
    zum Beispiel via XmlSerializer (siehe dort auch Weitere Ressourcen am Ende).

    Denn beim Transferieren von Daten übers Internet kommt bei binären Formaten
    die Byte-Order zum Tragen und sobald Du mit unterschiedlichen Hosts kommunizieren willst,
    gibt es schnell Datensalat.
    Für eine binäre Übertragung müßtest Du z. B. die Integer durch den BitConverter konvertieren,
    denn Standard für IP-Übertragungen ist Big Endian - wohingegen auf x86 Rechner LittleEndian verwendet wird.
    Hinzukommt dass Du die Zeichenkodierung bei Zeichenketten festlegen mußt (Unicode/Utf-8).

    Nur wenn es ein interner Transfer ist, könntest Du die binäre Serializierung verwenden,
    wobei Du aber auch dort mehr Vorkehrungen für spätere Änderungen treffen mußt.

    Insgesamt wird das sehr aufwändig, wohingegen der Weg über Xml  Dir ein definiertes Zeichen-Format
    vorgibt, was Du über XmlDocument etc. beiderseitig über vorhandene .NET Klassen verarbeiten kannst.
    Wenn Du zudem ein XmlSchema definierst bist Du für spätere Erweiterungen gerüstet.

    Gruß Elmar
    Sonntag, 7. Februar 2010 10:18
    Beantworter

Alle Antworten

  • Hallo,

    das beste wäre Du wandelst Deine Strukturen für den Transfer in Xml-Daten um,
    zum Beispiel via XmlSerializer (siehe dort auch Weitere Ressourcen am Ende).

    Denn beim Transferieren von Daten übers Internet kommt bei binären Formaten
    die Byte-Order zum Tragen und sobald Du mit unterschiedlichen Hosts kommunizieren willst,
    gibt es schnell Datensalat.
    Für eine binäre Übertragung müßtest Du z. B. die Integer durch den BitConverter konvertieren,
    denn Standard für IP-Übertragungen ist Big Endian - wohingegen auf x86 Rechner LittleEndian verwendet wird.
    Hinzukommt dass Du die Zeichenkodierung bei Zeichenketten festlegen mußt (Unicode/Utf-8).

    Nur wenn es ein interner Transfer ist, könntest Du die binäre Serializierung verwenden,
    wobei Du aber auch dort mehr Vorkehrungen für spätere Änderungen treffen mußt.

    Insgesamt wird das sehr aufwändig, wohingegen der Weg über Xml  Dir ein definiertes Zeichen-Format
    vorgibt, was Du über XmlDocument etc. beiderseitig über vorhandene .NET Klassen verarbeiten kannst.
    Wenn Du zudem ein XmlSchema definierst bist Du für spätere Erweiterungen gerüstet.

    Gruß Elmar
    Sonntag, 7. Februar 2010 10:18
    Beantworter
  • Danke für deine Antwort.

    An etwas wie die BinaryFormatter-Klasse hatte ich gedacht. Und die Beispiel dort sind auch sehr übersichtlich gehalten.

    Sender und Empfänger sind immer Programme, die von mir geschrieben sind. Es sind beides VB-Net-Programme.
    Kann es zu Schwierigkeiten kommen, wenn ein Programm auf einem anderen Rechner läuft, auf dem ein 64-Bit Windows installiert ist (ich nutze ein 32Bit-Windows) - ich denke da z.b. an die evtl. unterschiedlichen Wortbreite für den Typ Integer, oder ist der unter .Net immer 32-Bit breit? Und wenn es da Probleme geben könnte, dann ich VB.Net (ich habe die Express-Version) zwingen den Code auf dem Zielsystem (in dem Fall 64-Bit) als 32-Bit-Anwendung auszuführen? Ich meine da mal etwas entsprechendes gelesen zu haben.

    Gruß Valerie

    PS.
    Ich mag den XML-"Quatsch" nicht und brauche das sonst auch niemals .... ;)
    Sonntag, 7. Februar 2010 11:04
  • Hallo Valerie,

    die .NET/CLR Datentypen sind unabhängig vom Betriebssystem,
    so ist Integer ist immer ein 32-Bit Integer (System.Int32).

    Was den Xml-Quatsch angeht:
    Das wirst Du spätestens schätzen lernen, wenn Du mal Fehler suchen mußt,
    denn das Xml kann man so noch lesen.

    Und willst Du es einfach haben, kannst Du Linq To Xml
    wo Du Xml Code direkt in Visual Basic einbetten kannst.

    Bei einem BinaryFormatter wiederum mußt Du immer  dafür sorgen,
    dass auf beiden Enden identische (signierte) Assemblies verwendet werden.
    Sonst ist das schnell vorbei mit einfach.

    Gruß Elmar
    Sonntag, 7. Februar 2010 12:04
    Beantworter
  • ... identische (signierte) Assemblies ...


    Was meinst du genau damit? Meinst du das die Quell- und die Empfangsdatenstruktur identische sein muss - wenn ja, das ist ja selbstverständlich das die Identisch sein müssen.
    Was das nachlesen angeht, ich gehöre der Generation an, die per Packetsniffer auch noch gut binäre Daten "mitlesen" kann.


    Gruß,Valerie

    PS.
    Ich komme aus der 80er-Jahre Assembler-Hacker-Szene, kannte den Atari 800XL (zum Teile auch den C64) bis ins letzte Bit.

    Sonntag, 7. Februar 2010 17:11
  • Hallo Valerie,

    schau Dir den Link zur binären Serialisierung an, den ich schon gepostet habe -
    da steht so ziemlich alles was man wissen muß, insbes. auch
    Versionstolerante Serialisierung

    Gruß Elmar

    P.S.: Ich bin auch ziemlich lange im Geschäft und habe
    schon Atari, Z80, C64 am liebsten mit Assembler gequält.

    P. P. S:
    Und ich habe bis heute versucht der Entwicklung einigemaßen
    hinterher zu stolpern. Und Xml gehört IMHO zu den Dingen.
    mit den sich ein Entwickler auskennen sollte.

    Sonntag, 7. Februar 2010 20:52
    Beantworter
  • Hallo Elmar,

    ich habe das mit dem BinaryFormatter mal probiert und habe nun gesehen, zu welchen Problemen es da kommt.
    Nicht das ich das alle vollständig verstehen würde, aber BinaryFormatter scheint nicht dafür gemacht zu sein Daten zwischen unterschiedlichen Programmen auszutauschen, auch wenn die Datenstruktur die ausgetaischt werden soll, im Quell- und Zielprogramm identisch sind. Die Daten kommen zwar beim Ziel-Programm an, aber dort wird ein passendes Assembly vermisst ("Die Assembly "DOBBC2s, Version=2.3.3.1, Culture=neutral, PublicKeyToken=null" kann nicht gefunden werden.").
    Naja, die Serialisierung ist wohl fast ausschließlich dazu gemacht, (Objekt-)Zustände eines Programms festzuhalten, schade das man damit nicht mehr machen kann, das wäre so bequem.
    Ok, also werde ich mal eine anderen Ansatz probieren... - nein, kein XML, das ist mir zu sperrig und die Daten sind dann viel gut gut lesbar, so das ich die auch noch verschlüsseln müsste.
    Aber vielleicht, wenn der nächsten Ansatz nicht funktioniert, werde ich mich doch mal mit XML beschäftigen.

    Hier noch die Code-Schnipsel meines Versuches:
    'Auf der Senderseite
    
            Dim formatter As New BinaryFormatter
            formatter.Serialize(DOCCMNetworkStream, DOCCM)
    
    
    'Auf der Empfängerseite
             Dim formatter As New BinaryFormatter
             DOCCM = DirectCast(formatter.Deserialize(DOCCMNetworkStream), stDOCCM)
    


    Gruß, Valerie

    PS.
    Ich habe mich ja zumindest lesend  schon mit XML beschäftigt - ich rufte damals XML-Daten von einem Web-Server ab und verarbeitete die Daten - und fand das ganze sehr grauselig und sperrig. Ic habe die Routine dann umgeschrieben und die XML-Daten per String-Verarbeitung verarbeitet, was mir ehrheblich leichter gefallen ist und ich dort, nach meinem empfinden schnelle und direkter zum Ziel gekommen bin.
    Nenn' es Macke oder Aversion ...

    Sonntag, 7. Februar 2010 23:12
  • Hallo Valerie,

    mit der Fehlermeldung bist Du genau darauf gestossen,
    was das von Dir extrahierte Statement besagen sollte:

    Wenn Du die Assembly mit den Klassen veränderst
    mußt Du sie auch beim Empfänger installieren.
    Denn die Informationen über die Assembly steht mit
    in der binären Serialisierung.

    Deswegen sind solche Klassen in einer eigenen Schnittstellen
    Assembly am besten aufgehoben. Und man sollte kein
    automatisches Hochzählen  für AssemblyVersion verwenden,
    da damit bei jedem Kompilerlauf sich die Assembly - Signatur verändert.

    Zur Xml Serialisierung:
    Der Code wird am Ende nicht aufwändiger. Siehe z. B.:
    http://dzaebel.net/XmlSerializer.htm
    (und das ist nicht mal sonderlich toll gemacht)

    Du solltest Dich nicht zu früh von Aversionen leiten lassen.
    Denn gerade .NET hat sehr viel Unterstützung eingebaut -
    auch wegen der mittlerweile abgeflachten Hype seitens Microsoft,
    die ich nicht unbedingt geteilt habe ;-)
    So hätte man von Dir angesprochenen WebService
    nicht mehr von Hand verarbeiten müssen.

    Lies Dir die Abschnitte mal zu beiden Bereichen so vorurteilsfrei
    wie möglich durch.
    Gibt es dann Fragen zu einzelnen Abschnitten stelle sie hier.

    Gruß Elmar

    Montag, 8. Februar 2010 00:25
    Beantworter