Laden...

Variable in Thread erhalten

Erstellt von 1mannlan vor 12 Jahren Letzter Beitrag vor 12 Jahren 6.860 Views
1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren
Variable in Thread erhalten

Hi,

für ein kleines Projekt habe ich eine Möglichkeit gesucht an einem Port zu horchen, das ganze habe ich soweit hinbekommen. Hier im Forum bin ich auf diesen Post gestoßen:
[Tutorial] Windows Services mit C#

Habe das ganze angepasst und funktioniert auch soweit ich es möchte. Nun habe ich das ganze aber abgeändert, bestimmt nicht die beste Lösung aber solange es funktioniert gut:

internal class DataSender
        {
            bool deactivated;
            public void SendDataToClient(object client)
            {
                TcpClient networkClient = (TcpClient)client;
                NetworkStream networkStream = networkClient.GetStream();

                // Check to see if this NetworkStream is readable.
                networkStream.ReadTimeout = 10;

                byte[] myReadBuffer = new byte[1024];
                try
                {
                    int receivedDataLength = networkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
                    string stringdata = Encoding.ASCII.GetString(myReadBuffer, 0, receivedDataLength);
                    if (deactivated)
                    {
                        deactivated = false;
                        Console.WriteLine("New Status: active!");
                    }
                    else
                    {
                        deactivated = true;
                        Console.WriteLine("New Status: deactivated!");
                    }
                }
                catch
                {
                    int FreeMem = 0;
                    PerformanceCounter freeMem = new PerformanceCounter("Memory", "Available Bytes");
                    FreeMem = Convert.ToInt32(freeMem.NextValue() / 1024 / 1024);
                    if (deactivated)
                    {
                        byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes("locked");
                        networkStream.Write(dataBuffer, 0, dataBuffer.Length);
                        Console.WriteLine("Nothing sent, locked!");
                    }
                    else
                    {
                        byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(FreeMem.ToString());
                        networkStream.Write(dataBuffer, 0, dataBuffer.Length);
                        Console.WriteLine("Sent Data: " + FreeMem);
                    }
                }

                networkStream.Close();
                networkClient.Close();                
            }
        }

Ich sende also nichtnur, sondern prüfe auch ob Anfragen ankommen, Verbindet sich der Client und sendet innerhalb von 10ms nichts wird abgebrochen und ein Wert gesendet. Sendet der Client etwas wird "deactivated" umgeschaltet. So die Theorie, die Praxis ist aber anders, denn bei jeder Anfrage erhält der Client einen neuen Thread. Wie bekomme ich jetzt aus dem alten Thread das ganze in den neuen?

B
198 Beiträge seit 2005
vor 12 Jahren

Hallo,

Wie bekomme ich jetzt aus dem alten Thread das ganze in den neuen?

Was genau meinst du denn mit aus dem alten in den neuen bekommen?
Wenn du meinst wie du Daten übergeben kannst, ist die einfachste Möglichkeit diese über eine Lambda Expression zu übergeben. Das machst du so:


static void Main()
{
  Thread t = new Thread ( () => Print ("Hello from t!") );
  t.Start();
}
 
static void Print (string message) 
{
  Console.WriteLine (message);
}

Gelöschter Account
vor 12 Jahren

Gaaaaanz große VORSICHT bei Lambdas, Wertübergabe und Multithreading. Das ist eine oft betretene Falle. Stichwort: "Closures und Threading"

Des weiteren bin ich mir nicht sicher, ob du weißt was dein code da überhaupt macht.
Die Methode heißt SendDataToClient, welche aber nichts sendet. Es ließt was aus und erst im Fehlerfalle wird was gesendet. Das ist echt schlechter Stil, weil du ja zum Senden eine Exception vorraussetzt.

Was Threading betrifft, so hast du auch hier noch defizite. Wir haben in der Artikelsektion ein paar sachen zum Thema, die du dir mal durchlesen solltest.

1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren

Ich habe mich vorher nie richtig mit Threading und Sockets beschäftigt. Mein Ziel ist folgendes:
Client Verbindet sich mit Server, versucht Daten abzurufen:

TcpClient socketForServer;
        try
        {
            socketForServer = new TcpClient("192.168.215.17", 53287);
            NetworkStream networkStream = socketForServer.GetStream();

            byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes("toggle");
            networkStream.Write(dataBuffer, 0, dataBuffer.Length);

            System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
            Console.WriteLine("Pc: " + streamReader.ReadLine());
            networkStream.Close();
        }
        catch
        {
            Console.WriteLine("Pc: Error!");
        }
        Console.ReadLine();

Nun Überprüft der Server nach dem Verbinden ob der Client Daten schickt, wenn ja setzt es eine Variable um, wenn nicht gibt er Antwort.
Klar das das mit try catch unsauber ist, aber das ganze hat sich immer bei

