Senden von HELLO an den TCP-Server

Soundthesizer, Zusitool und andere Zusatzsoftware

Moderatoren: Andreas Damm, Jens Haupert

Antworten
Nachricht
Autor
Benutzeravatar
Daniel Thürck
Beiträge: 21
Registriert: 14.01.2006 12:07:18

Senden von HELLO an den TCP-Server

#1 Beitrag von Daniel Thürck »

Hallo,

ich versuche jetzt seit längerer Zeit, allerdings ohne Erfolg, eines meiner Programme in VB.NET mit dem TCP-Server zu verbinden:

Code: Alles auswählen

Public Class Form1

    Dim Client As New Net.Sockets.TcpClient()
    Dim Clientname As String = "fahrpult"

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Erst mal verbinden
        If Client.Connected() = False Then
            Client.Connect("192.168.178.21", 1435)
        End If
        Dim Stream As Net.Sockets.NetworkStream = Client.GetStream()
        'Jetzt den HELLO-Befehl senden
        Dim bufferA As String = Str(&H0) + Str(&H1) + Str(&H1) + Str(&H2) + Str(Clientname.Length()) + Clientname
        Dim bufferB As String = Str(bufferA.Length())
        Dim message As Byte()
        message = System.Text.Encoding.ASCII.GetBytes(bufferB + bufferA)
    End Sub
End Class
Führe ich das aus, so verbindet sich das Programm mit dem Server, allerdings zeigt dieser bei allen Feldern <unbekannt> an.

Muss ich den HELLO Befehl denn als ByteArray oder als String senden?
Und vor allem, wieso nimmt der Server meine jtzt geschickten Daten nicht an?

Gruß
Daniel
Zuletzt geändert von Daniel Thürck am 13.04.2006 19:37:40, insgesamt 1-mal geändert.

Benutzeravatar
Carsten Hölscher
Administrator
Beiträge: 33450
Registriert: 04.07.2002 00:14:42
Wohnort: Braunschweig
Kontaktdaten:

#2 Beitrag von Carsten Hölscher »

byte-Array sollte stimmen. Strings können gehen, gibt ja verschiedene Sorten, während byte-Array eindeutig ist.

Carsten

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#3 Beitrag von Roland Ziegler »

String ist unter .Net in jedem Fall Unicode und damit hier schon vom Format pro Zeichen nicht kompatibel.

Klappt denn das Connect()?

Ansonsten musst Du dafür sorgen, dass die Längenangabe 4 Byte hat. Üblicherweise kann man unsigned int (wie immer die in VB heißen mögen) "direkt" serialisieren, also in den Stream schreiben.

Generell würde ich hier keinen Umweg über Strings wählen, sondern die entsprechenden Daten möglichst immer "direkt" als 1-, 2- oder 4-Byte Integer in den Stream schicken. Für die Übersetzung der Client-Identifikation scheint die GetBytes-Methode aber vielleicht geeignet. (String: Unicode, 16 bit char, ASCII 8 bit char. Achtung: Keine Umlaute verwenden, Codepages werden nicht unterstützt.)

"Direkt" heißt mittels eines BinaryWriter (und Reader). Superstreng objektorientierte Bibliotheken ohne "Bypass" (auch Java) arbeiten so. Da dies hier .Net-Klassen sind, wird es sicher auch unter VB so ähnlich gehen, wie damals für C# hier skizziert:
http://zusiforum.eisenbahn-seiten.de/vi ... 753&#73753

Benutzeravatar
Jörg Petri
Beiträge: 921
Registriert: 04.11.2001 19:06:35
Aktuelle Projekte: S-Bahnen Berlin & diverse Straßenbahnen . [zusätzlich auch ZusiFunkTool & Schmalspurbereich(D & CH)]
Wohnort: Saaleplatte/Thüringen (ex.Leipzig/Sachsen) zw. Seelze/Niedersachsen
Kontaktdaten:

#4 Beitrag von Jörg Petri »

Für VB6 hatte ich damals was gebastelt und es lief. Nur weiß ich jetzt nicht in wiefern man das für VB.net anwenden kann.
Jörg Petri
Fdl FuB-Netz Hannover


Zusi-Signal-&-Fahrzeugbau Saaleplatte / Standort Saaleplatte und Seelze

