Laden...

Tcp Client hängt sich auf wegen timer - andere Möglichkeit?

Erstellt von LoU1923 vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.984 Views
L
LoU1923 Themenstarter:in
3 Beiträge seit 2013
vor 10 Jahren
Tcp Client hängt sich auf wegen timer - andere Möglichkeit?

Hallo. Ich bin noch nicht so erfahren in Sachen Programmierung. Ich habe angefangen einen Tcp Client zu basteln. Bis jetzt hab ich es so gemacht, dass die empfangenen Nachrichten erst erscheinen, wenn ich einen Aktualisieren Button mache, der eine Methode ausführt. Da dacht ich mir, ich erstell nen Timer, der die Methode die ganze Zeit ausführen lässt. Aber dann hängts und es geht gar nix mehr 😄

Hier mal mein Code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Windows.Threading;
using System.Threading.Tasks;


namespace Chatprogramm___Einfach
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer timer = new DispatcherTimer();
        TcpClient client;
        NetworkStream stream;
        bool status = false;
        const int maxBuffer = 100;

        public MainWindow()
        {
            InitializeComponent();
            timer.Interval = TimeSpan.FromMilliseconds(100);
            timer.Tick += new EventHandler(timer_Tick);
        }

        private void btnconnect_Click(object sender, RoutedEventArgs e)
        {
            if (!status)
            {
                //Client baut Verbindung zum Server auf
                client = new TcpClient();
                client.Connect(IPAddress.Parse(txtaddr.Text), Convert.ToInt32(txtport.Text));
                btnconnect.Content = "Trennen";
                lblstatus.Content = "Status: Verbindung hergestellt";
                status = true;
                //Networkstream zum Nachrichten schreiben wird erstellt
                stream = client.GetStream();

            }
            else
            {
                //Client trennt Verbindung zum Server
                btnconnect.Content = "Verbinden";
                lblstatus.Content = "Status: Warte auf Verbindung";
                status = false;
                client.Close();

            }
            
        }

        private void btnsend_Click(object sender, RoutedEventArgs e)
        {
            byte[] message = System.Text.Encoding.ASCII.GetBytes(txtbenutzer.Text + ": " + txtwrite.Text);
            stream.Write(message, 0, message.Length);
            txtread.Text += txtbenutzer.Text + ": " + txtwrite.Text + Environment.NewLine;
            txtwrite.Text = "";
            

        }

        public void Empfangen()
        {
            byte[] buffer = new byte[maxBuffer];
            int gelesen = stream.Read(buffer, 0, maxBuffer);
            string empfangen = System.Text.Encoding.ASCII.GetString(buffer, 0, gelesen);
            txtread.Text += txtaddr.Text + ": " + empfangen;
        }

        void timer_Tick(object sender, EventArgs e)
        {
            
        }

        private void btnaktualisieren_Click(object sender, RoutedEventArgs e)
        {
            Empfangen();
        }
    }
}


Gibt es da vielleicht eine andere Möglichkeit? So komm ich nämlich nicht weiter...

Vielen Dank schon mal für Ratschläge 😃

2.298 Beiträge seit 2010
vor 10 Jahren

Es wäre geschickter, das empfangen in eine Schleife zu packen, die dauerhaft läuft.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo LoU1923,

ist es Absicht, dass im Ereignishandler des Timers kein Code ausgeführt wird?

Aber dann hängts und es geht gar nix mehr

D.h. genau? Bitte beachte [Hinweis] Wie poste ich richtig? Punkt 5.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

2.298 Beiträge seit 2010
vor 10 Jahren

Ich vermute er hat den Timer auf ≤ 1 Sekunde gestellt. Da das Empfangen derzeit bei ihm im GUI Thread zu laufen scheint, geh ich davon aus, das er damit seine Oberfläche blockiert.

Da empfehle ich:
[Artikel] Multi-Threaded Programmierung
[FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)
[FAQ] Warum blockiert mein GUI?

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

R
212 Beiträge seit 2012
vor 10 Jahren

