TcpClient.BeginConnect

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von MarvinKleinMusic.

    TcpClient.BeginConnect

    Hallo zusammen,

    folgender Sachverhalt:

    Ich habe einen Listener, der auf eine IP des Servers auf dem Port 5698 hört.

    Der Server wird per TcpClient in C# kontaktiert.

    C#-Quellcode

    1. IPAddress[] hostIPs = Dns.GetHostEntry(_host).AddressList.Where(addr => addr.AddressFamily == AddressFamily.InterNetwork).ToArray();
    2. try
    3. {
    4. Console.WriteLine("Verbinden mit {0}:{1}", _host, _port);
    5. IAsyncResult ar = connection.BeginConnect(hostIPs, _port, null, connection);
    6. WaitHandle wh = ar.AsyncWaitHandle;
    7. if (wh.WaitOne(timeout,true))
    8. {
    9. Console.WriteLine("Verbunden mit {0}:{1}", ((IPEndPoint)connection.Client.RemoteEndPoint).Address, _port);
    10. IsConnected = true;
    11. }
    12. wh.Close();
    13. }


    Die Variabel _host spielgelt in diesem Fall den Servernamen wider, wovon alle IPv4 Adressen geholt werden.

    Nun ist das Problem, dass der Verbindungsaufbau ewig dauert. Ich vermute dass es daran liegt, dass er zuerst die IP verbinden möchte, welche nicht auf dem Port lauscht.

    Wie schaffe ich es nun, dass er pro IP in hostIPs maximal n Sekunden versucht eine Verbindung aufzubauen?

    LG Marvin

    *Topic verschoben*

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

    Du musst es selbst einzeln machen, denn Sockets ansich haben erst einmal kein timeout für den connect. Deshalb muss man auch hier den aufwand über das WaitHandle mahen, da wir hier aktiv warten und einfach nach einer gewissen Zeit den Verbindungsversuch abbrechen...

    Also einfach für jede der hostIPs dasselbe wie dein Code oben machen nur jeweils einzeln.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hallo jvbsl,

    vielen Dank für deine schnelle Antwort.

    Ich habe es nun wie folgt geändert:

    C#-Quellcode

    1. IPAddress[] hostIPs = Dns.GetHostEntry(_host).AddressList.Where(addr => addr.AddressFamily == AddressFamily.InterNetwork).ToArray();
    2. try
    3. {
    4. foreach (IPAddress IP in hostIPs)
    5. {
    6. using (Socket tmpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    7. {
    8. Console.WriteLine("Verbinden mit {0}:{1}", _host, _port);
    9. IAsyncResult ar = tmpSocket.BeginConnect(hostIPs, _port, null, tmpSocket;
    10. WaitHandle wh = ar.AsyncWaitHandle;
    11. if (wh.WaitOne(timeout, true))
    12. {
    13. Console.WriteLine("Verbunden mit {0}:{1}", IP, _port);
    14. IsConnected = true;
    15. connection = tmpSocket;
    16. break;
    17. }
    18. wh.Close();
    19. }
    20. }
    21. }
    22. catch (SocketException ex)
    23. {
    24. ...
    25. }



    Das Problem ist aber nun wie folgt. Es lädt mein angebenes Intervall lang, doch er kann innerhalb des Intervalls keine Verbindung aufbauen. Lasse ich ihn länger suchen 20-30 Sekunden, dann klappt es.

    Woran kann es liegen, dass der Aufbau solange braucht? Zwischenzeitlich funktioniert es sogar innerhalb weniger Sekunden. Der Server ist im lokalen Netz und der Port ist auch freigegeben.
    Viele Grüße,
    Marvin Klein
    Um wie viel Zeit handelt es sich denn die benötigt wird?

    Ich glaube auch beim warten auf das WaitHandle sollte der zweite Parameter false sein, ich denke aber eigt. nicht, dass dies einen Einfluss auf die Verbindungsdauer hat. Und kann echt nicht wirklich dazu etwas sagen, weil ich bei lokalen Servern bisher nie lange Verbindungszeiten hatte. Kann es vlt. sein, dass es am Server Code liegt?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hallo nochmal,

    exakt nach 20 Sekunden baut er mittels

    Quellcode

    1. connection.Connect(_host,_port);


    die Verbindung erfolgreich auf.


    Über den Servernamen erhalte ich folgende IPs 192.168.0.16 und 192.168.0.15

    Der Server läuft auf 192.168.0.15:5698. Beim Connect versucht er allerdings immer die 16 als erstes, was logischerweise dann auch nicht funktioniert. Ich schätze mal, dass der default Timeout also bei 20 Sekunden liegen muss.

    Rufe ich nun

    Quellcode

    1. connection.Connect("192.168.0.15", _port);


    auf, dann ist die Verbindung auch direkt hergestellt. Nun stellt sich mir die Frage, wieso ich mit dem Skript aus Post 3 keine Verbindung aufbauen kann, obwohl es doch mittels Connect direkt funktioniert (Sofern die IP stimmt).

    Hier der Code, der am Server das Socket empfängt.

    C#-Quellcode

    1. /// <summary>
    2. /// Der Server-Thread, der neue TcpClients empfängt
    3. /// </summary>
    4. public void Run()
    5. {
    6. while (true)
    7. {
    8. // Wartet auf eingehenden Verbindungswunsch
    9. TcpClient c = listener.AcceptTcpClient();
    10. // Initialisiert und startet einen Server-Thread
    11. // und fügt ihn zur Liste der Server-Threads hinzu
    12. ServerThread clientThread = new ServerThread(c)
    13. {
    14. VerbindungsID = verbindungsID
    15. };
    16. verbindungsID++;
    17. AufgabenServerEventLog.WriteEntry(clientThread.IP + " hat eine Verbindung aufgebaut", EventLogEntryType.Information, 200);
    18. clientThread.ClientDisconnected += ClientThread_ClientDisconnected; // Event abonnieren
    19. ClientThreads.Add(clientThread);
    20. }
    21. }


    Bin für jeden Hinweis dankbar!

    LG Marvin
    @MarvinKleinMusic Wenn Du vorher ein Ping() aufrufst, kannst Du die nicht vorhandenen Adressen wesentlch schneller ermitteln.
    docs.microsoft.com/de-de/dotne…twork_Ping_System_String_
    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!
    Nur das nicht unbedingt alles auf einen ICMP Request antwortet...

    Hast du das mit WaitOne(...,false) mal probiert? Könnte vlt. auch heißen, dass er bei true auf den thread-exit wartet, bin aber nicht sicher ob ich das richtig verstanden hab...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    RodFromGermany schrieb:

    @MarvinKleinMusic Wenn Du vorher ein Ping() aufrufst, kannst Du die nicht vorhandenen Adressen wesentlch schneller ermitteln.


    Das hilt mir leider nicht weiter. Die Adressen sind beide Erreichbar. Doch nur auf einer wird über meinen benötigten Port gelauscht.

    jvbsl schrieb:

    Nur das nicht unbedingt alles auf einen ICMP Request antwortet...

    Hast du das mit WaitOne(...,false) mal probiert? Könnte vlt. auch heißen, dass er bei true auf den thread-exit wartet, bin aber nicht sicher ob ich das richtig verstanden hab...


    Bereits ausprobiert. Leider auch keine Änderung am Verhalten erkennbar.

    LG Marvin
    Hallo nochmal,

    ich habe es nun gelöst. Scheinbar hatte das Using dazwischengefunkt.

    Mit folgendem Code funktioniert es nun.

    C#-Quellcode

    1. // Wir holen alle IPv4 Adressen des Hosts
    2. IPAddress[] hostIPs = Dns.GetHostEntry(_host).AddressList.Where(addr => addr.AddressFamily == AddressFamily.InterNetwork).ToArray();
    3. try
    4. {
    5. foreach (IPAddress IP in hostIPs)
    6. {
    7. connection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    8. Console.WriteLine("Verbinden mit {0}:{1}", _host, _port);
    9. IAsyncResult ar = connection.BeginConnect(IP, _port, null, connection);
    10. WaitHandle wh = ar.AsyncWaitHandle;
    11. if (wh.WaitOne(timeout, true))
    12. {
    13. Console.WriteLine("Verbunden mit {0}:{1}", IP, _port);
    14. IsConnected = true;
    15. if(StillConnected) // Prüfen ob die Verbindung noch besteht, da Virtuelle Netzwerk Adpater ggf. alles annehmen, aber dann nicht verwalten und direkt disconnecten
    16. break;
    17. }
    18. wh.Close();
    19. connection.Close();
    20. }
    21. }
    22. catch (SocketException ex)
    23. {..}


    Mittels hostIPs = hostIPs.Reverse().ToArray(); konnte ich es testen.
    Es wird für 3 Sekunden versucht bei der falschen IP zu connecten, dann springt er zur nächsten.

    LG Marvin
    mach das Reverse lieber in Zeile 2 vor dem Where, das dürfte wesentlich performanter sein, weil er dann nicht auf die komplette Liste warten muss und du andererseits nicht zweimal Array generierst, sondern nur einmal. Also performanter und speicher sparender zugleich. Eine besser Kombi gibts nicht :D


    Das mit dem Using ergibt tatsächlich Sinn, da wär ich aber nie drauf gekommen(zumindest nicht ohne auch zu probieren)...

    Aber das connection.Close macht keine Probleme? Wie sieht es denn mit connection.Dispose aus?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    ich versteh das mit der Waithandle nicht.
    Verstehe ich das richtig, dass da mit .BeginConnect ein nebenläufiger Vorgang gestartet wird, und mit .WaitOne() dann gewartet wird, bisser fertig ist?
    Wenn dem so ist, kann man die Nebenläufigkeit auch weglassen, denn dass gewartet wird bis fertig ist das typische Verhalten linearer Programmierung.

    ErfinderDesRades schrieb:

    ich versteh das mit der Waithandle nicht.
    Verstehe ich das richtig, dass da mit .BeginConnect ein nebenläufiger Vorgang gestartet wird, und mit .WaitOne() dann gewartet wird, bisser fertig ist?
    Wenn dem so ist, kann man die Nebenläufigkeit auch weglassen, denn dass gewartet wird bis fertig ist das typische Verhalten linearer Programmierung.


    Der Sinn ist genauso wie jvbsl es beschrieben hat. Würde ich das ganze Linear durchgehen und direkt warten, dann brauch er auch bei ungültigen Verbindungsdaten den vollen Timeout für den Socket Connect.

    Ich habe den Code nun allerdings noch weiter ausgelagert, sodass der Verbindungsaufruf über einen Task geschieht, welcher asynchron beim Programmstart ausgeführt wird. Dadurch lassen sich alle benötigten Sachen beim Start schneller Initialisieren, weil ich nun nicht mehr auf die Verbindung warten muss.

    LG Marvin