schmalspur(AT)zpa(DOT)zusi(DOT)de - ZPA-Abteilung Schmalspur

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#5 Beitrag von Roland Ziegler »

Außer der Syntax hat VB6 nicht viel mit VB .Net gemeinsam.

[OT]
@Daniel: Wenn Du neu an diese Entwicklung herangehst und mit .Net arbeiten willst, hast Du schon einmal daran gedacht, mit C# statt mit VB zu arbeiten?

Unter .Net sind zwar im Prinzip alle Sprachen gleichwertig (einschließlich Delphi, und mit Ausnahme der erweitereten Möglichkeiten von C++), aber die Programmiersprache ist halt das womit man täglich umgeht.

C# ist die "natürliche" Sprache für .Net. Üblicherweise ist man mit C# in der Lage, den kürzesten Code zu schreiben, der zusätzlich auch noch gut lesbar ist. Der Vergleich der Beispiele im SDK zwischen VB und C# kann dabei als guter Anhaltspunkt dienen.

Ein entscheidender Grund für C# ist die "Sauberkeit" der Sprache. Gerade wenn man neu einsteigt und völlig unvorbelastet ist, sollte einen nichts hindern, den Einstieg mit einer "ordentlichen" Programmiersprache zu wagen, "ordentlich" hier bezogen auf die Definition der Sprache.

Natürlich ist es absolut richtig, dass man auch mit VB sauberen Code schreiben kann, genauso, wie man mit C# beliebiges Chaos anrichten kann. Die Erfahrung lehrt allerdings, dass die Chancen für schlechten Code in VB höher und die Chancen für guten Code in C# höher sind. Warum? Derjenige, der sich mit C# beschäftigt, verwendet im Allgemeinen mehr Zeit damit, sich um Sprachaufbau, objektorientiertes Vorgehen und entsprechende Klassen- und Objektnutzung zu kümmern. Das zahlt sich hinterher normalerweise durch besseren Code aus. Der typische VB-Ansatz hingegen neigt zu "quick and dirty", was sich dann später rächt. Das hat, wie vorher angemerkt, nicht unmittelbar mit der jeweiligen Sprache zu tun, sondern mehr mit deren "üblicher" Nutzung.

Benutzeravatar
Daniel Thürck
Beiträge: 21
Registriert: 14.01.2006 12:07:18

#6 Beitrag von Daniel Thürck »

Dankeschön :wow , jetzt funktioniert's:

Code: Alles auswählen

Public Class Form1

    Dim Client As New Net.Sockets.TcpClient()
    Dim Clientname As String = "ET423"

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Erst mal verbinden
        If Client.Connected() = False Then
            Client.Connect("192.168.178.21", 1435)
        End If
        Dim Stream As Net.Sockets.NetworkStream = Client.GetStream()
        Dim Writer As New IO.BinaryWriter(Stream)
        'Jetzt den HELLO-Befehl senden
        Dim Clientname_Length As UInt32 = Clientname.Length()
        Dim Befehl() As Byte = {0, 1, 1, 2, 5}
        Dim b As Byte
        Dim packet_length As UInt32 = 11

        Writer.Write(packet_length)
        For Each b In Befehl
            Writer.Write(b)
        Next
        Writer.Write(Clientname_Length)
        Writer.Write(System.Text.Encoding.ASCII.GetBytes(Clientname))

    End Sub
End Class
Zwar wird beim Clientname noch so ein Kästchen angezeigt, aber das schaff' ich noch.


@Roland Zigeler:
C++ und C# hatte ich mir schon angeschaut, bin aber bei VB.NET verblieben, da ich damit gute Aussichten auf einen Ferienjob habe :]

Gruß
Daniel
Zuletzt geändert von Daniel Thürck am 14.04.2006 10:33:40, insgesamt 1-mal geändert.

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#7 Beitrag von Roland Ziegler »

C++ würde ich Dir auch nicht empfehlen. Das ist wirklich was für Fortgeschrittene, und im Rahmen von .Net eigentlich nur für spezielle Anwendungen von Interesse. Wenn Du Dich aber ernsthaft mit der Materie Software beschäftigen willst - nicht nur als Ferienjob, sondern auch eine mögliche berufliche Zukunft im Auge haltend - dann würde ich mich möglichst früh von VB lösen.

