Multiserver (TCP)

    • VB.NET

    Es gibt 854 Antworten in diesem Thema. Der letzte Beitrag () ist von ClonkAndre.

      Das Problem ist, wenn ich meine Öffentliche IP von meinem PC eingebe, kann sich niemand mit dem Server verbinden.
      Port ist im Netzwerk auch offen.

      Mein Code ist zwar relativ ähnlich zur Vorlage in diesem Post, aber hier:

      Quellcode

      1. Imports System.Net.Sockets
      2. Imports System.IO
      3. Imports System.Net
      4. Imports System.Windows.Forms
      5. Module Module1
      6. Private server As TcpListener
      7. Private client As New TcpClient
      8. Private ipendpoint As IPEndPoint = New IPEndPoint(IPAddress.Any, 4232)
      9. Private list As New List(Of Connection)
      10. Private ScriptReader As Resources.IResourceReader
      11. Private Script As String
      12. Private Structure Connection
      13. Dim stream As NetworkStream
      14. Dim streamwriter As StreamWriter
      15. Dim streamreader As StreamReader
      16. Dim nickname As String
      17. End Structure
      18. Private Sub SendToAllClients(ByVal s As String)
      19. For Each Connection As Connection In list
      20. Try
      21. Connection.streamwriter.WriteLine(s)
      22. Connection.streamwriter.Flush()
      23. Catch
      24. End Try
      25. Next
      26. End Sub
      27. Sub Main()
      28. Console.Title = "Server"
      29. Call BOT_WRITER.bot_writer()
      30. Console.WriteLine("Der Server läuft!")
      31. server = New TcpListener(ipendpoint)
      32. server.Start()
      33. While True
      34. client = server.AcceptTcpClient
      35. Dim connection As New Connection
      36. connection.stream = client.GetStream
      37. connection.streamreader = New StreamReader(connection.stream)
      38. connection.streamwriter = New StreamWriter(connection.stream)
      39. connection.nickname = connection.streamreader.ReadLine
      40. list.Add(connection)
      41. Call BOT_WRITER.bot_writer()
      42. Console.WriteLine(connection.nickname & " hat den Server ")
      43. Dim t As New Threading.Thread(AddressOf ListenToConnection)
      44. t.Start(connection)
      45. End While
      46. End Sub
      47. Private Sub ListenToConnection(ByVal con As Connection)
      48. Do
      49. Try
      50. Dim msg As String = con.streamreader.ReadLine
      51. Console.ForegroundColor = ConsoleColor.Green
      52. Console.Write(" <")
      53. Console.ForegroundColor = ConsoleColor.Red
      54. Console.Write(con.nickname)
      55. Console.ForegroundColor = ConsoleColor.Green
      56. Console.Write(">")
      57. Console.ForegroundColor = ConsoleColor.White
      58. Console.Write(" :> ")
      59. Console.ForegroundColor = ConsoleColor.Yellow
      60. Console.Write(msg)
      61. Console.WriteLine()
      62. SendToAllClients(con.nickname & " :> " & msg)
      63. Catch
      64. list.Remove(con)
      65. Call BOT_WRITER.bot_writer()
      66. Console.WriteLine(con.nickname & " hat den Server verlassen.")
      67. Exit Do
      68. End Try
      69. Loop
      70. End Sub
      71. End Module

      n7revenant schrieb:

      Öffentliche IP
      Wie bekommst du die?

      n7revenant schrieb:

      Call BOT_WRITER.bot_writer
      Bitte KEIN Call verwenden! Einfach ohne Call
      In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
      Hallo,

      hasst du deinen Server schon lokal getestet? Wenn ja und der Server richtig arbeitet, tippe ich mal auf ein Problem mit der Portfreigabe bzw deiner externen IP-Adresse.
      Da du in deinem Code IPEndPoint(IPAddress.Any) benützt sollte der Server die Clientaktivitäten an allen Netzwerkschnittstellen überwachen. Das sollte soweit korrekt sein.

      Bist du dir sicher das der offene Port auf den richtigen Zielhost in deinem lokalen Netzwerk zeigt?
      Teste das mal aus...

      IPChicken - Hier kannst du deine externe Adresse abfragen.
      CanYouSeeMe.org - Hier kannst du deine Portkonfiguration testen.
      Hallo hab auch eine Frage :)

      Wenn ich den Client starte und mit dem Server verbinden möchte, bekomme ich einen Fehler beim Client in der Sub Chatverbindung:

      VB.NET-Quellcode

      1. Sub chatverbindung(ByVal nick)
      2. t = New Threading.Thread(AddressOf Listen)
      3. client = New TcpClien
      4. client.Connect(My.Settings.IP, My.Settings.Port) ' hier die ip des servers eintragen. <-- Fehler wenn Server nicht läuft
      5. ' da dieser beim testen wohl lokal läuft, hier die loopback-ip 127.0.0.1.
      6. If client.Connected Then
      7. stream = client.GetStream
      8. streamw = New StreamWriter(stream)
      9. streamr = New StreamReader(stream)
      10. streamw.WriteLine(nick) ' das ist optional.
      11. streamw.Flush()
      12. t.Start()
      13. RichTextBox1.Text = ("Verbindung zu " & My.Settings.IP & " hergestellt." & vbCrLf & vbCrLf)
      14. ToolStripStatusLabel1.Text = ("verbunden")
      15. ToolStripStatusLabel1.ForeColor = Color.Green
      16. Else
      17. MessageBox.Show("Verbindung zum Server nicht möglich!")
      18. End If
      19. End Sub


      Code-Tags eingefügt. ~Thunderbolt

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

      @Jonesi Und welcher Fehler tritt jetzt auf?

      Editor-Bug gefixt. ~Thunderbolt

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

      Wenn der Client im Beispiel die Verbindung mit

      VB.NET-Quellcode

      1. ​client.Close()
      trennt, registriert das der Server im Catch Block ganz unten.
      Verliert der Client allerdings ungewollt die Netzwerkverbindung und kann den Close()-Befehl nicht mehr absenden, bemerkt der Server das nicht.

      Ich würde gerne auch diesen Fall abfangen.
      Wie kann ich kontrollieren, ob der jeweilige Client noch verbunden ist?

      Danke!
      @DavidVB Schick dem Client einfach periodisch einen Ping. Kommt keine Antwort, kannst du davon ausgehen, dass der Client nicht mehr verbunden ist (bzw das Senden müsste schon fehlschlagen).
      Hallo,

      Wenn sich ein neuer Client mit dem Server verbindet, wird die Connection in einer Liste gespeichert und dann wird ein neuer Thread mit ihr erstellt, der dann auf diese Connection empfängt.

      VB.NET-Quellcode

      1. Private list As New List(Of Connection)
      2. list.add(c)
      3. Listener.Start(c)


      VB.NET-Quellcode

      1. 'Thread Listener(ByVal c As Connection)'
      2. c.streamr.readline()
      3. c.streamw.writeline(irgendwas)


      Wenn jetzt mehrere Clients verbunden sind ist jetzt meine Frage wie man vom Server an einen bestimmten Client sendet. Also angenommen ich habe einen Thread, in den kein ' c As Connection ' übergeben wurde.
      Also wie kann ich es z.B. machen, dass man sagen kann: Sende an den Client, wo c.nick = MaxMustermann ist. Also eine Funktion, wo man Username und Nachricht als Parameter hat und die dann sendet.

      Hoffe ich konnte mein Problem gut beschreiben :rolleyes:

      MfG MrBodega

      Hätte mal ne Frage zum Port

      Wie läuft das eigentlich mit dem Port ab?
      Kann ich jetzt z.b. den Port 8000 einstellen und ein anderer den Port 3557 spielt das keine Rolle?
      Oder muss wirklich Port von mir und den anderen Client übereinstimmen?
      Oder spielt vielleicht nur die IP Adresse ne Rolle?

      wäre dankbar für eine Antowrt
      Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

      Für VB.NET Entwickler: Option Strict On nicht vergessen!
      Post#1, Zeile#33: Der Server erstellt einen Lauscher auf dem IPEndPoint. Und der ist so definiert:

      VB.NET-Quellcode

      1. Private ipendpoint As IPEndPoint = New IPEndPoint(IPAddress.Any, 8000)
      Also "lauscht" der Server nur auf Port 8000. Wenn ein Client versucht, was auf einem anderen Port zu senden, wird der Server das nicht wahrnehmen (wollen).
      Allerdings gibt Deine Antwort auch schon IntelliSense und/oder ObjectBrowser:

      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.

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

      @VaporiZed
      Danke schonmal für deine Antwort ;)

      Nun hab ich da mal eine Frage: Undzwar wenn man den Server startet soll dieser zuerst fragen welchen Port er "belauschen" soll wenn ich da jetzt standardmäßig
      "8000" eingebe und den Client öffne kann dieser sich nicht verbinden.

      Das ist der Code im Server (Load):

      VB.NET-Quellcode

      1. Console.WriteLine("Bitte geben Sie einen 4 Stelligen Port ein der ggf auch freigegeben ist.")
      2. Port_Integer = Console.ReadLine()
      3. Console.Clear()


      Das sind die Variablien die ich benutze:

      VB.NET-Quellcode

      1. Private Port_Integer As Integer
      2. Private ipendpoint As IPEndPoint = New IPEndPoint(IPAddress.Any, Port_Integer) ' eingestellt ist port 8000. dieser muss ggf. freigegeben sein!


      Wie schon oben beschrieben funktioniert das mit dem manuellen Eingeben dess Ports nicht doch wenn ich im Code selber die "8000" eingebe gehts.
      Warum ist das so?
      Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

      Für VB.NET Entwickler: Option Strict On nicht vergessen!
      hä? müsste der Code dann nich ehr so aussehen?

      VB.NET-Quellcode

      1. Private Port_Integer As Integer
      2. Console.WriteLine("Bitte geben Sie einen 4 Stelligen Port ein der ggf auch freigegeben ist.")
      3. Port_Integer = Console.ReadLine()
      4. Console.Clear()
      5. Private ipendpoint As IPEndPoint = New IPEndPoint(IPAddress.Any, Port_Integer) ' eingestellt ist port 8000. dieser muss ggf. freigegeben sein!



      Du musst doch zuerst den Port per abfrage bekommen um ihn dan auch weiter im Programmcode benutzen zu können?
      @MVN050
      Ohhh danke
      Echt.. darauf hätte ich aber auch kommen können ist ja klar warum es nicht geht..
      Danke :thumbup:
      Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

      Für VB.NET Entwickler: Option Strict On nicht vergessen!
      Servus,

      der Thread ist jetzt schon so lang und ich finde bisher nicht die Lösung für mein Problem.
      Ich habe den Quellcode aus dem ersten Beispiel mittlerweile in eine eigene Klasse gepackt:


      Form1.vb:

      VB.NET-Quellcode

      1. Public Class Form1
      2. Public srv As New ServerEngine
      3. ...
      4. end class


      ServerEngine.vb:

      VB.NET-Quellcode

      1. Imports System.Net.Sockets
      2. Imports System.IO
      3. Imports System.Net
      4. Imports System.Threading
      5. Public Class ServerEngine
      6. Private mainthread As New System.Threading.Thread(AddressOf ListenToNewConnection)
      7. Private server As TcpListener
      8. Private client As New TcpClient
      9. Private ipendpoint As IPEndPoint = New IPEndPoint(IPAddress.Any, 8000) ' eingestellt ist port 8000. dieser muss ggf. freigegeben sein!
      10. Private list As New List(Of Connection)
      11. Private Structure Connection
      12. Dim stream As NetworkStream
      13. Dim streamw As StreamWriter
      14. Dim streamr As StreamReader
      15. Dim nick As String ' natürlich optional, aber für die identifikation des clients empfehlenswert.
      16. End Structure
      17. Public Sub New()
      18. mainthread.Start()
      19. End Sub
      20. Public Sub ListenToNewConnection()
      21. Form1.TextBoxServerMessages.Text = "Der Server läuft!"
      22. server = New TcpListener(ipendpoint)
      23. server.Start()
      24. While True ' wir warten auf eine neue verbindung...
      25. client = server.AcceptTcpClient
      26. Dim c As New Connection ' und erstellen für die neue verbindung eine neue connection...
      27. c.stream = client.GetStream
      28. c.streamr = New StreamReader(c.stream)
      29. c.streamw = New StreamWriter(c.stream)
      30. c.nick = c.streamr.ReadLine ' falls das mit dem nick nicht gewünscht, auch diese zeile entfernen.
      31. list.Add(c) ' und fügen sie der liste der clients hinzu.
      32. Form1.TextBoxServerMessages.Text = c.nick & " has joined."
      33. ' falls alle anderen das auch lesen sollen können, an alle clients weiterleiten.
      34. Dim t As New Threading.Thread(AddressOf ListenToConnection)
      35. t.Start(c)
      36. End While
      37. End Sub
      38. Private Sub ListenToConnection(ByVal con As Connection)
      39. Do
      40. Try
      41. Dim tmp As String = con.streamr.ReadLine ' warten, bis etwas empfangen wird...
      42. Console.WriteLine(con.nick & ": " & tmp)
      43. For Each c As Connection In list ' an alle clients weitersenden.
      44. Try
      45. c.streamw.WriteLine(con.nick & ": " & tmp)
      46. c.streamw.Flush()
      47. Catch
      48. End Try
      49. Next
      50. Catch ' die aktuelle überwachte verbindung hat sich wohl verabschiedet.
      51. list.Remove(con)
      52. Console.WriteLine(con.nick & " has exit.")
      53. Exit Do
      54. End Try
      55. Loop
      56. End Sub
      57. End Class


      das Problem ist nun, dass ich, sobald sich ein Client verbindet eine Debuggerfehlermeldung bekomme:

      Quellcode

      1. System.Net.Sockets.SocketException
      2. HResult=0x80004005
      3. Nachricht = Normalerweise darf jede Socketadresse (Protokoll, Netzwerkadresse oder Anschluss) nur jeweils einmal verwendet werden
      4. Quelle = System
      5. Stapelüberwachung:
      6. bei System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
      7. bei System.Net.Sockets.Socket.Bind(EndPoint localEP)
      8. bei System.Net.Sockets.TcpListener.Start(Int32 backlog)
      9. bei System.Net.Sockets.TcpListener.Start()
      10. bei SpacePhysics.ServerEngine.ListenToNewConnection() in C:\Users\Richard\source\repos\spacePhysics\SpacePhysics\ServerEngine.vb: Zeile29
      11. bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
      12. bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
      13. bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
      14. bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
      15. bei System.Threading.ThreadHelper.ThreadStart()



      Ich möchte erreichen, dass die ServerMessages in die Textbox auf meiner Form geschrieben werden statt auf die Konsole.
      Was mache ich falsch?

      Für Eure Hilfe bedanke ich mich im Voraus.

      Lieben Gruß

      Richard

      P.S.: Bitte seht mir nach, dass ich den Code nicht als solchen im Editor ausgezeichnet habe. Ich bin Blind und nutze für die Arbeit am Computer einen Screen Reader. Dieser präsentiert die Formatieroptionen des Editors für mich nicht zugänglich, weswegen es nur reiner Text ohne Formatierung geworden ist.

      *BB-Codes eingefügt*

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

      Hallo @Tschapajew

      deiner Fehlermeldung nach gibt es bereits eine Anwendung die auf dem Port 8000 lauscht. Das Problem ist, dass auf einem Port nur eine Anwendung lauschen darf (es gibt jedoch gewisse Außnahmen)

      Das Problem zu beheben ist relativ einfach:
      Finde das Programm das auf den Port lauscht und beende es,
      oder lass dein Programm auf einen anderen Port lauschen.
      Servus,

      nö, daran liegt es nicht. Das problem scheint an meiner Implementierung zu liegen. Scheinbar habe ich irgendwo einen Fehler der dazu führt, dass der Thread auf dem Port zwei mal aufgemacht wird. und die beißen sich dann. Bin für weitere Vorschläge und Hilfestellung weiterhin offen und dankbar.

      LG

      Richard
      @Tschapajew

      wie oft feuerst du das hier ab? Ich gehe davon aus, das du es öfters feuerst.

      VB.NET-Quellcode

      1. Public Sub New()
      2. mainthread.Start()
      3. End Sub


      Das hier wird wohl auch nich funktionieren. du bist in einem Thread und versucht auf den Mainthread zuzugreifen. Die UI ist vom Mainthread geblockt.
      Form1.TextBoxServerMessages.Text = "Der Server läuft!"

      Tschapajew schrieb:

      System.Net.Sockets.SocketException
      HResult=0x80004005
      Nachricht = Normalerweise darf jede Socketadresse (Protokoll, Netzwerkadresse oder Anschluss) nur jeweils einmal verwendet werden

      Der hiesige "MultiServer" ist ziemlich Grütze.

      In TcpKommunikation + Networkstream findest du die Lösung - ich kann sie dir auch auskopieren:

      VB.NET-Quellcode

      1. Private Sub Connect(ByVal EP As IPEndPoint)
      2. _Listener = New TcpListener(EP)
      3. _Listener.ExclusiveAddressUse = False
      4. _Listener.Start()
      5. _Listener.BeginAcceptTcpClient(AddressOf EndAccept, Nothing)
      6. End Sub
      Servus,

      vielen Dank für Eure Antworten, das hat mich auf der Serverseite doch ganz schön weitergebracht, der Läuft nun und wurde von mir sogar um eine Benutzeranmeldung mit md5 codiertem Passwort erweitert. Klappt mit der umgebauten DemoClientAnwendung auch so wie es soll.

      Nun habe ich jedoch versucht, den Client-Code in ein anderes Projekt zu übertragen. Hier bekomme ich jedoch bei Aufruf die Fehlermeldung, dass Me kein Member von ServerConnection sei. Verstehe ich nicht, da ich die Klasse in einem Form mit New instanziert habe. Somit sollte Invoke doch zur Verfügung stehen, oder?
      Hier mal meine Klasse:

      VB.NET-Quellcode

      1. Imports System.ComponentModel
      2. Imports System.Net.Sockets
      3. Imports System.IO
      4. Public Class ServerConnection
      5. Public ConnectionToServerEstablished As Boolean = False
      6. Public MessageCounter As Integer = 0
      7. Private stream As NetworkStream
      8. Private streamw As StreamWriter
      9. Private streamr As StreamReader
      10. Private client As New TcpClient
      11. Private t As New Threading.Thread(AddressOf Listen)
      12. Public Delegate Sub DAddItem(ByVal s As String)
      13. Public nick As String = "unknown"
      14. Public pwd As String = "unknown"
      15. Public Sub ConnectToServer()
      16. Try
      17. client.Connect("127.0.0.1", 8000)
      18. If client.Connected Then
      19. stream = client.GetStream
      20. streamw = New StreamWriter(stream)
      21. streamr = New StreamReader(stream)
      22. streamw.WriteLine(nick)
      23. streamw.WriteLine(pwd)
      24. streamw.Flush()
      25. t.Start()
      26. ConnectionToServerEstablished = True
      27. Else
      28. MessageBox.Show("Verbindung zum Server nicht möglich!")
      29. Application.Exit()
      30. End If
      31. Catch ex As Exception
      32. MessageBox.Show("Verbindung zum Server nicht möglich!")
      33. Application.Exit()
      34. End Try
      35. End Sub
      36. Private Sub Listen()
      37. While client.Connected
      38. Try
      39. Me.Invoke(New DAddItem(AddressOf AddItem), streamr.ReadLine)
      40. Catch
      41. MessageBox.Show("Verbindung zum Server nicht möglich!")
      42. Application.Exit()
      43. End Try
      44. End While
      45. End Sub
      46. Public Sub SendToServer(msg As String)
      47. If msg = Nothing Then
      48. msg = "emptyMessage"
      49. End If
      50. streamw.WriteLine(MessageCounter & ";" & msg)
      51. streamw.Flush()
      52. MessageCounter = MessageCounter + 1
      53. End Sub
      54. End Class


      Ich habe mir ein Form namens StartUp angelegt. Hier lade ich alles, was vor der eigentlichen Programmausführung gebraucht wird, unter Anderem eben auch das Servermodul:

      VB.NET-Quellcode

      1. Public Class StartUp
      2. Public ServerConnectionInterface As New ServerConnection
      3. End Class

      Wo liegt denn da mein Denkfehler?

      Lieben Gruß

      Richard

      *Code BB-Code eingefügt*

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

      Invoke ist ja Bestandteil der Control-Klasse. Aber hier verwendest Du Me innerhalb der ServerConnection-Klasse. Me verweist auf die aktuelle Klasseninstanz. Hier eben auf die ServerConnection-Klasseninstanz. Dass Du innerhalb einer Form-Klasse die ServerConnection instanziierst, ist eine Sache. Aber Du kommst nicht implizit an das Form-Invoke ran. Da müsstest Du schon die Form-Instanz an die ServerConnection-Klasse als Parameter übergeben. Oder ggf. mit Delegaten arbeiten.
      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.