TcpClient.ConnectAsync bleibt bei WaitForActivation [gelöst]

  • VB.NET
  • .NET 5–6

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    TcpClient.ConnectAsync bleibt bei WaitForActivation [gelöst]

    Hallo zusammen.

    Ich habe eine .NET-Framework-DLL, die mir eine TCP-Verbindung aufbaut. Der problematische Teil davon lautet:

    VB.NET-Quellcode

    1. Dim ConnectionTask = TcpClient.ConnectAsync(ServerName, PortToUse).ContinueWith(Sub(x) If x.IsFaulted Then TcpClient.Dispose, TaskContinuationOptions.ExecuteSynchronously)
    2. Dim TimeoutTask = Task.Delay(5000).ContinueWith(Sub(x) TcpClient.Dispose)
    3. Task.WhenAny(ConnectionTask, TimeoutTask).Wait()

    Eine Race-Condition wird also gestartet. Wenn der Client sich mit dem Server verbinden kann, ist alles gut, ansonsten (nach 5 Sekunden) eben nicht -> Fehlermeldung

    Ich habe also eine DLL-Methode X, die den o.g. Code enthält.

    Das komische/Problem ist:
    Wenn ich X von einem .NET 6-Projekt ausführe, hat ConnectionTask nach Zeile#3 den Status WaitForActivation, der Task wurde also nicht gestartet.
    Wenn ich X von einem .NET-FX 4.8-Projekt ausführe, hat ConnectionTask nach Zeile#3 den Status RanToCompletion, der Task wurde also gestartet.

    Was mache ich falsch?
    Hintergrund: Ich will ein .NET-FX-Projekt nach .NET 6 portieren.

    Vielleicht versteh ich bis heute Tasks einfach nicht richtig.

    ##########

    Es ist kein Problem der DLL. Wenn ich den Code in einem neuen Projekt erstelle, ist das Verhalten auch vom Projekttyp abhängig.

    VB.NET-Quellcode

    1. Imports System.Net.Sockets
    2. Imports System.Threading.Tasks
    3. Public Class Form1
    4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    5. Dim Client = New TcpClient
    6. Dim ConnectionTask = Client.ConnectAsync(MeinPcName, PortDerGegenstelle).ContinueWith(Sub(x) If x.IsFaulted Then Client.Dispose(), TaskContinuationOptions.ExecuteSynchronously)
    7. Dim TimeoutTask = Task.Delay(5000).ContinueWith(Sub(x) Client.Dispose())
    8. Task.WhenAny(ConnectionTask, TimeoutTask).Wait()
    9. Stop
    10. End Sub
    11. End Class


    .NET -> WaitForActivation, also: Task wurde nicht gestartet
    .NET-FX -> RanToCompletion, also: Task wurde gestartet
    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 3 mal editiert, zuletzt von „VaporiZed“ ()

    Hallo,
    wenn du mit asynchronen Methoden arbeitest, solltest du auch gleich mit dem async/await Pattern arbeiten. Der Tcp-Client wirft u.a. eine SocketException, wenn keine Verbindung hergestellt werden kann. Das lässt sich mit Try/Catch abfangen.

    Beispielhaft mal hier dargestellt

    VB.NET-Quellcode

    1. ​Public Class Form1
    2. Private Sub ButtonConnect_Click(sender As Object, e As EventArgs) Handles ButtonConnect.Click
    3. ConnectClient()
    4. End Sub
    5. Private Async Sub ConnectClient()
    6. Using tcpClient As New TcpClient()
    7. Try
    8. Await tcpClient.ConnectAsync("localhost", 8080)
    9. Using s = tcpClient.GetStream()
    10. 'Await s.WriteAsync(...)
    11. End Using
    12. Catch se As SocketException
    13. ' Handle no Connection
    14. MessageBox.Show(se.Message)
    15. Catch ex As Exception
    16. ' Handle other Exceptions
    17. Finally
    18. If tcpClient.Connected Then tcpClient.Close()
    19. End Try
    20. End Using
    21. End Sub
    22. End Class
    Ich verwende Async/Await immer wieder und verlinke EdRs Thread auch immer mal wieder in vbp-Threads. Ich brauch aber einen Timeout. Es bringt mir nix, ewig und 3 Tage auf den Server zu warten. Meine Programme sollten nach x Sekunden einfach aufgeben und sagen: »Sorry, Server(App) ist wohl nicht ereichbar. Prüf mal den Zustand und die Verbindungskonfiguration.« Das funktioniert ja auch in .NET-FX.
    Das hab ich auch schon anders gelöst gehabt. 5-Sekunden-Warteschleife und wiederholte Überprüfung des Verbindungsstatus, Einbau einer CancellationTokenSource zum Abbruch eines Wartethreads usw.
    Das ist ja auch nicht das Problem. Der Unterschied in der Ausführung zwischen .NET-FX und .NET ist mein Problem.

    ##########

    Aaallerdings hat mich Dein Beispielcode insofern schonmal weitergebracht, indem ich sehen konnte, dass 5 Sekunden Wartezeit nicht ausreichen, um über das .NET-Programm so eine Verbindung zu erzeugen. Es dauert länger. Während in .NET-FX das in weniger als 0,2 Sekunden erledigt ist. Warum auch immer :cursing:
    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“ ()

    Hallo,
    Ich habe mal geschaut, was der Unterschied zwischen der Implementierung in .NET 6 zum alten Framework ist. Das Problem liegt wohl in der Namensauflösung unter Windows, die (schon länger; .Net Core 2.1) asynchrone Win32-Apis verwendet. Ich habe dazu bei Github diese Issues gefunden: #1 #2. Zum Test kannst du mal anstelle deines PC-Namens die IP-Adresse direkt verwenden; dann ist es genau so schnell wie unter .NET 4.x.

    VB.NET-Quellcode

    1. ​Await tcpClient.ConnectAsync("127.0.0.1", 8080)
    Ah ja, damit läuft es jetzt fix. Muss mal schauen, wie das dann im Netzwerk am besten läuft, wenn die Gegenstelle per Computernamen oder IP angesprochen wird.
    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.