Zur Sache: Ich gehe im Moment fest davon aus, dass der Aufbau des Telegramms, also der Byte-Strom, nicht dem vom Server erwartenen entspricht. Mit dem BinaryWriter dazwischen sollte sich das aber ändern.
Zuletzt geändert von Roland Ziegler am 14.04.2006 10:19:07, insgesamt 1-mal geändert.

Benutzeravatar
Daniel Thürck
Beiträge: 21
Registriert: 14.01.2006 12:07:18

#8 Beitrag von Daniel Thürck »

Roland Ziegler hat geschrieben:C++ würde ich Dir auch nicht empfehlen. Das ist wirklich was für Fortgeschrittene, und im Rahmen von .Net eigentlich nur für spezielle Anwendungen von Interesse. Wenn Du Dich aber ernsthaft mit der Materie Software beschäftigen willst - nicht nur als Ferienjob, sondern auch eine mögliche berufliche Zukunft im Auge haltend - dann würde ich mich möglichst früh von VB lösen.

Zur Sache: Ich gehe im Moment fest davon aus, dass der Aufbau des Telegramms, also der Byte-Strom, nicht dem vom Server erwartenen entspricht. Mit dem BinaryWriter dazwischen sollte sich das aber ändern.
Naja, ich programmiere schon etwas länger: Angefangen habe ich mit BASIC, dann für einige Zeit C, später über VB dann zu VB.NET.
Dafür habe ich noch ein Buch hier liegen, da muss ich noch 800 Seiten durchmachen :D
C# kann ich mir vielleicht danach anschauen, das sollte ja dan einfacher sein, da man die .NET-Bibliothek schon kennt.

Gruß
Daniel

Mirko
Beiträge: 3410
Registriert: 04.11.2001 20:39:16
Aktuelle Projekte: Nothing in particular
Wohnort: Erftstadt
Kontaktdaten:

#9 Beitrag von Mirko »

Wie war das? "The last good thing written in C was Schubert's Symphony No. 9 - and even this is missing the last finish."
Man sollte sich immer gut überlegen, was man sich wünscht. Manchmal passiert es, dass man es kriegt (Meat Loaf)

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#10 Beitrag von Roland Ziegler »

Schön, dass es funktioniert. :]

Eine feine Eigenschaft der OO auch in .Net ist das Überladen von Methoden.

Damit man weiß, was im Code drinsteht, könnte man ihn mit Hilfe der überladenen Funktionen auch so schreiben (keinerlei Gewähr für VB-Syntax):

Code: Alles auswählen

        
   Dim Clientname_Length As UInt32 = Clientname.Length()
   ' Dim Befehl() As Byte = {0, 1, 1, 2, 5}
   
   Dim packet_length As UInt32 = 11
   Writer.Write(packet_length)
   
   Dim befehl As UInt16 = HELLO 'HELLO vorher als enum definiert 

   Writer.Write (befehl) 
   Writer.Write (version)  ' vorher definiert als 1 byte uint
   Writer.Write (client_type) ' vorher definiert als 1 byte uint

   ' For Each b In Befehl
   '        Writer.Write(b)
   '    Next
Im nächsten Schritt würde man dann die Paketlänge noch dynamisch ermitteln - häufige Fehlerquelle wenn hart codiert.

Und danach, wie man fast ahnen kann, bietet sich die Kapselung in Klassen an.

Eine SW-Engineering-Vorgehensweise ist in bestimmten Fällen durchaus so, erst mal probieren, ob es überhaupt geht, so wie hier, dann diesen Code wegwerfen, und in saubere Klassen verpacken. Das nennt man "Bottom-up". Kann sehr mühsam werden, weil immer wieder viel Zeugs weggeworfen werden muss, um einigermaßen pflegbaren Code zu bekommen.

Eleganter ist der Top-Down-Ansatz, der erst die Klassen grob identifiziert, die man braucht, und die dann Stück für Stück implementiert.

Benutzeravatar
Daniel Thürck
Beiträge: 21
Registriert: 14.01.2006 12:07:18

#11 Beitrag von Daniel Thürck »