int receivedDataLength = networkStream.Read(myReadBuffer, 0, myReadBuffer.Length);

festgefressen wenn nichts gesendet wurde.

Z
403 Beiträge seit 2007
vor 12 Jahren

Hallo 1mannlan,

festgefressen wenn nichts gesendet wurde.

Sockets sind blockierend d.h. es wird gewartet bis etwas empfangen wurde, undzwar soviel wie angegeben.

Es wäre also schlau nur soviel zu empfangen, wie auch gesendet wurde.

Andernfalls benutze BeginSend und BeginReceive als Asynchrone variante.

Du solltest dir erstmal ein generelles verständnis von Threads und Sockets aneignen.

André

Hinweis von MarsStein vor 12 Jahren

Hallo TheGear,

das ist so nicht ganz richtig.

  1. wird nicht von einem Socket gelesen, da heisst die entsprechende Methode Receive, sondern aus einem NetworkStream
  2. Beide (Socket und NetworkStream) blockieren zwar mit dem synchronen Aufruf, aber nicht etwa bis der Puffer voll ist, sondern nur bis Daten zur Verfügung stehen. Sind dies weniger Daten als in den Puffer passen, wird alles gelesen und die Methode kehrt anschliessend zurück.
Gelöschter Account
vor 12 Jahren

Und generell ist die direkte Verwendung von Sockets mit dem .Net Framework völlig unnötig geworden. Es gibt derart viel und gutes auf höheren Leveln im Framework, wo du dir diese art der Programmierung vollkommen sparen kannst.

1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren

Zum Beispiel welche?

2.298 Beiträge seit 2010
vor 12 Jahren

Remoting und WCF zum Beispiel.

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

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

Hinweis von herbivore vor 12 Jahren

... und damit genug zum Thema Sockets, zurück zum Thema Threading.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo 1mannlan,

Ich habe mich vorher nie richtig mit Threading [...] beschäftigt.

dann solltest du das unbedingt tun!

Threading ist nichts, was man mal nebenbei macht. Dazu gibt es zuviele Fallstricke, vor denen einen auch Antworten in einem Forum nicht bewahren können. Das böse bei Threading ist, wenn ein Programm auf dem Entwicklungsrechner tut, was es soll, bedeutet das überhaupt nicht, dass es korrekt ist (Stichwort: Race Condition).

Du solltest dich also unbedingt erst gründlich einlesen! In einem Forum zu fragen ist bei weitem nicht ausreichend.

Wenn du einen Vorgeschmack haben willst, was für Konstellationen man beim Threading schon bei den einfachsten Aufgabenstellungen beachten muss, schau mal in SyncQueue <T> - Eine praktische Job-Queue.

herbivore

1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren

Dann werde ich mal ins Threading einarbeiten. Könnte mir trotzdem jemand einen Ansatz geben? Sonst kann ich hier nicht weitermachen -.-.

Gelöschter Account
vor 12 Jahren

Das Problem ist, das die Einarbeitung der Ansatz ist. Mehr kann man dir momentan nicht helfen, außer wenn man für dich das Problem mit code löst und das machen wir hier nicht.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo 1mannlan,

einen ersten Einstieg findest du in [Artikel] Multi-Threaded Programmierung.

Wenn es um die Details geht, ist die Webseite Threading in C# von Joseph Albahari sehr empfehlenswert.

Zwischen den beiden Punkten liegen aber vermutlich noch viele Bücher und eine längere Lernphase. An der Uni hat man nach einer Multithreading-Vorlesung mit 4 oder 6 Semesterwochenstunden - entspricht inkl. der Eigenarbeit einer Gesamtdauer von vielleicht 80-120 Zeitstunden - gerade mal an der Oberfläche gekratzt.

Um das vielleicht noch deutlicher zu machen: Edsger Dijkstra, einer der Multithreading-Päpste hat nicht nur einen Algorithmus für "Garbage Collection on the fly" entwickelt und sich Gedanken über dessen Korrektheit gemacht, sondern die Korrektheit sogar formal bewiesen. Um das zu tun, muss man sich wirklich tief in den Algorithmus und die Abläufe eindenken. Man muss wirklich alle möglichen (und unmöglichen) Fallkonstellationen bedenken. Das geht über einen normalen Test weit, weit hinaus. Das Problem war nur, dass sich der Algorithmus trotzdem als fehlerhaft herausgestellt hat. Dijkstra hat also nicht nur einen Fehler im Algorithmus übersehen, sondern diesen trotz der tiefen Auseinandersetzung für den formalen Beweis nicht gefunden. Und der Mann ist wirklich gut! So vertrackt ist Multithreading! Da liest man nicht einfach nur ein Buch und schüttelt dann alles aus dem Ärmel.

herbivore

1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren

