Laden...

Thread läuft nach erstem Durchlauf nicht mehr weiter

Erstellt von PaddelCore vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.730 Views
P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren
Thread läuft nach erstem Durchlauf nicht mehr weiter

Hallo,
ich habe mal eine Frage: Ich habe einen Client programmiert und ich nutze die Methode HandleCommunication() um Nachrichten abzuarbeiten. Diese sieht folgendermaßen aus:

  private void HandleCommunication()
        {
            //Array, welches auf dem Panel eingefügt wird. An der ersten Stelle steht der User
            List<String> tasksTmp;
            String sDataIncomming;
            String name;
            int count = 0;
            while (isConnected)
            {
               
                sDataIncomming = sReader.ReadLine();
                if (sDataIncomming.Equals("*User"))
                {
                    tasksTmp = new List<string>();
                    sDataIncomming = sReader.ReadLine();
                    name = sDataIncomming;
                    sDataIncomming = sReader.ReadLine();
                    
                    if (sDataIncomming.Equals("*Tasks"))
                    {
                        sDataIncomming = sReader.ReadLine();
                        while (!sDataIncomming.Equals("/Tasks")){ 
                                                      
                            tasksTmp.Add(sDataIncomming);
                            sDataIncomming = sReader.ReadLine();
                        }
                      addTasks(name, tasksTmp, count++);
                    }
               
                }
               
            }
        }

Wenn die äußere WhileSchleife einmal durchgelaufen ist und sich im zweiten Durchlauf befindet bricht der Thread bzw das Debugging in der Zeile ab:

while (!sDataIncomming.Equals("/Tasks")){ 
                                                      
                            tasksTmp.Add(sDataIncomming);
                            sDataIncomming = sReader.ReadLine();// in dieser Zeile wenn der Client /Tasks erhält    

Und danach ist Feierbaend und er führt das zweite addTasks nicht mehr aus. Hat irgendjmand eine Idee woran das liegen kann? Anbei noch einmal das komplette Programm.
Beste Grüße und vielen Dank!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace Station_9_Client
{
    public partial class Client : Form
    {
        private TcpClient client;
        private StreamReader sReader;
        private StreamWriter sWriter;
     //   private List<Task> lTasks = new List<Task>();
        private List<User> lUser = new List<User>();
        private List<Task> lTasks = new List<Task>();

        private delegate void addTaskDelegate(String name, List<String> users, int count);

        private Boolean isConnected = false;

        public Client()
        {
            InitializeComponent();
        }

        private void bConnect_Click(object sender, EventArgs e)
        {
            client = new TcpClient();
            client.Connect(tServer.Text, Int32.Parse(tPort.Text));

            sReader = new StreamReader(client.GetStream(), Encoding.ASCII);
            sWriter = new StreamWriter(client.GetStream(), Encoding.ASCII);

            isConnected = true;
        }

        private void HandleCommunication()
        {
            //Array, welches auf dem Panel eingefügt wird. An der ersten Stelle steht der User
            List<String> tasksTmp;
            String sDataIncomming;
            String name;
            int count = 0;
            while (isConnected)
            {
               
                sDataIncomming = sReader.ReadLine();
                if (sDataIncomming.Equals("*User"))
                {
                    tasksTmp = new List<string>();
                    sDataIncomming = sReader.ReadLine();
                    name = sDataIncomming;
                    sDataIncomming = sReader.ReadLine();
                    
                    if (sDataIncomming.Equals("*Tasks"))
                    {
                        sDataIncomming = sReader.ReadLine();
                        while (!sDataIncomming.Equals("/Tasks")){ 
                                                      
                            tasksTmp.Add(sDataIncomming);
                            sDataIncomming = sReader.ReadLine();
                        }
                      addTasks(name, tasksTmp, count++);
                    }
               
                }
               
            }
        }
        private void addTasks(String name, List<String> tasks, int count)
        {
            if (InvokeRequired)
                Invoke(new addTaskDelegate(addTasks), new object[] { name, tasks, count });
            else
            {
                Label lName = new Label();
                lName.Text = name;
                int yPostion ;
                User user = new User();

                for(int i = 0; i < tasks.Count; i ++) {
                    yPostion = count * 25 + 53;
                    Button button = new Button();
                    button.Text = tasks.ElementAt(i);
                    int xPostion = (i * 120 + 80);
                    button.Location = new Point(xPostion, yPostion);
                    button.Size = new Size(100, 20);
                    button.BackColor = Color.Red;
                    this.Controls.Add(button);
                    //Hinzufügen eines Events beim Drücken
                    button.Click += new EventHandler(this.clickButton);
                    user.Tasks.Add(new Task(button, tasks.ElementAt(i), false));
          
                        //lTasks.Add(new Task(button, task, false));
                }
                lUser.Add(user);
            }
        }


        public void clickButton(Object sender,
                           EventArgs e)
        {
            Button button = (Button)sender;
            Task taskTmp = null;
            //Suchen der konkreten Aufgabe die geändert werden soll
            foreach (Task task in lTasks)
            {
                if (task.Taskname1.Equals(button.Text))
                {
                    taskTmp = task;
                }
            }
            if (taskTmp.State1 == true)
            {
                taskTmp.State1 = false;
                taskTmp.Button1.BackColor = Color.Red;

            }
            else
            {
                taskTmp.State1 = true;
                taskTmp.Button1.BackColor = Color.Green;
            }

            sWriter.WriteLine("*changeState");
            sWriter.Flush();
            sWriter.WriteLine(taskTmp.Taskname1);
            sWriter.Flush();
        }

        private void bSend_Click(object sender, EventArgs e)
        {
            sWriter.WriteLine(tText.Text);
            sWriter.Flush();
            lNachricht.Text = "Sie sind als " + tText.Text + " angemeldet";
            Thread t = new Thread(HandleCommunication);
            t.Start();
        }

        private void bTerminateConnection_Click(object sender, EventArgs e)
        {
            sWriter.WriteLine("Disconnect");
            sWriter.Flush();
            isConnected = false;
            client.Close();
        }

        private void Client_Load(object sender, EventArgs e)
        {

        }
    }
}

16.806 Beiträge seit 2008
vor 5 Jahren

Magst mal erzählen, was das werden soll?
Vielleicht kann man Dir dann eine allgemein saubere Lösung anbieten.

Das hier ist schon ziemlicher Spaghetti-Code 😉

P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Eine Art Lernhelfer. Der Client bekommt die Aufgaben und kann gucken wer fertig ist. Und dabei auch seine eigenen Aufgaben abhaken.

Der Server stellt Aufgaben zur Verfügung. Wenn das System gestartet wird erhält der Client alle Nutzer mit allen Aufgaben. Und dann soll man live sehen können, wer welche Aufgabe als erledigt makiert hat. Bzw kann man die eigenen Aufgaben dann auch als erledigt makieren.

Was ist an dem Code denn so wahnsinnig schlimm?

Beste Grüße

P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Den Fehler habe ich gefunden. Anmerkungen zum Code würden mich trotzdem freuen 😃

16.806 Beiträge seit 2008
vor 5 Jahren

Anmerkungen zum Code würden mich trotzdem freuen 😃

.. wie bereits in den anderen Threads geschrieben: ist halt ziemlich unsauberer Code, sowohl von der Art und Weise wie auch von der Struktur. Grundlegende Dinge wie Trennung und sowas wie Exception Handlung fehlt auch überall. Insofern...
[FAQ] TcpClient: einfaches Beispiel wurde Dir ja schon verlinkt.

P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Erst einmal danke für eure Antworten! Ich darf in der Schichten Architektur ja nur von oben nach unten gehen. Wenn ich nun die Methode handleCommunictation nutze, dann muss die untere Schicht doch auf die Daten warten. Erhält diese Daten müssten die in der Schicht da drüber verarbeitet werden. Und ein Thread müsste ja durchgehend abfragen ob neue Daten erhalten wurden. Dies würde doch dann den umgekehrten Weg gehen, oder?

Beste Grüße

P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Ich habe nun 3 Klassen gebaut. 1. die GUI:


   public partial class Client : Form
    {
  private List<Label> labels = new List<Label>();
        private List<Button> buttons = new List<Button>();
        private Logic logicController;
        private String name;


        public delegate void addTaskDelegate(String name, List<Task> tasks, int count);
        private delegate void deleteAllLabelsDelegate();
        

        

        public Client()
        {
            logicController = new Logic(this);
            InitializeComponent();
        }

        private void bConnect_Click(object sender, EventArgs e)
        {
            logicController.connect(tServer.Text, Int32.Parse(tPort.Text));

}

   public void deleteAllLabels()
        {
            if (InvokeRequired)
                Invoke(new deleteAllLabelsDelegate(deleteAllLabels), new object[] { });
            else
            {
                foreach (Label label in labels)
                {
                    this.Controls.Remove(label);
                }
                foreach (Button button in buttons)
                {
                    this.Controls.Remove(button);
                }


            }
        }
        public void addTasks(string name, List<Task> tasks, int count)
        {
            if (InvokeRequired)
                Invoke(new addTaskDelegate(addTasks), new object[] { name, tasks, count });
            else
            {
                //Namen hinzufügen
                Label lName = new Label();
              
                lName.Text = name;
                int yPostion = count * 25 + 53;
                int xPostion = 20;
                lName.Size = new Size(80, 20);
                lName.Location = new Point(xPostion, yPostion);
                labels.Add(lName);
                this.Controls.Add(lName);
               User user = new User();
                user.UserName1 = name;
                //Hinzuügen der einzelnen Aufgaben
                for(int i = 0; i < tasks.Count; i ++) {
                   
                    Button button = new Button();
                    button.Text = tasks.ElementAt(i).Taskname;
                    xPostion = (i * 120 + 110);
                    button.Location = new Point(xPostion, yPostion);
                    button.Size = new Size(100, 20);
                    if (tasks.ElementAt(i).State)
                    {
                        button.BackColor = Color.Green;
                    }
                    else
                    {
                        button.BackColor = Color.Red;
                    }
                    this.Controls.Add(button);
                    //Hinzufügen eines Events beim Drücken
                    button.Tag = name;
                    button.Click += new EventHandler(this.clickButton);
                    buttons.Add(button);
                  //  user.Tasks.Add(new Task(button, tasks.ElementAt(i), false));
            
                        //lTasks.Add(new Task(button, tasks.ElementAt(i), false, user));
                }
                //lUser.Add(user);
            }
        }


        public void clickButton(Object sender,
                           EventArgs e)
        {
            Button button = (Button)sender;
            if (button.Tag.Equals(name))
            {
                logicController.sendText("*changeState");
                logicController.sendText(button.Text);

                 }
          
        }

        private void bSend_Click(object sender, EventArgs e)
        {
            logicController.sendText(tText.Text);
          
            name = tText.Text;
            lNachricht.Text = "Sie sind als " + tText.Text + " angemeldet";
            logicController.startThreadHandleCommunication();
            
        }

        private void bTerminateConnection_Click(object sender, EventArgs e)
        {
            logicController.sendText("Disconnect");
                  
         
        }

        private void Client_Load(object sender, EventArgs e)
        {

        }
    }
}



