Laden...

korrektes asynchrones Scheiben im Datenstrom

Erstellt von KuGBeginner vor 2 Jahren Letzter Beitrag vor 2 Jahren 537 Views
K
KuGBeginner Themenstarter:in
21 Beiträge seit 2015
vor 2 Jahren
korrektes asynchrones Scheiben im Datenstrom

Hallo an das Forum,
Ich bitte um Unterstützung bei folgender Frage:
Über einen NamedPipeStream möchte ich ca. 1000 string Daten von Programm1 lesen, in Programm2 bearbeiten und anschließend von Programm2 an Programm1 zurückschreiben.
Der Arbeitsstand ist, dass ich Daten von Programm1 in Programm2 lesen, formatieren und bearbeiten kann.

Die Rückgabe der bearbeiteten Daten bereitet mir allerdings Probleme
Zum Lesen verwende ich folgenden Code


While (true)
transferData = await reader.ReadLineAsync();

transferData ist eine statische string Variable

Den Inhalt von transferData übergebe ich meinem streamWriter mit folgendem Code


streamWriter.AutoFlush = true;
while (true)
{
    await streamWriter.WriteLineAsync(transferData);
    if (transferData?.Length == 0) break; //??
}

Das Ergebnis ist, dass die Daten zwar gelesen und geschrieben werden; allerdings erhalte ich eine Vielzahl von mehrfach geschriebenen Daten.
Der Schreibvorgang wird auch fortgesetzt, obwohl keinen neuen Daten mehr gelesen werden.
Offensichtlich ist while(true) der falsche Aufruf. Ich habe aber bisher keine korrekte Lösung gefunden.
(die Verwendung eines "SynchronisationsCounters" funktioniert; ist aber unzumutbar langsam)
Wie kann ich das Problem lösen? Der Schreibvorgang soll möglichst synchron mit der Auswertung der gelesenen Daten erfolgen.
Herzlichen Dank für Eure Hinweise!

16.830 Beiträge seit 2008
vor 2 Jahren