Ich gebe dir vorerst einen grundlegenden Tipp was fehleranalye angeht dieser tipp lautet:

Haltepunkte(F9) und F11, mit dieser Taste bist du dazu in der lage deinen quellcode zeile für zeile abzuarbeiten nachdem du einen bestimmten punkt erreicht hast.

Ich bin mir nicht sicher, aber dein programm bleibt solange in Client.GetStream() bis dein gegenüber eine nachricht versand hat(sieht dann aus wie ein absturtz da nurnoch Client.GetStream() ausgeführt wird) es währe sinnvoller das ganze in einen Thread zu Stecken, nebeneffekt du kannst gleichzeitig mehrere Clients abfragen.

[Ich aktuallisiere meine antwort so gegen 6-7Uhr da ich im moment mein altes Net.Soc projekt nich finde und gerade unterwegs bin.]

C
2.121 Beiträge seit 2010
vor 10 Jahren

Hat der TcpClient kein Event das eingehende Daten weitergibt? Dann kannst du doch einfach dieses nutzen und brauchst gar nichts selber regeln.

L
LoU1923 Themenstarter:in
3 Beiträge seit 2013
vor 10 Jahren

ist es Absicht, dass im Ereignishandler des Timers kein Code ausgeführt wird?

Ja, das ist absicht. Weils ja nicht funktioniert. Also wenn ich das so mache:


void timer_Tick(object sender, EventArgs e)
        {
            Empfangen();
        }

dann funktioniert es halt nicht. Das Fenster friert ein, ich kann nichts mehr anklicken, nichtmal das Fenster bewegen. Egal ob ich den Interval auf 1 Millisekunde oder 1 Sekunde stelle.
Wenn ich allerdings eine Nachricht an meinen Client schreibe (über SocketTest v3.0.0) dann erscheint die Nachricht beim Client und das Fenster ist kurz "entfroren".
Ich hoffe ihr versteht was ich meine. Wenn jemand will, dann lad ich mein Project auch hoch zum Angucken.

@chilic: Weiß nicht was das ist 😄

@Robin0: Das was du geschrieben hast klingt nachvollziehbar und wird wohl so sein. Er wartet solange bis eine Nachricht kommt und dann gehts kurz wieder.

Wie man das ganze in einen eigenen Thread packt, weiß ich leider nicht. Das muss ich mir dann wohl mal angucken.

//Edit

Habe die ganze Geschichte nun erweitert, mit einem neuen Task. Ich bin ganz ehrlich, ich weiß nicht was da genau passiert, aber es funktioniert zum Teil. Problem ist diesmal, dass dieser neue Task wohl nur 1x ausgeführt wird wenn ich mich nicht irre. D.h. ich kann eine Nachricht empfangen, danach nicht mehr. Und pack ich das ganze in eine While-Schleife, dann geht wieder nichts, da das Fenster einfriert.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Windows.Threading;
using System.Threading.Tasks;


namespace Chatprogramm___Einfach
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer timer = new DispatcherTimer();
        TcpClient client;
        NetworkStream stream;
        bool status = false;
        const int maxBuffer = 128;
        [B]TaskScheduler UIThread = TaskScheduler.FromCurrentSynchronizationContext();[/B]

        public MainWindow()
        {
            InitializeComponent();
            timer.Interval = TimeSpan.FromSeconds(3);
            timer.Tick += new EventHandler(timer_Tick);
        }

        private void btnconnect_Click(object sender, RoutedEventArgs e)
        {
            if (!status)
            {
                //Client baut Verbindung zum Server auf
                client = new TcpClient();
                client.Connect(IPAddress.Parse(txtaddr.Text), Convert.ToInt32(txtport.Text));
                btnconnect.Content = "Trennen";
                lblstatus.Content = "Status: Verbindung hergestellt";
                status = true;
                //Networkstream zum Nachrichten schreiben wird erstellt
                stream = client.GetStream();
                [B]Task listenConnection = new Task(new Action(listenToStream));
                listenConnection.Start();[/B]
            }
            else
            {
                //Client trennt Verbindung zum Server
                btnconnect.Content = "Verbinden";
                lblstatus.Content = "Status: Warte auf Verbindung";
                status = false;
                client.Close();

            }
            
        }

        private void btnsend_Click(object sender, RoutedEventArgs e)
        {
            if (txtwrite.Text != "")
            {
                byte[] message = System.Text.Encoding.ASCII.GetBytes(txtbenutzer.Text + ": " + txtwrite.Text);
                stream.Write(message, 0, message.Length);
                txtread.Text += txtbenutzer.Text + ": " + txtwrite.Text + Environment.NewLine;
                txtwrite.Text = "";
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            
        }

        [B]private void listenToStream()
        {
            Task updateTextWrite = Task.Factory.StartNew(() =>
            {
                byte[] buffer = new byte[maxBuffer];
                int gelesen = stream.Read(buffer, 0, maxBuffer);
                string empfangen = System.Text.Encoding.ASCII.GetString(buffer, 0, gelesen);
                txtread.Text += txtaddr.Text + ": " + empfangen;
            }, CancellationToken.None, TaskCreationOptions.None, UIThread);
        }[/B]
    }
}

