System.StackOverflowException.... Lösungsansatz?

  • VB.NET

Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von Litti.

    System.StackOverflowException.... Lösungsansatz?

    Hallo VB-Freunde,

    seit einiger Zeit hänge ich an einem Problem das ich kenne und auch weiß woran es liegt, nur komm ich net drauf wie man es lösen könnte :(

    Kurz zum Projekt. In unserer Firma stehen mehrere Server die Maschinen steuern.. Unabdingbar ist es immer den Status und andere Informationen zu überwachen und zu verarbeiten von einem Zentral-Server aus.
    Also habe ich eine TCP Server Software programmiert die auf der Zentral-Server läuft und die die Client-Verbdinungen der einzelnen Server annimmt und die Basis- und Maschineninformationen sendet. Alle Server werden mir in der ListBox aufgelistet und ich kann sie fein auswählen und die Informationen verarbeiten.

    So weit alles 1000% :D

    Nun das kleine Problem. Is der Zentral-Server nun nicht mehr erreichbar, durch Neustart-Gründe oder dergleichen, muss natürlich eine "Reconnect"-Methode greifen auf der Client-Seite. Auch das funktioniert einwandfrei..... Aber logischerweise is das ja eine Endlosschleife (Wenn er immer wieder versucht sich zu verbinden).... Logisch das ein System.StackOverflowException entsteht, dieser kann ja auch nicht im Try-Catch abgefangen werden.

    ich poste mal kurz die Clientseite Verbindungsmethode und die Reconnectmethode


    Verbinden-Sub:

    VB.NET-Quellcode

    1. Private Sub MaschineVerbinden()
    2. Try
    3. Me.MaschinenClient= New TcpClient
    4. Me.MaschinenClient.Connect(IPAdresse, Port)
    5. If Me.MaschinenClient.Connected = True Then
    6. Me.stReader = New StreamReader(Me.MaschinenClient.GetStream)
    7. Me.stWriter = New StreamWriter(Me.MaschinenClient.GetStream)
    8. Me.SendeMaschinenStatus() 'Maschinenstatus senden
    9. Dim thEmpfangen As New Thread(AddressOf EmpfangeInformationen) 'Befehle des Zentralservers entgegennehmen
    10. thEmpfangen.IsBackground = True
    11. thEmpfangen.Start()
    12. Else
    13. Reconnect()
    14. End If
    15. Catch ex As Exception
    16. Me.MaschinenClient.Close()
    17. Reconnect()
    18. End Try
    19. End Sub


    und hier die Reconnect-Sub:

    VB.NET-Quellcode

    1. Private Sub ReconnectMaschinenClient()
    2. Me.MaschinenClient.Close()
    3. Thread.Sleep(3000)
    4. MaschineVerbinden()
    5. End Sub


    Gibt es überhaupt eine möglichkeit die Exception zu "umgehen" oder gar einen ganz anderen Lösungsweg?

    LG
    nein ;) wenn Client.Connected = True dann ist er ja verbunden und sendet die Daten, gleichzeitig horcht der Client auf Anfragen vom ZentralServer (zum neuen abrufen von infos oder verändern von Daten)...bricht die verbindung ab greift ja der ELSE-Block und ruft die Reconnect-Sub auf... is der Zentralserver länger nicht ONLINE dann rutscht er ja von der Verbinden-Sub in die Reconnect-Sub und dann wieder von vorn...bis er wieder sich verbindet...dauert dieses aber zu lang kommt ja die exception...das dies ja dann als endlosschleife erkannt wird
    hab ich auch schon versucht aber da es ja dann immer noch eine endlose rekursion ist und der call stack irgendwann mal voll ist, kommt die StackoverflowExeption...

    Mit

    VB.NET-Quellcode

    1. Try
    2. 'code
    3. catch ex as System.StackOverflowException
    4. End Try


    funktioniert es leider auch net die Exception abzufangen
    hab es mal so versucht wie du sagtest

    VB.NET-Quellcode

    1. Private Sub MaschineVerbinden()
    2. Me.MaschinenClient = New TcpClient
    3. Do
    4. If Not Me.MaschinenClient.Connected Then
    5. Me.MaschinenClient.Connect(IPAdresse, Port)
    6. Me.stReader = New StreamReader(Me.MaschinenClient.GetStream)
    7. Me.stWriter = New StreamWriter(Me.MaschinenClient.GetStream)
    8. Me.SendeMaschinenStatus()
    9. Dim thEmpfangen As New Thread(AddressOf EmpfangeInformationen) 'Befehle des Zentralservers entgegennehmen
    10. thEmpfangen.IsBackground = True
    11. thEmpfangen.Start()
    12. Else
    13. Reconnect()
    14. End If
    15. Loop
    16. End Sub


    Aber auch dort kommt leider die StackOverflow Exception.... Ich schätze weil der ELSE-Block auf die Reconnect verweist die dann einfach nur wieder die Verbinden-Sub aufruft.... oder?
    Jetzt steh ich gerade iwie auf dem Schlauch... Kannst du mir kurz auf die Sprünge helfen wie der dann Reconnectet falls die Verbindung "abreißt"? Den wenn er einmal verbunden war und dann die verbindung von zb. TCPServer unterbrochen wurde und der wieder horcht springt die loop schleife ja nicht mehr hoch .... oder hab ich gerad n hänger im kopp ;(
    Der Loop überprüft andauernd ob die Verbindung noch besteht. Wenn nicht wird sie sofort wieder aufgebaut, ohne irgendwelche Methodenaufrufe. Alles, was innerhalb der If-Abfrage steht ist der Reconnect-Code, der aufgerufen wird, wenn keine Verbindung mehr besteht (If Not Me.MaschinenClient.Connected).
    ahhh stimmt klar... da kann ich den else-block quasi ja wegglassen mit der reconnect-sub..... aber allerdings kommt trotzdem der overflow

    VB.NET-Quellcode

    1. Me.MaschinenClient = New TcpClient
    2. Do
    3. If Not Me.MaschinenClient.Connected Then
    4. Me.MaschinenClient.Connect(IPAdresse, Port) ' Hier die Zeile wo die StackOverflowException entsteht
    5. Me.stReader = New StreamReader(Me.MaschinenClient.GetStream)
    6. Me.stWriter = New StreamWriter(Me.MaschinenClient.GetStream)
    7. Me.SendeMaschinenStatus()
    8. Dim thEmpfangen As New Thread(AddressOf EmpfangeInformationen)
    9. thEmpfangen.IsBackground = True
    10. thEmpfangen.Start()
    11. End If
    12. Loop


    PS: Ich sehe du kommst aus Würzburg ;) Grüße Grüße ;) hab bis vor kurzem 2 Jahre dort direkt am Main gewohnt :D
    An dem Code hier liegt das aber sicherlich nicht. Du könntest höchstens mal versuchen ein Thread.Sleep in die Schleife zu setzen um dem ganzen ein bisschen Zeit zu lassen (ist sowieso klug wegen der CPU-Auslastung). Wenn der Fehler danach weiterhin besteht fällt mir leider auch nichts mehr ein, muss dann irgenwie mit dem TCPClient zusammenhängen, damit kenne ich mich aber kaum aus.
    Du hast hier ne epische Rekursion gebastelt, die dir früher oder später halt mit ner StackOverflowException um die Ohren fliegen wird. Nebenbei ist eine solche Exception nicht fangbar, weil es, wenn sie geworfen wird, bereits zu spät für jede Hilfe ist. Du solltest die Struktur des Programms überdenken.
    Hey,

    wenn Du uns den kompletten, relevaten Code zur Verfügung stellst, dann können wir gerne mal drüber guggn.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    Hier mein ursprüngliche Client-Klasse:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Threading
    3. Imports System.Net.Sockets
    4. Public Class Client
    5. Dim MainClient As TcpClient
    6. Dim stReader As StreamReader
    7. Dim stWriter As StreamWriter
    8. Dim Verbunden As Boolean = False
    9. Private IPAdresse As String
    10. Private Port As Integer
    11. Public Sub Beginn(ByVal IPAdresse As String, ByVal Port As Integer)
    12. Me.IPAdresse = IPAdresse
    13. Me.Port = Port
    14. Verbinden()
    15. End Sub
    16. Private Sub Verbinden()
    17. Try
    18. Me.MainClient = New TcpClient
    19. Me.MainClient.Connect(IPAdresse, Port) ' Hier der Stackoverflow nach einigen minuten versuchten Verbindungsaufbau
    20. If Me.MainClient.Connected = True Then
    21. Me.Verbunden = True
    22. RaiseEvent Connected()
    23. Me.stReader = New StreamReader(Me.MainClient.GetStream)
    24. Me.stWriter = New StreamWriter(Me.MainClient.GetStream)
    25. Me.BasisInformationenSenden()
    26. Dim thEmpfangen As New Thread(AddressOf EmpfangeInfo)
    27. thEmpfangen.IsBackground = True
    28. thEmpfangen.Start()
    29. Else
    30. Me.Verbunden = False
    31. Reconnect()
    32. End If
    33. Catch ex As Exception
    34. Me.Verbunden = False
    35. Me.MainClient.Close()
    36. Reconnect()
    37. End Try
    38. End Sub
    39. Sub BasisInformationenSenden()
    40. Me.SendeInfo(LaserMSH1.Status)
    41. Me.SendeInfo(LaserMSH2.Status)
    42. '// Weitere Informationen folgen
    43. End Sub
    44. Private Sub Reconnect()
    45. Me.MainClient.Close()
    46. Thread.Sleep(3000)
    47. Verbinden()
    48. End Sub
    49. Public Sub SendeInfo(ByVal info As String)
    50. Try
    51. Me.stWriter.WriteLine(info)
    52. Me.stWriter.Flush()
    53. Catch ex As Exception
    54. End Try
    55. End Sub
    56. Private Sub EmpfangeInfo()
    57. Try
    58. While Me.Verbunden
    59. Dim info As String
    60. info = Me.stReader.ReadLine
    61. End While
    62. Catch ex As Exception
    63. Me.Verbunden = False
    64. Reconnect()
    65. End Try
    66. End Sub
    67. End Class

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

    Ich würds iwi so machen. Man kann sich natürlich über das Ganze streiten. Es gibt gewisse Regeln, gewisse Vorlieben, andere würden es ganz anders machen. Der Code ist nicht getestet, sollte aber auch nur ein Anschauungsbeispiel sein. Ich würde auf jeden Fall auf Async-Methoden setzten. Diese gehen "schonender" mit den Ressourcen um. Vor allem bei Deinem Server würd ich unbedingt diese Methoden einsetzen. Für nen Client ists aus meiner Sicht nicht so relevant. Was wichtig ist, damit Dir die StackOverFlowEx nicht ständig um die Ohren fliegt, springe mit Exit Sub bei einem erneuten Connect-Versuch raus. Damit sollte sich das Problem erledigen.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Client
    2. Private _client As New TcpClient
    3. Private _serverAddress As String
    4. Private _serverPort As Integer
    5. Private _readStream As Stream
    6. Private _writeStream As Stream
    7. Private _readBuffer(4095) As Byte
    8. Public Event Client_Connected As EventHandler
    9. Public Event Client_NewMessage As EventHandler(Of String)
    10. Public Event Client_Disconnected As EventHandler
    11. Public Sub New(ByVal ip As String, ByVal port As Integer)
    12. _serverAddress = ip
    13. _serverPort = port
    14. End Sub
    15. Private Sub _client_BeginConnect()
    16. If Not _client.Connected Then
    17. _client.BeginConnect(_serverAddress, _serverPort, AddressOf _client_EndConnect, Nothing)
    18. End If
    19. End Sub
    20. Private Sub _client_EndConnect(ByVal ar As IAsyncResult)
    21. Try
    22. _client.EndConnect(ar)
    23. _readStream = _client.GetStream()
    24. _writeStream = _client.GetStream()
    25. _client_StartReceive()
    26. Catch ex As Exception
    27. _client_BeginConnect()
    28. Exit Sub
    29. End Try
    30. End Sub
    31. Private Sub Disconnect_Client()
    32. _readStream.Close()
    33. _writeStream.Close()
    34. _client.Close()
    35. End Sub
    36. Public Sub Connect()
    37. _client_BeginConnect()
    38. End Sub
    39. Private Sub _client_StartReceive()
    40. _readStream.BeginRead(_readBuffer, 0, _readBuffer.Length, AddressOf _client_EndReceive, Nothing)
    41. End Sub
    42. Private Sub _client_EndReceive(ByVal ar As IAsyncResult)
    43. Try
    44. Dim recLen As Integer = _readStream.EndRead(ar)
    45. Dim recMessage As String = ""
    46. If recLen = 0 Then
    47. Throw New Exception()
    48. End If
    49. recMessage = Encoding.UTF8.GetString(_readBuffer, 0, recLen)
    50. _client_StartReceive()
    51. Catch ex As Exception
    52. Disconnect_Client()
    53. End Try
    54. End Sub
    55. Private Sub SendMessageToHost(ByVal message As String)
    56. Try
    57. Using sW As New StreamWriter(_writeStream)
    58. sW.WriteLine(message)
    59. sW.Flush()
    60. sW.Close()
    61. End Using
    62. Catch ex As Exception
    63. Disconnect_Client()
    64. End Try
    65. End Sub
    66. Private Sub OnClient_Connected()
    67. RaiseEvent Client_Connected(Me, EventArgs.Empty)
    68. End Sub
    69. Private Sub OnClient_Disconnecte()
    70. RaiseEvent Client_Disconnected(Me, EventArgs.Empty)
    71. End Sub
    72. Private Sub OnClient_NewMessage(ByVal message As String)
    73. RaiseEvent Client_NewMessage(Me, message)
    74. End Sub
    75. End Class



    Wie gesagt, der Code ist ungetestet und mit Sicherheit fehlerbehaftet und Du solltest ihn nicht Copy&Pasten. Bestenfalls solltest Du Dir hier und da eine Idee abgucken.

    EDIT: Eigentlich sollte diese Ex beim Verwenden der Asnychronen-Connect-Variante auch ohne einen Sprung aus der Methode nicht mehr auftreten.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o

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