Und hier mit dynamischer Paketlängeberechnung:
(Allerdings zeigt der TCP-Server nur den ersten Buchstaben, das E, des Clientnamens an. Liegt das evtl. an der Codierung? Ich benutze gerade für GetBytes() Unicode mit 16-Bit. Bei Unicode mit 8-Bit wird überhaupt nichts mehr übertragen, d.h. der Server zeigt wieder <unbekannt> an.)

Code: Alles auswählen

Public Class Form1

    Dim Client As New Net.Sockets.TcpClient()
    Dim Clienttype As Byte = 2
    Dim Clientname As String = "ET423"

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Erst mal verbinden
        If Client.Connected() = False Then
            Try
                Client.Connect("192.168.178.21", 1435)
            Catch ex As Exception
                MsgBox("Sorry, konnte keine Verbindung herstellen!")
                Exit Sub
            End Try
        Else
            MsgBox("Schon verbunden!")
        End If
        If Client.Connected() = True Then
            Label1.BackColor = Color.Green
            Label1.Text = "Verbunden"
        End If
        Dim Stream As Net.Sockets.NetworkStream = Client.GetStream()
        Dim Writer As New IO.BinaryWriter(Stream)

        'Jetzt den HELLO-Befehl senden
        Dim befehl() As Byte = {0, 1, 2, Clienttype, Clientname.Length()}
        Dim b As Byte
        Dim packet_length As UInt32 = befehl.Length() + Clientname.Length()

        Writer.Write(packet_length)
        For Each b In befehl
            Writer.Write(b)
        Next
        Writer.Write(System.Text.Encoding.Unicode.GetBytes(Clientname))
        Threading.Thread.Sleep(10000)
    End Sub
End Class

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#12 Beitrag von Roland Ziegler »

Vergiss Unicode, 8 und 16bit, wenn Du Dich wie hier außerhalb der .Net-Welt bewegst. 8bit Unicode ist UTF8. Das gibt es binär oder als Text, mit und ohne Header. Macht die Sache unnötig kompliziert. ASCII reicht an dieser Stelle allemal aus.

Vermeide lieber solche Iterationen über Byte-Felder. Dafür gibt es die vorhin angesprochene Funktionsüberladung beim Writer. Bei Reader sind es an dieser Stelle Funktionen mit unterschiedlichem Namen, da in der C#-OO-Welt (C++/Java), an die sich auch VB Net halten muss, Überladung allein über Rückgabetyp nicht zulässig ist.

Dies ist der allgemein übliche Weg. Dein Ansatz mag technisch funktionieren, hat aber noch wenig mit OO zu tun.

Benutzeravatar
Daniel Thürck
Beiträge: 21
Registriert: 14.01.2006 12:07:18

#13 Beitrag von Daniel Thürck »

ASCII genommen und siehe da: Es funktioniert!

Vielen, vieln Dank nochmal, Roland!

Gruß
Daniel

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#14 Beitrag von Roland Ziegler »

Viel Erfolg weiterhin!

Benutzeravatar
Daniel Thürck
Beiträge: 21
Registriert: 14.01.2006 12:07:18

Weiter geht's...

#15 Beitrag von Daniel Thürck »

Ich benötige wieder einmal eure Hilfe...