Ich habe mir Multithreading in C# zum größten Teil einmal durchgelesen. Auch wenn ich ihn schon kannte kam es mir so vor als dass der BackgroundWorker eigentlich genau das machen würde was ich bräuchte. Das er zum Threading gehört hatte ich erst gar nicht so wahrgenommen. Ich denke ich könnte diesen nutzen und bei beenden der Aufgabe etwas zurück an die Aufrufsmethode geben, in der dann der Schalter für Aktiv oder inaktiv und gleichzeitig der aktuelle Stand wieder in den nächsten Worker übergeben wird. Denkt ihr das könnte so funktionieren oder habe ich mit größeren Problemen zu Rechnen?

(Ich werde jetzt mal damit anfangen und wenn es klappen sollte Rückmeldung geben, wenn nicht natürlich auch 😛. Sollte während meiner Arbeit jemand hier Posten das es damit Sinnlos wäre muss ich mir noch mehr Stoff reinziehen. Ich brauche ja eigentlich nur ein paar Basis Methode, Synchronisation usw. wird ja alles nicht benötigt.)

Gelöschter Account
vor 12 Jahren

Sollte während meiner Arbeit jemand hier Posten das es damit Sinnlos wäre muss ich mir noch mehr Stoff reinziehen.

Es ist Sinnlos.
Mit dem Backgroundworker kommst du bei deinem Problem nicht weit. Mal davon abgesehen, das dieser ein komplett anderes Einsatzgebiet hat.

Ich brauche ja eigentlich nur ein paar Basis Methode, Synchronisation usw. wird ja alles nicht benötigt.

Warum denkst du, das Synchronisation nicht benötigt wird?

Du hast das Thema immer noch nicht begriffen und du unterschätzt das ganze massiv. Probiere es, wenn du unbedingt meinst aber sei nicht allzu sehr frustriert wenn es nicht fruchtet.

Dir persönlich würde ich nun wirklich raten, die abstrahierten Schichten für die Netzwerkkommunikation im Framework zu verwenden.
Alternativen wurden ja bereits genannt. Selbst der TcpClient und TcpListener wäre schon ein deutlicher gewinn für dein vorhaben.

Direktes arbeiten mit Sockets ist schon eine Ansage und dann noch mit Multithreading muss man schon sehr genaue Ahnung von der Materie haben. Da du den Einarbeitungsaufwand scheust, solltest du meinen Rat annehmen.

1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren

So, ich habe nun den Code Komplett überarbeitet. Es funktioniert nun recht gut, blos eben weiß ich nicht wie ich den Status Speichern kann fordert der Client zur Aktion auf.

Client:

using System;
using System.Net.Sockets;

public class Client
{
    static public void Main(string[] Args)
    {
        TcpClient socketForServer;
        socketForServer = new TcpClient("192.168.215.17", 53287);
        NetworkStream networkStream = socketForServer.GetStream();
        System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
        System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);

        string outputString;
        {
            outputString = streamReader.ReadLine();
            Console.WriteLine("Ram: " + outputString + " MB");

            streamWriter.WriteLine("toggle");
            streamWriter.Flush();
        }

        networkStream.Close();
        Console.ReadLine();
    }
}

Server:

using System;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;

public class AsynchIOServer
{
    public static void Main()
    {
        TcpListener tcpListener = new TcpListener(IPAddress.Any, 53287);
        tcpListener.Start();
        while (true)
        {
            Console.Clear();
            Socket socketForClient = tcpListener.AcceptSocket();
            BackgroundWorker bgworker = new BackgroundWorker();
            bgworker.DoWork += bw_DoWork;
            bgworker.RunWorkerAsync(socketForClient);
        }
    }

    static void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        Socket netSocket = (Socket)(object)e.Argument;
        if ( netSocket.Connected)
        {
            Console.WriteLine("Client connected");
            NetworkStream networkStream = new NetworkStream(netSocket);
            System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
            System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
            Console.WriteLine("Data sent");
            streamWriter.WriteLine("233");
            streamWriter.Flush();

            if (streamReader.ReadLine() == "toggle")
            {
                Console.WriteLine("Server status toggled!");
                e.Result = "toggled";
            }
            streamReader.Close();
            networkStream.Close();
            streamWriter.Close();
        }
        netSocket.Close();
        Console.ReadLine();
    }
}

Das ganze sieht jetzt zwar deutlich sauberer aus, aber ich bekomme das Result nicht aus dem Worker hinaus. Würde ich es in die Obere while-Schleife bekommen könnte ich dem Worker einen anderen Parameter übergeben der dementsprechende dann anders reagiert.
Ist es das wo ich scheitern sollte?

1
1mannlan Themenstarter:in
88 Beiträge seit 2009
vor 12 Jahren

Hat denn keiner eine Idee dazu?