Die Logik Schicht:


 class Logic
    {
        Boolean isConnected;
        Data data = new Data();
        Client client;

       public Logic(Client client)
        {
            this.client = client;
            isConnected = true;
        }

        public void connect(String address, int port)
        {
            data.connect(address, port);
        }
        private void HandleCommunication()
        {
            //Array, welches auf dem Panel eingefügt wird. An der ersten Stelle steht der User
            List<Task> tasksTmpList;
            String sDataIncomming;

            String name;
            Task taskTmp;
            int count = 0;
            

            while (isConnected)
            {

                
                sDataIncomming = data.readLine();
                if (sDataIncomming.Equals("*New"))
                {
               client.deleteAllLabels();
                    sDataIncomming = data.readLine();
                    while (!sDataIncomming.Equals("/User"))
                    {

                        tasksTmpList = new List<Task>();
                        sDataIncomming = data.readLine();
                        name = sDataIncomming;

                        sDataIncomming = data.readLine();
                        if (sDataIncomming.Equals("*Tasks"))
                        {
                            sDataIncomming = data.readLine();
                            while (!sDataIncomming.Equals("/Tasks"))
                            {

                                taskTmp = new Task();

                                taskTmp.Taskname = sDataIncomming;

                                if (data.readLine().Equals("true"))
                                {
                                    taskTmp.State = true;
                                }
                                else
                                {
                                    taskTmp.State = false;
                                }

                                tasksTmpList.Add(taskTmp);
                                sDataIncomming = data.readLine();

                            }

                            client.addTasks(name, tasksTmpList, count++);
                            sDataIncomming = data.readLine();
                        }

                    }
                    count = 0;
                }


            }
        }
        public void sendText(String text)
        {
            data.send(text);
        }
        public void startThreadHandleCommunication()
        {
            Thread thread = new Thread(HandleCommunication);
            thread.Start();
        }
    }
  
}