NamedPipeStream ist pflicht? Ein moderner Ansatz wäre gRPC (https://docs.microsoft.com/de-de/aspnet/core/grpc/?view=aspnetcore-6.0), auf das Du auch Event-bezogen reagieren und programmieren kannst.
Würde by design das While-True-Thema lösen.

transferData ist eine statische string Variable

sowas hört sich an für sich schon nach einem Designfehler an, weil man Nachrichten kapseln und nicht irgendwo statisch abgelegen sollte -> große Race Condition Gefahr.
Siehe Docs, wie man sowas als Beispiel umsetzt => https://docs.microsoft.com/de-de/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication

K
KuGBeginner Themenstarter:in
21 Beiträge seit 2015
vor 2 Jahren

Hallo Abt,
vielen Dank für Deinen Hinweis ==> ich habe die statische Variable geändert.
Ich möchte gerne mit NamedPipeStream weiterarbeiten.
Damit bleibt die Frage, wie ich die Daten von Read nach Write übertragen kann.
Ist es eine Lösung in Read die Daten in einer List zu speichern und in Write den jeweiligen Index 1 zu lesen
und nach dem Schreiben zu löschen?
while(true) scheint zum Lesen der List erforderlich zu sein.
Es verbleibt damit das Ausgangsproblem ==> Wie weise ich Write an, mit dem Schreiben zu beginnen oder es zu beenden ....

16.830 Beiträge seit 2008
vor 2 Jahren

Dann nur noch der Hinweis, dass Dir klar sein muss, dass Du bei NamedPipes alles, wirklich alles selbst programmieren musst - und je nachdem was und in welchem Rahmen Du dies verwendest bezieht sich das "musst" auch auf die gesetzliche Lage.
Puffer, Authentifizierung, Verschlüsselung, Retry, Frames...
WCF hat einem damals viel abgenommen dazu, aber auch hier ist der Nachfolger gRPC.

Zu den Pipes:
Bist Du mit den alten Pipes unterwegs, oder mit den neuen unter System.IO.Pipes mit NamedPipeClientStream und NamedPipeServerStream?
Riecht so, dass Du noch mit der alten Welt unterwegs bist.
So oder so: eigentlich musst nur das Snippet aus der Doku nehmen und auf Deinen Fall anwenden; da kannst auch die asynchronen Methoden nehmen, das ist kein Thema.

In der neuen Welt ist prinzipiell auch kein While-True notwendig, da mit Callbacks gearbeitet wird.
Das heisst beim Instanziieren der Verbindung musst Du deklarieren, was ausgerufen wird, wenn eine Nachricht eintrudelt.
Einzig der Client braucht theoretisch ein Mechanismus (While wäre ein Weg), da der Client dauernd überprüfen muss, ob der Server eine neue Nachricht zum Abholen zur Verfügung gestellt hat.
Du kannst halt bei Named Pipes niemals sicher sein, dass eine Server Antwort sich 1:1 auf den vorherigen Request bezieht.
NamedPipes ist kein reines Request-Response-Modell wie HTTP (und Du willst glaub eher ein Request-Response-Modell, wie sich das anhört).
Dafür hatte WCF das Named Pipes Binding Modell drauf gesetzt.

Wenn Du das Duplex-Verfahren nimmst, dann wäre Program1 bei Dir der Client und Program2 der Server.

Deine Frage mit dem Index verstehe ich nicht ganz.

16.830 Beiträge seit 2008
vor 2 Jahren

OOps. Danke, korrigiert.

K
KuGBeginner Themenstarter:in
21 Beiträge seit 2015
vor 2 Jahren

Hallo Abt,
vielen Dank für den Hinweis auf den Umfang der Programmierung ==> macht viel Mut!?
Ich verwende tatsächlich die von Dir vorgeschlagenen Snippets.
Im Grunde ist auch alles funktionsfähig, außer dass das Rückschreiben der Daten an Progamm1 nicht funktioniert.
Ich habe jetzt ein Callback einfügt; kann dieses aber in Write nicht aufrufen


while (true)
{
     string? readData = await reader.ReadLineAsync();
     Console.WriteLine("Data Received from NT " + readData);
     if (readData?.Length == 0) { break; }
     //Create a new read event args
     ServerMessageEventArgs newMessage = new ServerMessageEventArgs(readData);
     // das Event wird erkannt
     OnStringReceived?.Invoke(newMessage);
}

Wie kann ich im Writer OnStringReceived aufrufen?

16.830 Beiträge seit 2008
vor 2 Jahren

vielen Dank für den Hinweis auf den Umfang der Programmierung ==> macht viel Mut!?

Naja, Du hast doch sicherlich evaluiert, was Du für Kommunikationswege einsetzen kannst und Dich dann für NamedPipes entschieden.
Da solltest ja auf die Defizite dieser Technologie gestoßen sein - daher war das nur noch mal eine Erinnerung, dass es einfach heutzutage bessere Lösungen gibt 😉
Entmutigend ist das natürlich, wenn diese Fakten für Dich neu waren - aber so sind eben die NamedPipes. Hat ein Grund, wieso man diese kaum noch sieht 😉


Dein Snippet entspricht weder dem Client noch dem Server-Beispiel aus den Docs - oder siehst Du andere snippets?
Server


class PipeServer
{
    static void Main()
    {
        using (NamedPipeServerStream pipeServer =
            new NamedPipeServerStream("testpipe", PipeDirection.Out))
        {
            Console.WriteLine("NamedPipeServerStream object created.");

            // Wait for a client to connect
            Console.Write("Waiting for client connection...");
            pipeServer.WaitForConnection();

            Console.WriteLine("Client connected.");
            try
            {
                // Read user input and send that to the client process.
                using (StreamWriter sw = new StreamWriter(pipeServer))
                {
                    sw.AutoFlush = true;
                    Console.Write("Enter text: ");
                    sw.WriteLine(Console.ReadLine());
                }
            }
            // Catch the IOException that is raised if the pipe is broken
            // or disconnected.
            catch (IOException e)
            {
                Console.WriteLine("ERROR: {0}", e.Message);
            }
        }
    }
}

Client


class PipeClient
{
    static void Main(string[] args)
    {
        using (NamedPipeClientStream pipeClient =
            new NamedPipeClientStream(".", "testpipe", PipeDirection.In))
        {

            // Connect to the pipe or wait until the pipe is available.
            Console.Write("Attempting to connect to pipe...");
            pipeClient.Connect();

            Console.WriteLine("Connected to pipe.");
            Console.WriteLine("There are currently {0} pipe server instances open.",
               pipeClient.NumberOfServerInstances);
            using (StreamReader sr = new StreamReader(pipeClient))
            {
                // Display the read text to the console
                string temp;
                while ((temp = sr.ReadLine()) != null)
                {
                    Console.WriteLine("Received from server: {0}", temp);
                }
            }
        }
        Console.Write("Press Enter to continue...");
        Console.ReadLine();
    }
}

Basierend auf dem ReadLine willst Du wohl hier den Client darstellen, ich weiß aber nicht was Du mit dem Event nun vor hast - oder ich verstehe die Frage nicht.
Was Du machen musst:

  • Über den Client an den Server schicken
  • Server antwortet
  • >> Request kannst einfach asynchron verarbeiten
  • Client muss pollen, obs eine Server-Antwort gibt
  • (Optional Verbindung schließen)
  • >> Response kannst asynchron verarbeiten

Du kannst das auch mit Events machen, auf beiden Seiten.
Für die Logik spielt das aber keine Rolle.

Kannst ja einfach mal die beiden Doc Samples laufen lassen, dann siehst ja, wie das mit dem Verhalten der beiden Komponenten funktioniert.
Und dann kannst es beliebig ausbauen - da gibts dann sehr viele Wege nach Rom (Events, Queues, asynchrone Delegates..). Aber da weiß ich halt nicht, was Du brauchst / willst.