Mein Client sendet HELLO, empfängt ACK_HELLO, sendet NEEDED_DATA (ID 1; km), empfängt ACK_NEEDED_DATA.
Ich verbinde ihn also mit dem Server, danch Zusi mit diesem. Zusi zeigt auch im Verbindungsfenster an, dass es die KM ausgeben soll.
Lese ich allerdings per Binary Reader jetzt die Daten aus, so kommt immernoch nur das ACK_NEEDED_DATA vom Server, obwohl Zusi di km sendet?
?(

Gruß
Daniel

Code: Alles auswählen

Public Class Form1

    Dim Client As New Net.Sockets.TcpClient()
    Dim Clienttype As Byte = 2
    Dim Clientname As String = "ET423"

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Erst mal verbinden
        If Client.Connected() = False Then
            Try
                Client.Connect("192.168.178.21", 1435)
            Catch ex As Exception
                MsgBox("Sorry, konnte keine Verbindung herstellen!")
                Exit Sub
            End Try
        Else
            MsgBox("Schon verbunden!")
        End If
        If Client.Connected() = True Then
            Label1.BackColor = Color.Yellow
            Label1.Text = "Warten auf Server..."
        End If

        Dim Stream As Net.Sockets.NetworkStream = Client.GetStream()
        Dim Writer As New IO.BinaryWriter(Stream)
        Dim Reader As New IO.BinaryReader(Stream)

        'HELLO Senden
        Dim befehl() As Byte = {0, 1, 2, Clienttype, Clientname.Length()}
        Dim packet_length As UInt32 = befehl.Length() + Clientname.Length()

        Writer.Write(packet_length)
        Writer.Write(befehl)
        Writer.Write(System.Text.Encoding.ASCII.GetBytes(Clientname))

        'ACK_HELLO Empfangen
        Dim stream_in() As Byte
        stream_in = Reader.ReadBytes(7)

        If stream_in(stream_in.Length() - 1) = 0 And stream_in(stream_in.Length() - 2) = 2 And stream_in(stream_in.Length() - 3) = 0 Then
            Label1.BackColor = Color.Green
            Label1.Text = "Verbunden"
        End If
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim Stream As Net.Sockets.NetworkStream = Client.GetStream()
        Dim Writer As New IO.BinaryWriter(Stream)
        Dim Reader As New IO.BinaryReader(Stream)
        Dim stream_in() As Byte

        Dim DatenID As Byte = 1
        Dim befehl() As Byte = {0, 3, 0, 10, DatenID}
        Dim packet_length As UInt32 = befehl.Length

        Writer.Write(packet_length)
        Writer.Write(befehl)
        Label2.BackColor = Color.Yellow
        Label2.Text = "Anforderung"

        stream_in = Reader.ReadBytes(7)

        If stream_in(stream_in.Length() - 1) = 0 And stream_in(stream_in.Length() - 2) = 4 And stream_in(stream_in.Length() - 3) = 0 Then
            Label2.BackColor = Color.Green
            Label2.Text = "Anforderung bestätigt"
        End If

        Dim befehl2() As Byte = {0, 3, 0, 0}
        packet_length = befehl2.Length()

        Writer.Write(packet_length)
        Writer.Write(befehl2)

    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Dim Stream As Net.Sockets.NetworkStream = Client.GetStream()
        Dim Reader As New IO.BinaryReader(Stream)
        Dim Sl As Single

        Sl = Reader.ReadByte()
        TextBox1.Text = Str(Sl)

    End Sub
End Class

Benutzeravatar
Roland Ziegler
Beiträge: 5508
Registriert: 04.11.2001 22:09:26
Wohnort: 32U 0294406 5629020
Kontaktdaten:

#16 Beitrag von Roland Ziegler »

Hallo Daniel,

versuche es doch mal mit einer Klasse, in der Du neben dem Socket auch die I/O-Streams unterbringst. Ob es überhaupt funktioniert, den Reader pro Befehl neu zuzuweisen, vermag ich nicht zu beurteilen. Es ist zumindest nicht üblich, so etwas zu machen. Zum anderen rate ich nochmals an, beim Lesen und Schreiben in den BinaryStream die typsicheren Methoden für Einzelvariablen zu benutzen und nicht mit anonymen Byte-Feldern zu arbeiten. Das kann ein Fremder praktisch nicht lesen und Hifestellung nur mit größerem Aufwand gewähren.

Ein möglicher Feld-, Wald- und Wiesenaufbau würde eines Basisklasse für den allgemeinen Befehlsaufbau definieren und entweder abgeleitete Klassen oder Methoden für jeden konkreten Befehl. Deine Buttons rufen dann nur noch die Funktionen der Befehlsklassen auf.

Trenne damit Form und Funktion und beherzige so einen der wichtigsten Grundsätze der Softwareerstellung. Fenster und Buttons haben mit der Socket-Verbindung doch gar nichts zu tun.

Du fängst gerade an, Dich mit Software zu beschäftigen, darum sei Dir typisches VB-Vorgehen verziehen. Aber glaub mir, sauberer OO-Code lässt sich viel besser debuggen als Spaghetti-Code und erleichtert anderen die Hilfe.

Antworten