und die Data Schicht:


class Data
    {
        
        private TcpClient client;
        private StreamReader sReader;
        private StreamWriter sWriter;

        private String lastString;

        public string LastString
        {
            get
            {
                return lastString;
            }

            set
            {
                lastString = value;
            }
        }

        public void connect(String address, int port)
        {
            
            client = new TcpClient();
            client.Connect(address, port);

            sReader = new StreamReader(client.GetStream(), Encoding.ASCII);
            sWriter = new StreamWriter(client.GetStream(), Encoding.ASCII);
            
        }
        public void send(String text)
        {
            sWriter.WriteLine(text);
            sWriter.Flush();
        }
        public void read()
        {
            lastString = sReader.ReadLine();
        }

        public String readLine()
        {
            return sReader.ReadLine();
        }
    }
4.931 Beiträge seit 2008
vor 5 Jahren

Ja, so sieht der Code doch viel aufgeräumter (und damit besser verständlich) aus.

Wenn eine untergeordnete Schicht Benachrichtigungen oder Daten an obere Schichten schicken soll, so sollten dafür dann Ereignisse benutzt werden, s.a. [FAQ] Eigenen Event definieren / Information zu Events (Ereignis/Ereignisse).

16.806 Beiträge seit 2008
vor 5 Jahren

Naja grundlegende Dinge sind eigentlich:

  • Weil Du direkt mit TCP arbeitest, solltest Du Dir eigentlich ein Protokoll ausdenken, mit dem Du dann arbeitest. Dass Du derzeit nur einzelne Stichworte über den Kanal schickst; das mag aktuell noch funktionieren. Aber im Endeffekt betreibst Du hier ein absolutes No Go: unstrukturierte Informationen über einen Kanal schicken.
    Daher auch meine häufigen Hinweise: vermeide TCP, wenn möglich. Dafür gibt es Abstraktionen, die einem sehr viel abnehmen (ich hab aber ehrlich gesagt immer noch nicht verstanden, wieso Du Dir das antust).
  • "Warten" ist immer eine schlechte Sache, denn das heisst, dass Code blockiert. Sollte man also wenn immer möglich, vermeiden. In diesem Fall muss man aber auf die Verbindung eines Clients "warten", weshalb ein Task notwendig ist (direkte Verwendung von Thread heisst, dass man selbst managen muss -> sollte man auch vermeiden).
  • Die UI wartet also nicht, sondern sie hört auf Events, die Deine Kommunikationsservices zur Verfügung stellen.
  • Eine mehrschichtige Architektur geht von Oben nach Unten. Die UI kennt die Logik, aber die Logik nicht die UI. Bei Dir ist das leider alles Kreuz und Quer: Du übergibst der Logik eine Referenz der Form 😉
P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Naja grundlegende Dinge sind eigentlich:

  • Weil Du direkt mit TCP arbeitest, solltest Du Dir eigentlich ein Protokoll ausdenken, mit dem Du dann arbeitest. Dass Du derzeit nur einzelne Stichworte über den Kanal schickst; das mag aktuell noch funktionieren. Aber im Endeffekt betreibst Du hier ein absolutes No Go: unstrukturierte Informationen über einen Kanal schicken.