2.298 Beiträge seit 2010
vor 10 Jahren

Mist, von Haus aus kann der TcpClient kein Asynchrones empfangen von Daten. Ansonstne hätte ich dir vorgeschlagen, dies zu verwenden.

Man könnte jedoch einen Wrapper über den TcpClient schreiben, der das ganze übernimmt.

Alternativ kannst du dir mal folgendes ansehen:
Server / Client "synchrone" Übertragung
Asynchronous TCPClient C# messages arent sending?

Weil ich gerade das EDIT deines Beitrages sehe...

Mein Vorschlag wäre folgendes (ungetestet):


delegate void SetTextDelegate(string sText);

private void listenToStream()
{
    // neuen Thread erstellen und starten
    new Thread(delegate(Object state)
    {
        // Der Thread wird hier solang ausgeführt wie die Anwendung lebt.
        while(true)
        {
             byte[] buffer = new byte[maxBuffer];
             int gelesen = stream.Read(buffer, 0, maxBuffer);
             string empfangen = System.Text.Encoding.ASCII.GetString(buffer, 0, gelesen);
             this.SetText(empfangen);
        }
    }.Start();
}

private void SetText(string sText)
{
      // Hier wird der Zugriff auf die Oberfläche synchronisiert
      if(this.InvokeRequired)
      {
          this.Invoke(new SetTextDelegate(SetText), sText);
          return;
      }

      txtread.Text += txtaddr.Text + ": " + empfangen;
}

Sieh dir aber ganz dringend Threading an, damit du auch verstehst was passiert. Denn das sollte man auf jeden Fall wissen, wenn man mit Threads arbeitet (Tasks zähle ich hier mal mit).

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

R
212 Beiträge seit 2012
vor 10 Jahren

Ich kanns nurnochmal widerholen client.getStream wartet solange bis eine nachricht eingegangen ist,
Wenn dein Fenster in einer methode in einer schleife festhängt friert es quasi ein kennste bestimmt, warum dein Fenster dann kurzzeitig entfriert ist weil du dann aus client.getStream gehst und den Text darstellst SetCallback aufgrund des Timers direkt wider startest, Steck das ganze in einen thread und es läuft ohne einzufrieren.

Noch ein kleine tipp zu Threads du kannst nicht aus einem Thread auf deine Form zugreifen schau dir hierzu Thread.Invoke an, nützlich wären hierbei auch grundkentnisse zu Delegates.

How to: Make Thread-Safe Calls to Windows Forms Controls

L
LoU1923 Themenstarter:in
3 Beiträge seit 2013
vor 10 Jahren

Vielen Dank. Ich werd mich mal einlesen 😃

D
6 Beiträge seit 2013
vor 10 Jahren

Hast du dein Problem gelöst? Denn ich habe genau das gleiche Problem. Würd mich freuen wenn du dein Code Posten könntest fals du es gelöst hast 😃.

4.221 Beiträge seit 2005
vor 10 Jahren

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...