Händisches Timeout für Socket

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

Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Händisches Timeout für Socket

    Hallo zusammen,

    vielleicht kann mir ja Jemand von euch weiterhelfen.

    Ich habe einen Server, welcher den Zugriff auf die Datenbank ausübt und einen Client, der mittels API Daten vom Server abfragt. Dabei werden Objekte beim Versand mittels Base64 serialisiert und beim Empfänger wieder deserialisiert.

    Soweit so gut, doch nun gibt es größere Objekte, welche je nach Bandbreite unterschiedlich lange für den Empfang brauchen. Nun habe ich das Problem, dass hier nun die Übertragung aufgrund eines manuellen Timeouts abgebrochen wird. Hier mal eine Funktion der API:

    C#-Quellcode

    1. public Aufgabe GetAufgabe(long A_NR, bool istAktiveAufgabe = true)
    2. {
    3. if (!IsLoggedIn)
    4. throw new Exceptions.LoginRequiredException("Aktion erfordert Login am AufgabenServer. Bitte loggen Sie sich ein.");
    5. var clientProtokoll = new ClientProtokoll<RequestAufgabeNachricht>(new RequestAufgabeNachricht(A_NR, istAktiveAufgabe));
    6. Stopwatch watch = new Stopwatch();
    7. Aufgabe result = null;
    8. bool empfangen = false;
    9. EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
    10. void handler(object sender, CustomEventArgs e)
    11. {
    12. if (e.AnfrageId == clientProtokoll.AnfrageId)
    13. {
    14. if (e.Nachricht is ServerProtokoll<ObjectNachricht> objNachricht)
    15. {
    16. watch.Stop();
    17. empfangen = true;
    18. result = (Aufgabe)objNachricht.Nachricht.Value;
    19. ewh.Set();
    20. }
    21. }
    22. }
    23. NachrichtenQueue.NachrichtReceived += handler;
    24. watch.Start();
    25. clientProtokoll.Send(ServerConnection.Connection);
    26. ewh.WaitOne(Timeout);
    27. NachrichtenQueue.NachrichtReceived -= handler;
    28. Debug.WriteLine("Antwort erhalten in: " + watch.ElapsedMilliseconds + " ms");
    29. return result;
    30. }


    Das Timeout ist hier 3 Sekunden. Die Übertragung dauert allerdings ca 6 Sekunden. Nun möchte ich nicht einfach das Timeout höher setzen, sondern er soll einfach solange das Timeout wiederholen, bis die Nachricht empfangen wurde oder noch ein Empfang einer Nachricht stattfindet.

    Mein Ansatz sieht hier so aus:

    C#-Quellcode

    1. do
    2. {
    3. ewh.WaitOne(Timeout);
    4. } while (!empfangen && ServerConnection.IsReceivingData);



    Der Empfang findet in einem Separaten Thread statt. Das Flag IsReceivingData wird dabei wie folgt gesetzt:

    C#-Quellcode

    1. List<byte> bytesReceived = new List<byte>();
    2. while (IsConnected && !stop)
    3. {
    4. try
    5. {
    6. // Stream zum lesen holen
    7. while (Connection.Available > 0 && Connection.Connected)
    8. {
    9. IsReceivingData = true;
    10. int size = (Connection.Available > Int32.MaxValue) ? Int32.MaxValue : Connection.Available;
    11. byte[] nextByte = new byte[size];
    12. Connection.Receive(nextByte, 0, size, SocketFlags.None);
    13. bytesReceived.AddRange(nextByte);
    14. }
    15. Thread.Sleep(10); // Nach jedem Durchgang warten wir 10ms, damit die CPU Auslastung nicht durchs permanente Abfragen bis auf 100% ansteigt!
    16. }
    17. catch (Exception ex)
    18. {
    19. Console.WriteLine(ex.Message);
    20. File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), ex.Message + Environment.NewLine);
    21. bytesReceived.Clear();
    22. }
    23. // Prüfen ob eine Nachricht vollständig angekommen ist
    24. if (bytesReceived.Contains(GlobaleVariabeln.Delimiter))
    25. {
    26. // Wir müssen an dieser Stelle prüfen ob wir mehr als eine Nachricht vollständig empfangen haben.
    27. int delimiterPos = bytesReceived.IndexOf(GlobaleVariabeln.Delimiter);
    28. byte[] data = bytesReceived.GetRange(0, delimiterPos).ToArray();
    29. bytesReceived.RemoveRange(0, delimiterPos + 1);
    30. NachrichtenQueue.Enqueue(data);
    31. IsReceivingData = bytesReceived.Count > 0;
    32. }
    33. }


    Verabeitet wird die Nachricht dann, wenn der Delimiter erkannt worden ist. Dies geschieht durch das Hinzufügen zur NachrichtenQueue.

    Wobei NachrichtenQueue wie folgt dargestellt wird public sealed class NachrichtenQueue : Queue.
    QueueAbarbeiten wird ebenfalls in einem neuen Thread ausgeführt, damit ich in Events erneute Anfragen an den Server senden kann, ohne meinen Empfangsthread zu blockieren.
    QueueAbrabeiten dequeued die Nachricht nun und ruft das abonnierte Event NachrichtReceived auf.

    Hat Jemand eine Idee, wie ich das ganze lösen kann?

    LG

    MarvinKleinMusic schrieb:

    C#-Quellcode

    1. int size = (Connection.Available > Int32.MaxValue) ? Int32.MaxValue : Connection.Available;
    machst Du

    C#-Quellcode

    1. int size = Math.Min(Connection.Available, Int32.MaxValue)
    Du kannst aber testen, welche Datenmengen wie lange benötigen und dann den berechneten Timeout-Wert pluss 20% oder so setzen.
    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!