Was ist denn der Unterschied zu dem was ich getan habe, zu einem Protokoll? Es wird new dann wird der user übergeben dann die einzelnen tasks?
dann > Daher auch meine häufigen Hinweise: vermeide TCP, wenn möglich. Dafür gibt es Abstraktionen, die einem sehr viel abnehmen (ich hab aber ehrlich gesagt immer noch nicht verstanden, wieso Du Dir das antust). Weil es in der didaktischen Jahresplanung steht. Ich habe damit vorher auch noch nie etwas gemacht

  • "Warten" ist immer eine schlechte Sache, denn das heisst, dass Code blockiert. Sollte man also wenn immer möglich, vermeiden. In diesem Fall muss man aber auf die Verbindung eines Clients "warten", weshalb ein Task notwendig ist (direkte Verwendung von Thread heisst, dass man selbst managen muss -> sollte man auch vermeiden). Das würde ja durch Events überflüssig werden, wenn ich das richtig verstanden habe?>
  • Die UI wartet also nicht, sondern sie hört auf Events, die Deine Kommunikationsservices zur Verfügung stellen. Das heißt, ich schreibe ein Event welches in der GUI ausgelöst wird und dann geht das von oben nach unten? Heißt in diesem Fall, die Klasse Data löst ein Event aus, welches in der GUI aktiviert wird und gibt sendet dabei die Daten mit. Die GUI verarbeitet das. Ändert die Daten in der Logik und diese sendet diese an die verschiedenen Clients. Was impliziert, dass du mit Kommunikationsservice die untere Data Schicht meinst. Oder habe ich das falsch verstanden?
    Gruß
P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Wenn eine untergeordnete Schicht Benachrichtigungen oder Daten an obere Schichten schicken soll, so sollten dafür dann Ereignisse benutzt werden, s.a.
>
.

Ich beziehe mich jetzt auf diese Aussage. Mir ist in diesem Fall nicht ganz klar wo die TCPListener als Kommunikationsschnittstelle ihren Platz finden.

16.806 Beiträge seit 2008
vor 5 Jahren

Weil es in der didaktischen Jahresplanung steht. Ich habe damit vorher auch noch nie etwas gemacht

Interessehalber: steht da wirklich "TcpClient" oder steht da "Tcp-basierende Kommunikation"?

Das würde ja durch Events überflüssig werden, wenn ich das richtig verstanden habe?

Events sind später dazu da, dass die UI informiert wird.

Das heißt, ich schreibe ein Event welches in der GUI ausgelöst wird und dann geht das von oben nach unten?

... daher kann die GUI den Event nich auslösen, sondern sie abonniert ihn.
Auslöser ist der Service, der kommuniziert.
Ansonsten würde ja die Logik die UI kennen: das ist ja falschrum.

Heißt in diesem Fall, die Klasse Data löst ein Event aus, welches in der GUI aktiviert wird und gibt sendet dabei die Daten mit.

Die Bezeichner sind zwar nicht wirklich gut, aber ja: das ist der Flow.

Was impliziert, dass du mit Kommunikationsservice die untere Data Schicht meinst.
Gruß

Kurzer Umriss:

Im Endeffekt läuft das auf Abstraktion hinaus.

  • Der UI ist egal, wie die Logik implementiert ist
  • Der Logik ist egal, wie der Datenaustausch implementiert ist

Die UI will eigentlich nur ein Event, sobald etwas passiert ist, sodass sich diese aktualisieren kann.
Für die Logik zB ist nur wichtig: sie will Daten bekommen, sie will Daten senden.
Ob die Daten nun über einen TcpClient, einen REST-Client oder Bluetooth geschickt werden: das ist ja der Logik egal (darüber hinaus muss der Logik egal sein, ob sie von Windows Forms, einer Webanwendung, WPF oder eine Konsolenanwendung oder gar nur einer anderen Bibliothek verwendet wird. Daher darf eine Logik die UI Technologie nicht kennen).
Aus diesem Grund arbeitet man mit Interfaces.

Die Logik zB kennt nur ein Interface: IMyDataClient
Es kann nun eine Implementierung alá MyDataTcpClient, MyDataRestClient, MyDataBluetoothClient geben.

Für die Logik ändert sich: nichts.
Ihr ist die Implementierung egal, solange alle 3 Kanäle exakt das gleiche machen - nur eben über ihre jeweiligen Technologien.
Die Logik hat nur eine Abhängigkeit zum Interface; nicht zur Implementierung.

P
PaddelCore Themenstarter:in
17 Beiträge seit 2018
vor 5 Jahren

Vielen vielen Dank für deine Hilfe! Ich denke, damit komme ich gut weiter 😃 Und Auszug aus der DJP:
"Fortgeschrittene Programmieraufgaben - Netzwerkprogrammierung - TcpListener - TcpClient"

Viele Grüße