Server verliert Performance, während clients sich authentifizieren =/

  • VB.NET

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von MrTweek.

    Server verliert Performance, während clients sich authentifizieren =/

    Mein Server verliert, wenn sich Clients anmelden drastisch an Performance, bei 1 Client fällt das nicht auf, aber wenn wir 30 Connecten lassen, dann ist das merklich Spürbar... hat da jemand ideen? =(


    Der Server hat für jede Connection einen Listening-Thread...

    VB.NET-Quellcode

    1. Private Sub ListenToConnection(ByVal con As Object)
    2. Dim message As String = ""
    3. Dim _con = CType(con, Connection)
    4. Do
    5. Try
    6. message = _decrypt(_con.streamr.ReadLine)
    7. Catch ex As Exception
    8. RaiseEvent ConnectionRemoved(_con)
    9. End Try
    10. If message <> "" Then
    11. If Me.InvokeRequired = True Then
    12. Me.Invoke(New Delegated_Receive(AddressOf _Receive), _con, message)
    13. Else
    14. _Receive(_con, message)
    15. End If
    16. End If
    17. Loop
    18. End Sub


    und wenn daten empfangen werden, dann landen sie hier ^^

    VB.NET-Quellcode

    1. Private Sub _Receive(ByVal con As Connection, ByVal Message As String)
    2. If Message <> "" Then
    3. If Message.StartsWith("!Auth*" & con.cguid) Then
    4. Dim _authstring As String = Mid(Message, InStr(Message, "*", CompareMethod.Text) + 1)
    5. For i = 0 To ListView1.Items.Count - 1
    6. If CInt(ListView1.Items.Item(i).Text) = con.id Then
    7. ListView1.Items.Item(i).SubItems(4).Text = _authstring.ToString
    8. con.cguid = _authstring.ToString
    9. con.cname = "Client-" & con.cguid
    10. ListView1.Items.Item(i).SubItems(1).Text = con.cname.ToString
    11. ListView1.Items.Item(i).SubItems(3).Text = "Ermitteln des Benutzernamens"
    12. ListView1.Refresh()
    13. SendToClient(CInt(con.id), "?osuser*" & ListView1.Items.Item(i).SubItems(4).Text)
    14. End If
    15. Next
    16. ElseIf Message.StartsWith("!nvclient_closing*" & con.cguid) Then
    17. If Me.InvokeRequired = True Then
    18. Me.Invoke(New Delegated_removeclient(AddressOf Close_Connection), con)
    19. Else
    20. Close_Connection(con)
    21. End If
    22. ElseIf Message.StartsWith("!osuser*" & con.cguid) Then
    23. Dim _username As String = Mid(Message, InStr(Message, "#", CompareMethod.Text) + 1)
    24. For i = 0 To ListView1.Items.Count - 1
    25. If CInt(ListView1.Items.Item(i).Text) = con.id Then
    26. ListView1.Items.Item(i).SubItems(6).Text = _username
    27. con.clientuser = _username
    28. ListView1.Items.Item(i).SubItems(3).Text = "Ermitteln des Betriebssystems"
    29. ListView1.Refresh()
    30. SendToClient(CInt(con.id), "?osversion*" & ListView1.Items.Item(i).SubItems(4).Text)
    31. End If
    32. Next
    33. ElseIf Message.StartsWith("!osversion*" & con.cguid) Then
    34. Dim _osversion As String = Mid(Message, InStr(Message, "#", CompareMethod.Text) + 1)
    35. For i = 0 To ListView1.Items.Count - 1
    36. If CInt(ListView1.Items.Item(i).Text) = con.id Then
    37. ListView1.Items.Item(i).SubItems(5).Text = _osversion
    38. ListView1.Items.Item(i).SubItems(3).Text = "Ermitteln der Client-Version..."
    39. SendToClient(CInt(con.id), "?nvclientver*" & ListView1.Items.Item(i).SubItems(4).Text)
    40. ListView1.Refresh()
    41. End If
    42. Next
    43. ElseIf Message.StartsWith("!nvclientver*" & con.cguid) Then
    44. Dim _nvcver As String = Mid(Message, InStr(Message, "#", CompareMethod.Text) + 1)
    45. For i = 0 To ListView1.Items.Count - 1
    46. If CInt(ListView1.Items.Item(i).Text) = con.id Then
    47. ListView1.Items.Item(i).SubItems(8).Text = _nvcver
    48. con.clientos = _nvcver
    49. ListView1.Items.Item(i).SubItems(3).Text = "Verbunden..."
    50. ListView1.Refresh()
    51. End If
    52. Next
    53. If Me.InvokeRequired = True Then
    54. Me.Invoke(New _Delegated_writeToLog(AddressOf _writeToLog), con.cname & " hat sich mit uns verbunden!")
    55. Else
    56. _writeToLog(con.cname & " hat sich mit uns verbunden!")
    57. End If
    58. End If
    59. End If
    60. End Sub

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

    Was ist Dein Problem?
    Dass es zu lange dauert oder dass Du Deine GUI nicht mehr bedienen kannst?
    Lagere den Code in einen BackgroundWalker aus.
    ---------------------------
    Option Strict On solltest Du allen Deinen Projekten geben:
    Studio -> Extras -> Optionen
    Projekte und Projektmappen -> VB-Standard
    On / On / Binary / On
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Mein Problem ist folgendes... ich lasse den Client (von einem Laptop), zum test mittels For-Schleife 30 mal verbinden jeweils immer (Sleep(10))



    Macht er auch alles Problemlos... aber dann merkt man, dass er Ewig braucht die ganze routine zu durchlaufen...


    Der Client, meldet dass er fertig sei (mEssages werden auch vom Server gesendet (jeweils mit der richtigen System.Guid, die den Client identifiziert)

    aber der Server meint laut Gui, er habe gerade mal 5 Verbindungen (nach kurzer zeit hat er sie dann auch ... ) und der Client schon 10



    RodFromGermany schrieb:

    Lagere den Code in einen BackgroundWalker aus.




    und wie müsste ich den dann initieren? =( der wirft doch dann exception, wenn man ihn mehrmals startet =(
    Dann starte pro Connection einen.
    Mach Dir eine Klasse, die von BGW erbt oder die einen BGW als Member hat und übergib der mit New erstellten Instanz die jeweiligen Daten. Die BGW starten dann und melden die Verbindung an, wenn sie fertig sind. Das Hauptprogramm muss dann nur noch zählen und bei der richtigen Bedingung die Controls enablen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Du meinst in ListeningToConnection? oder wo... könnte den auch mittels structure einbinden =)

    VB.NET-Quellcode

    1. Public Structure Connection
    2. Dim bgw as Backgroundworker ' <--------- So meine ich
    3. End Structure

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „MrTweek“ ()

    Keine Struktur.
    Mach Dir eine ordentliche Klasse, in der genau eine Authentifizierung durchgeführt wird.
    Strukturen haben manchmal ein sonderliches Verhalten, weil die Instanzen nicht mit New erstellt werden müssen.

    VB.NET-Quellcode

    1. Public Class Connection
    2. Private BGW As BackgroundWorker
    3. ' ...
    4. Public Sub Init(bla As String) ' oder so
    5. ' ...
    6. End Sub
    7. ' ...
    8. End Class
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Gelöschter Benutzer schrieb:

    hat der tcpclient nicht events?
    Leider nein.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hab das mal umgebaut, aber es lag glaube an dem Problem mit den Exceptions, die er warf (beim test mit 100 Clients) und mit try-catch abfing... auch glaube ich dass es an den Ständigen Listview.refresh lag =/
    Auch 100 Clients können schneller verbinden und werden nun sehr schnell verworfen, da der ganze Server nun in einer Klasse ist. Jetzt zum Problem, Der Server schmeißt bei jedem Disconnect eine IO-Exception x(
    Undzwar hier:

    VB.NET-Quellcode

    1. If client.sclient.Client.Connected And client.streamr.BaseStream.CanRead Then
    2. client.incoming_data = client.streamr.ReadLine ' <-------------------- IO-Exception passiert hier =(
    3. Else
    4. Remove_Client(client)
    5. Exit Do
    6. End If


    Das Direktfenster...

    Quellcode

    1. Eine Ausnahme (erste Chance) des Typs "System.IO.IOException" ist in System.dll aufgetreten.
    2. Eine Ausnahme (erste Chance) des Typs "System.IO.IOException" ist in System.dll aufgetreten.
    3. Eine Ausnahme (erste Chance) des Typs "System.IO.IOException" ist in System.dll aufgetreten.
    4. Eine Ausnahme (erste Chance) des Typs "System.IO.IOException" ist in System.dll aufgetreten.
    5. Eine Ausnahme (erste Chance) des Typs "System.IO.IOException" ist in System.dll aufgetreten.
    6. Eine Ausnahme (erste Chance) des Typs "System.IO.IOException" ist in System.dll aufgetreten.... Und so weiter...

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

    Solange Du probleme hast, solltest Du diese klären und nicht 100 Instanzen davon anlegen :!:
    Wie sieht denn nun die Klasse aus, wovon client eine Instanz ist?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also bei 8 oder 9 Instanzen tritt der Fehler nicht auf?
    Tritt er bei 10 Instanzen ein Mal oder mehrfach auf?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Pro Client, gibts 1 Exception, dixconnected nur 1 Client, bleibt das Exception-frei (ohne-Try-Catch)



    Simulation eines 24-Reconnects x)

    hier ist die Sub...



    Spoiler anzeigen


    VB.NET-Quellcode

    1. Protected Sub ListenToConnection(ByVal client As Client)
    2. Dim message As String = ""
    3. Dim MessageBytes() As Byte = Nothing
    4. guid = New System.Guid
    5. client.isListening = True
    6. Do Until client.sclient.Connected = False
    7. Try
    8. client.incoming_data = client.streamr.ReadLine ' <-------------------- IO-Exception passiert hier =(
    9. Catch ex As IOException
    10. client.isListening = False
    11. MessageBytes = Nothing
    12. message = ""
    13. Remove_Client(client)
    14. Exit Do
    15. End Try
    16. If Not client.incoming_data Is Nothing Then
    17. MessageBytes = FromBase64String(client.incoming_data)
    18. message = Encoding.Default.GetString(MessageBytes)
    19. If message <> "" Then
    20. If Not client.isAuthed Then
    21. If message.StartsWith("!Auth*") Then
    22. If guid.TryParse(Mid(message, InStr(message, "*", CompareMethod.Text) + 1), guid) Then
    23. client.cguid = Mid(message, InStr(message, "*", CompareMethod.Text) + 1)
    24. client.cname = "Client-" & client.cguid
    25. If Not client.cguid = "" Or Not client.cguid Is Nothing Then
    26. SendToClient(CInt(client.id), "?osuser*" & client.cguid)
    27. Else ' Ich glaube nicht, dass dieser Client zu uns gehört!
    28. Remove_Client(client)
    29. Exit Do
    30. End If
    31. Else ' Nee... da ist wohl jemand, der uns veräppeln will =/
    32. Remove_Client(client)
    33. Exit Do
    34. End If
    35. ElseIf message.StartsWith("!osuser*" & client.cguid) Then
    36. If Not Mid(message, InStr(message, "#", CompareMethod.Text) + 1) = "" Then
    37. client.clientuser = Mid(message, InStr(message, "#", CompareMethod.Text) + 1)
    38. If Not client.cguid = "" Or Not client.cguid Is Nothing Then
    39. SendToClient(client.id, "?osversion*" & client.cguid)
    40. Else
    41. Remove_Client(client)
    42. Exit Do
    43. End If
    44. Else
    45. Remove_Client(client)
    46. Exit Do
    47. End If
    48. ElseIf message.StartsWith("!osversion*" & client.cguid) Then
    49. If Not Mid(message, InStr(message, "#", CompareMethod.Text) + 1) = "" Then
    50. client.clientos = Mid(message, InStr(message, "#", CompareMethod.Text) + 1)
    51. If Not client.cguid = "" Or Not client.cguid Is Nothing Then
    52. SendToClient(CInt(client.id), "?nvclientver*" & client.cguid)
    53. Else
    54. Remove_Client(client)
    55. Exit Do
    56. End If
    57. Else
    58. Remove_Client(client)
    59. Exit Do
    60. End If
    61. ElseIf message.StartsWith("!nvclientver*" & client.cguid) Then
    62. If Not Mid(message, InStr(message, "#", CompareMethod.Text) + 1) = "" Then
    63. client.clientos = Mid(message, InStr(message, "#", CompareMethod.Text) + 1)
    64. client.isAuthed = True
    65. RaiseEvent Client_Connected(client.id, client.cname, client.remoteIP, client.cguid, client.clientos, client.clientuser)
    66. Else
    67. Remove_Client(client)
    68. Exit Do
    69. End If
    70. Else ' Ein Client sollte diesen Punkt niemals erreichen!
    71. Remove_Client(client)
    72. Exit Do
    73. End If
    74. Else
    75. RaiseEvent Datareceived(client.id, message)
    76. End If
    77. End If
    78. Else
    79. client.isListening = False
    80. MessageBytes = Nothing
    81. message = ""
    82. Remove_Client(client)
    83. Exit Do
    84. End If
    85. Loop
    86. End Sub


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

    Ich denke, du solltest nicht für jede Connection einen Thread verwenden.
    Sowohl der TcpListener als auch der TcpClient haben Asynchrone Methoden.
    Diese sind wesentlich resourcenschonender, weil du nicht zig Threads verwenden musst.
    Orientiere dich ggf. an dem Chat Versuch vom ErfinderDesRades!

    Gruss Mono
    Das ist meine Signatur und sie wird wunderbar sein!
    hab mal getestet mit 50 Clients, da steigt es auf 100MB (RAM), bei 1000 sind es gleich 1000MB! =(

    Mit der alten Variante... mit der von mir im expander-gepostetetn, liege ich ca bei 89MB und bei 1000 (854)

    Die von mir verwendete Klasse basiert auf dem TCP-Multiserver von Kevin89 =(

    ich werde mich mal darauf hinausbewegen, es asyncron zu machen, muss da viel umgebaut werden? =(

    MrTweek schrieb:

    bei 1000 sind es gleich 1000MB!

    Klar. Jeder managed thread braucht IMMER 1MB RAM. Das ist eine Eigenheit von Net, denn normalerweise MUSS ein Thread seine 1MB Speicher nicht komplett committen ... das Net Framework macht es aber aus "Sicherheitsgründen".

    Wobei ein Speicherverbrauch von 1 GB bei EINTAUSEND angemeldeten Usern theoretisch auch zu verkraften wäre. imho.

    picoflop schrieb:

    MrTweek schrieb:

    bei 1000 sind es gleich 1000MB!

    Klar. Jeder managed thread braucht IMMER 1MB RAM. Das ist eine Eigenheit von Net, denn normalerweise MUSS ein Thread seine 1MB Speicher nicht komplett committen ... das Net Framework macht es aber aus "Sicherheitsgründen".

    Wobei ein Speicherverbrauch von 1 GB bei EINTAUSEND angemeldeten Usern theoretisch auch zu verkraften wäre. imho.



    Das mag sein aber:

    1. Warum habe ich einen Verbauch (gemessen im TaskMGR) bei 1000 Verbindungen 1GB - ca 1,1 GB und bei 50 nur ca knapp 850 (Leerlauf mit jeweils clients)?
    2. Hat nicht jeder mal eben 8GB-Ram und evtl hat man lust das Programm auf Windows 7 oder Windows Server 2008 laufen zu lassen... (Was zusätzlich wenn man X64 nutzt) seine 2,3GB sehen will
    wobei 2. das eigentliche Manko ist =(