TCP Client/Server Accept/Deny Login

  • VB.NET

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von dive26.

    TCP Client/Server Accept/Deny Login

    Hallo zusammen,
    ich habe mich für ein Projekt dazu entschieden, die Daten (Einfache strings) per TCP zu übertragen.
    Prinzipiell habe ich mit Hilfe der Tut´s einen Multiserver gebaut. Das funktioniert auch soweit. (Denke ich ;( )
    Die Client´s können sich einloggen und eine prinzipielle Kommunikation (Reader/Writer) funktioniert.

    Ich habe nun 2 Punkte, bei denen mir irgendwie keine Lösung einfällt.
    Zum einen möchte ich dass der Client sich unter einem bestimmten Namen am Server anmeldet. Kein Passwort oder so.
    Dazu sende ich als ersten den Login Namen an den Server, dieser soll dann prüfen ob der Name in der Liste ist.(Funktioniert)
    Wenn aber nicht, möchte ich dass die Verbindung gekappt wird bzw. gar nicht erst in die Überwachung aufgenommen.

    Wenn ich nun die Verbindung nicht annehme, wartet aber der Client auf eine Bestätigung. Aber irgendwie bekomme ich es nicht hin dort ein Timeout zu machen. Das Programm scheint beim streamreader hängen zu bleiben, vermutlich weil ich ja die Gegenstelle nicht weiter aufgebaut habe.

    Ich hoffe ihr versteht mein Problem und habt da einen Ansatz für mich.
    Besten Dank

    Kiter20

    Server Code

    VB.NET-Quellcode

    1. ​ client = server.AcceptTcpClient
    2. Dim _IP As IPAddress = CType(client.Client.RemoteEndPoint, IPEndPoint).Address
    3. Dim _Client As New Connection
    4. With _Client
    5. .stream = client.GetStream
    6. .streamr = New StreamReader(_Client.stream)
    7. .streamw = New StreamWriter(_Client.stream)
    8. .Machine = _Client.streamr.ReadLine.Split(CChar("-"))(1).Replace(" ", "")
    9. .IP = _IP.ToString
    10. End With
    11. If _Client.Machine = _GetValue("Login1", "") Or _Client.Machine = _GetValue("Login2", "") Then
    12. _Client.streamw.Write("Connected")
    13. _Client.streamw.Flush()
    14. _Connections.Add(_Client)
    15. Dim _Watch As New Threading.Thread(AddressOf ListenToConnection) 'Neuer Thread, der die Connection abhört wird erstellt
    16. _Watch.Start(_Client)
    17. Else
    18. 'client.Close()
    19. End If



    Client Code

    VB.NET-Quellcode

    1. ​If My.Computer.Network.Ping(ip) Then
    2. ' Try
    3. client.Connect(ip, port) 'Client verbindet sich mit IP: 192.168.111.17 und Port 4000
    4. If client.Connected Then
    5. Deklare_Streams() ' Sub Deklare_Streams()
    6. ' login()
    7. ' Do Until _Stopwatch.ElapsedMilliseconds > 5000
    8. ' Application.DoEvents()
    9. streamw.WriteLine("Login-" & txtLogin.Text)
    10. streamw.Flush()
    11. 'MessageBox.Show(streamr.ReadLine())
    12. ' _Delay(2)
    13. If streamr.ReadLine = "Connected" Then
    14. If t.IsAlive Then
    15. t.Resume()
    16. Else
    17. t.Start()
    18. End If
    19. Else
    20. client.Close()
    21. End If
    22. 'Loop
    23. '_Stopwatch.Stop()
    24. Else
    25. MessageBox.Show("Verbindung konnte nicht mit " & ip & " aufgebaut werden!")
    26. End If
    27. ' Catch ex As Exception
    28. 'MessageBox.Show("Verbindung konnte nicht mit " & ip & " aufgebaut werden!")
    29. 'End Try
    30. Else
    31. MessageBox.Show("Verbidnung mit " & ip & " ist nicht erreichbar!")
    32. End If
    "Mann" lernt mit seinen Projekten.
    Kurze Info meierseits, wie ich es mache (auch um Verbindungsprobleme zu behandeln): ich lasse zwei Tasks nebeneinanderlaufen:
    • den Verbindungstask inkl. CancellationToken, um ihn auf Signal hin zu beenden
    • einen Wartetask, der x Sekunden läuft und dann eben die CancellationToken des anderen Tasks aktiviert
    Wenn die Verbindung erfolgreich ist, geht alles normal seinen Gang, wenn zu lange gewartet wurde (weil der Server warum auch immer nicht antwortet), bricht das Programm mit Fehlermeldung ab.

    Du könntest aber ggf. trotzdem eine Antwort senden. Und wenn es nur ein Leerstring ist, mit dem der Client nix anfangen kann.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Das ist nur bedingt richtig, dass ich nicht Antworte.
    Ja, der Client würde in diesem Fall hängen bleiben, weil ich auf das "Connected" im reader warte. Hier wäre ja dann der Punkt, an dem ich ein Timeout benötige.
    Welches aber, wie die auskommentierten Reste zeigen, ich mit einem do loop und ner Stopwatch zu lösen. Leider ging das nicht.
    Ich hatte das in einem anderen Programm so gelöst, aber hier will das nicht.

    Im Idealfall würde der Client seinen Namen senden, dann würde dieser im Server positiv abgeglichen werden und der Server sendet das Connected zurück, auf welches der Client ja warten soll.

    Oder habe ich hier einen Gedankenfehler?

    ErfinderDesRades schrieb:

    folglich sollte der Server auch dann antworten, wenn er die Verbindung ablehnt
    Ich hatte das jetzt bewusst nicht gemacht, weil ich doch die Gegenstelle ev. gar nicht kenne.Nicht das irgendein anderes Programm versucht mit mir zu reden.Ebenso geht es mir bei dem Client auch da drum, dass er abbrechen soll, wenn die Anmeldung erfolglos war, denn es könnte ja sein, das in dem Netzt noch andere Server laufen und durch einen Tippfehler eine verkehrte IP angewählt wurde.
    "Mann" lernt mit seinen Projekten.

    kiter20 schrieb:

    Oder habe ich hier einen Gedankenfehler?

    Der hier:

    kiter20 schrieb:

    Das ist nur bedingt richtig, dass ich nicht Antworte.

    Wieso nur bedingt richtig?
    In deim Code ist zu sehen, dass du nicht antwortest, wenn du einen Client nicht verbinden willst. Und dein System verhält sich dann auch wie ein System, wo die Antwort ausbleibt.
    Naja, und das ist halt das Problem.
    Also antworte doch einfach, dass du diesen Client nicht verbinden willst.
    Dann benötigt der Client auch keinen Timeout.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ich denke aber, dein Problem ist noch viel grösser. Scheinbar hast du nu ein Server-Client-"Netzwerk", wo der Server die Clients hört, und die Clients hören den Server.

    Aber nur mit Hören ist noch nichts erreicht. Das Gesagte muss auch verstanden werden. Es muss also eine Sprache existieren - in anderen Worten: ein bidirektionales KommunikationsProtokoll.

    Mit das grösste Problem bei der Kommunikation ist, einen Befehl genau bis zu seinem Ende zu lesen, und nicht darüberhinaus (weil das gehört zum nächsten Befehl).

    Habich Tut zu gemacht, ist aber bisserl anspruchsvoller.
    TcpKommunikation + Networkstream

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Eben noch mal in die Doku vom NetworkStream geguckt, versuch mal zu schauen ob du damit was machen kannst. Evtl. 50 ms warten und schauen ob denn was zu lesen da ist.

    learn.microsoft.com/de-de/dotn…tworkstream-dataavailable
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Ok, für den Idealfall sehe ich vor, dass ich zumindest irgend etwas zurückschicke.
    Aaaaber, wenn ich das dann doch abbrechen möchte.
    Wieso geht das hier nicht?

    VB.NET-Quellcode

    1. Do
    2. Application.DoEvents()
    3. Dim x = streamr.ReadLine()
    4. If x = "Connected" Then
    5. c = True
    6. Else
    7. MessageBox.Show(x)
    8. Return False
    9. End If
    10. Loop Until t.ElapsedMilliseconds > 5000


    Oder bleibt das Ding wirklich stumpf beim streamreader hängen?

    Also könnte man das nur über einen eigenen Task realisieren, welchen man dann abschießt?
    "Mann" lernt mit seinen Projekten.
    was ist das?
    Server-Code, Client-Code?
    An wen wird da was returnt?

    Wer "will abbrechen" der Server oder der Client?

    jedenfalls StreamReader.Readline liest eine Zeile. Kommt keine Zeile, dann ist die Code-Ausführung blockiert.



    Du solltest nicht "irgendwas" zurückschicken, sondern etwas ganz bestimmtes. Nämlich dass du die Anfrage abweist. Du könntest zB "Rejected" zurückschicken.
    Dann ginge vlt. das hier:

    VB.NET-Quellcode

    1. Do
    2. Application.DoEvents()
    3. Dim x = streamr.ReadLine()
    4. If x = "Connected" Then
    5. c = True
    6. ElseIf x = "Rejected" Then
    7. Return False
    8. ElseIf x = "Disconnected" Then
    9. Return False
    10. Else
    11. MessageBox.Show(x)
    12. End If
    13. Loop
    Wie du siehst, haben wir hier schon den Beginn einer Sprache - mit den Worten: Connected, Rejected, Disconnected. Noch einiges wird hinzukommen, und das hier ist nur, was der Client versteht. Für was der Server verstehen soll ist hier noch nichts angedeutet.
    Also gut konzipieren, am besten Tut durcharbeiten.
    .ReadLine() ist schoma eine erhebliche konzeptionelle Einschränkung, weil wenn du das verwendest, kannst du nit mehr BinärDaten übertragen.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()

    kiter20 schrieb:

    Oder bleibt das Ding wirklich stumpf beim streamreader hängen?


    Ja der bleibt beim StreamReader hängen, setz mal haltepunkte. Die Komunikation mit der Gegenseite ist ertsmal vorbei, auf ClientSeite wird auf input gewartet, von daher erwähnte ich das mit dem NetworkStream, weil mit TcpListener.GetStream() bekommst du einen networkstream, wenn du genau so einen verwendest, sollte die erwähnte Property die Lösung sein. Nur dann lesen, wenn denn was da ist. Kann aber einen Moment dauern, musst also evtl. kurz warten.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    ErfinderDesRades schrieb:

    dann ist die Code-Ausführung blockiert.


    Das ist Client Code.
    Ich hatte mir jetzt den Login in eine separate Funktion gelegt.
    In diesem code snippet geht es mir vor allem da drum, dass ich nach 5000ms das Ding abbrechen möchte.

    Hier der angepasste Code vom Server, in dem ich immer etwas zurückschicke.

    VB.NET-Quellcode

    1. If _Client.Machine = _GetValue("Login1", "") Or _Client.Machine = _GetValue("Login2", "") Then
    2. _Client.streamw.Write("Connected")
    3. _Client.streamw.Flush()
    4. _Connections.Add(_Client)
    5. Dim _Watch As New Threading.Thread(AddressOf ListenToConnection) 'Neuer Thread, der die Connection abhört wird erstellt
    6. _Watch.Start(_Client)
    7. Else
    8. _Client.streamw.Write("You are not welcome!")
    9. _Client.streamw.Flush()
    10. End If


    "Mann" lernt mit seinen Projekten.
    mit Networkstream.DataAvailable habich jetzt rumprobiert - springt tatsächlich auf False, wenn im nws (momentan) nixmehr drinne ist.
    Aber auch hier ist .ReadLine ein Problem, weil es liest bis einschliesslich Zeilenvorschub - bleibt der aus, ist blockiert (bevor nws.DataAvailable umschalten kann).
    Und in meim eigenen Ansatz kann ichs auch net brauchen, weil ich bin drauf angewiesen, eine genau angegebene Menge Bytes auszulesen.
    Bleibt die aus, muss ich warten, und solange blockieren.
    Ah - kann mir doch helfen, nämlich wenn die Verbindung schlecht ist.
    Weil ich lese eine definierte Anzahl Bytes. Und wenn die nicht kommt ergibt sich eine "rasende Schleife" mit extremer Prozessorlast. Da kannich nu sowas einbauen:

    VB.NET-Quellcode

    1. While Not nws.DataAvailable : Threading.Thread.Sleep(20) : End While
    und der Kernel ist entlastet.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Vielen Dank für die guten Tipps.
    Erst einmal vorweg, für´s erste läuft das so wie ich es gerne hätte. Mit und ohne Antwort vom Server.
    Den Hauptfehler, welchen ich gemacht hatte, einfach mal copy and paste und eine Variable einfach "t" benannt gelassen.
    Leider hatte ich selber auf die schnelle zum testen den gleichen Namen verwendet. Naja... war halt suboptimal :S
    Hier der laufende Server Code:

    VB.NET-Quellcode

    1. Sub Main()
    2. server = New TcpListener(ipendpoint)
    3. server.Start()
    4. While Not t_stop = True
    5. Try
    6. client = server.AcceptTcpClient
    7. Dim _IP As IPAddress = CType(client.Client.RemoteEndPoint, IPEndPoint).Address
    8. _LoginActive = True
    9. Dim _Client As New Connection
    10. Dim _AlreadyConnected As Boolean = False
    11. With _Client
    12. .stream = client.GetStream
    13. .streamr = New StreamReader(_Client.stream)
    14. .streamw = New StreamWriter(_Client.stream)
    15. .Machine = _Client.streamr.ReadLine.Split(CChar("-"))(1).Replace(" ", "")
    16. .IP = _IP.ToString
    17. ._Connected = False
    18. End With
    19. If _Client.Machine = _GetValue("Login1", "") Or _Client.Machine = _GetValue("Login2", "") Then
    20. For Each con In _Connections
    21. If con.Machine = _Client.Machine Then
    22. _AlreadyConnected = True
    23. End If
    24. Next
    25. If _AlreadyConnected = False Then
    26. _Connections.Add(_Client)
    27. Dim _Watch As New Threading.Thread(AddressOf ListenToConnection)
    28. _Watch.Start(_Client)
    29. Else
    30. _Client.streamw.Write("Client schon verbunden!")
    31. _Client.streamw.Flush()
    32. _Delay(2)
    33. client.Close()
    34. End If
    35. Else
    36. _Client.streamw.Write("You are not welcome!")
    37. _Client.streamw.Flush()
    38. _Delay(2)
    39. client.Close()
    40. End If
    41. _Delay(5)
    42. _LoginActive = False
    43. Catch ex As Exception
    44. End Try
    45. End While
    46. End Sub


    Ich hatte erst gedacht, direkt bei der Anmeldung alles zu erledigen und sofort auch die positive Rückmeldung an den Client zu senden.
    Das scheint aber nicht sauber zu funnktionieren. Ich hatte da wohl irgendwelche timing überschneidungen. Somit habe ich mir eine Variable mit in die Connection gebaut, ob der Anmeldevorgang schon abgeschlossen wurde.
    Somit kann ich im Listen.Thread als erstes ein "Connected" senden.

    VB.NET-Quellcode

    1. Sub ListenToConnection(con As Connection)
    2. While Not t_stop = True
    3. If con._Connected = False Then
    4. _Send(con, "Connected")
    5. End If
    6. Do
    7. Try
    8. If con.stream.DataAvailable = True Then
    9. Dim x = con.streamr.ReadLine
    10. MessageBox.Show(con.Machine & " Receive: " & x)
    11. Select Case x
    12. Case "Test"
    13. _Send(con, "Test OK")
    14. Case "Disconnect"
    15. _Connections.Remove(con)
    16. Case "Still there"
    17. _ConnectionAlive = True
    18. End Select
    19. End If
    20. Catch
    21. _Connections.Remove(con)
    22. MessageBox.Show(con.Machine & " wurde beendet.")
    23. Exit Do
    24. End Try
    25. Loop
    26. End While
    27. End Sub


    ErfinderDesRades schrieb:

    muss man einen eigenen timeout implementieren? man kann doch am nws Timeouts einstellen.

    Irgendwie wollte das aber nicht so richtig funktionieren.

    TcpClient.Dispose() wird mir nicht angeboten. Scheint jetzt .close zu sein.
    __________________________________________________________________________________________

    Und hier die Client Seite:

    VB.NET-Quellcode

    1. Sub Connect_to_Server(ByVal ip As String, ByVal port As Integer)
    2. If client.Connected Then Return
    3. _LoginActive = True
    4. client = New TcpClient
    5. _ListenThread_Stop = False
    6. If My.Computer.Network.Ping(ip) Then
    7. ' Try
    8. Try
    9. client.Connect(ip, port)
    10. Catch ex As Exception
    11. MessageBox.Show("Server antwortet nicht.")
    12. Exit Sub
    13. End Try
    14. If client.Connected Then
    15. Deklare_Streams()
    16. If _Login() = True Then
    17. If _ListenThread.IsAlive Then
    18. _ListenThread.Resume()
    19. Else
    20. _ListenThread.Start()
    21. End If
    22. Else
    23. streamr.Dispose()
    24. streamr.Close()
    25. streamw.Dispose()
    26. streamw.Close()
    27. stream.Dispose()
    28. stream.Close()
    29. client.Close()
    30. MessageBox.Show("Verbindung konnte nicht hergestellt werden.")
    31. End If
    32. Else
    33. MessageBox.Show("Verbindung konnte nicht mit " & ip & " aufgebaut werden!")
    34. End If
    35. Else
    36. MessageBox.Show("Verbidnung mit " & ip & " ist nicht erreichbar!")
    37. End If
    38. _LoginActive = False
    39. End Sub


    Und siehe da, das Timeout funktioniert auch.

    VB.NET-Quellcode

    1. Private Function _Login() As Boolean
    2. Dim _Stopwatch As New Stopwatch
    3. Dim c As Boolean = False
    4. Try
    5. streamw.WriteLine("Login-" & txtLogin.Text)
    6. streamw.Flush()
    7. _Stopwatch.Start()
    8. Do
    9. Application.DoEvents()
    10. If stream.DataAvailable = True Then
    11. Dim x = streamr.ReadLine
    12. If x = "Connected" Then
    13. c = True
    14. Else
    15. MessageBox.Show(x)
    16. Return c
    17. End If
    18. End If
    19. Loop Until _Stopwatch.ElapsedMilliseconds > 5000
    20. _Stopwatch.Reset()
    21. Return c
    22. Catch
    23. Return c
    24. End Try
    25. End Function


    Was ich jetzt habe ist, dass obwohl ich alles geschlossen, disposed und angehalten habe, die Client Anwendung nach der Trennung vom Server beim schließen das Debugging nicht beendet.
    Habt ihr da einen Tipp, wie man herausfindet, wo das Programm hängt? Gibt es da etwas im Visual Studio?

    Vielen Dank
    Kiter20
    "Mann" lernt mit seinen Projekten.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kiter20“ ()

    Auch wenn es nichts weiter am Problem macht:

    kiter20 schrieb:

    While Not t_stop = True
    Boolean-Vergleiche mit True sind überflüssig.

    kiter20 schrieb:

    If _Client.Machine = _GetValue("Login1", "") Or _Client.Machine = _GetValue("Login2", "") Then
    Der Unterschied zwischen And und AndAlso/Or und OrElse

    kiter20 schrieb:

    Catch ex As Exception
    Fange nur Exceptions ab, die Du kennst und sinnvoll bearbeiten kannst.

    kiter20 schrieb:

    Application.DoEvents()
    8|
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.