Benutzer mit den meisten Antworten
TcpListener, Client verbindet sich automatisch bei Neustart oder Absturz - Konzeptsuche

Frage
-
Hallo,
ich suche ein Musterbeispiel für
Frage - Antwort Schnittstelle.Client, Server
Wenn der Client abstürzt, sollte bei Neustart eine Wiederverbindung stattfinden,
so dass der Bediener nichts merkt.Hier suche ich Konzepte, konkretes Beispiel.
Das sollte einfach gehen, ohne großes Rumbasteln.
Ich denke, so was brauchen doch mehrere Leute, warum das nicht im Standard drin ist, weiß ich nicht.Evtl. kann mir jemand helfen.
- Thread Probleme
- UmsetzungÄhnlich diesem Ansatz.
public void Start(int port) { this._started = true; this._port = port; this._tcpListener = new TcpListener(this._ipAdress, this._port); this._cancelTokenSource = new CancellationTokenSource(); this._cancellationToken = this._cancelTokenSource.Token; this.OnStarted(new ConnectionEventArgs(this._port)); Task.Factory.StartNew(delegate { try { this._tcpListener.Start(); while (!this._cancellationToken.IsCancellationRequested) { if (!this._tcpListener.Pending()) { Thread.Sleep(10); } else { this.OnWaiting(null); TcpClient tcpClient = this._tcpListener.AcceptTcpClient(); this._data = null; NetworkStream stream = tcpClient.GetStream(); int count; while ((count = stream.Read(this._puffer, 0, this._puffer.Length)) != 0) { this._data = Encoding.ASCII.GetString(this._puffer, 0, count); byte[] bytes; this.OnReceiving(new ConnectionEventArgs(this._data)); ConnectionEventArgs connectionEventArgs = new ConnectionEventArgs(); // switch // Telegramm this.OnSending(connectionEventArgs); bytes = Encoding.ASCII.GetBytes(connectionEventArgs.ToString()); // Frage, Antwort Konzept stream.Write(bytes, 0, bytes.Length); } tcpClient.Close(); } } this._started = false; this.OnStopped(new ConnectionEventArgs(this._port)); } catch (Exception) { } finally }); }
Macht mir das schon einen Thread ?
Task.Factory.StartNew(delegate
Task BeispielGrüße Ulrich
Antworten
-
Hi Ulrich,
nachfolgend eine kleine Demo zur Anregung. Ich habe ein paar Festlegungen getroffen und im Code implementiert, um das Grundprinzip darzustellen, ohne den Code zu optimieren und ausreichend auszutesten. Je nach Deinen Anforderungen ist es möglich, dass die endgültige Lösung anders und komplexer aussehen kann.Client XAML:
<Window x:Class="Client.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Client" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:ViewModel x:Key="vm"/> </Window.Resources> <Grid DataContext="{Binding Source={StaticResource vm}}"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition/> </Grid.RowDefinitions> <Button Content="Abrufen" Command="{Binding Cmd}" Margin="5"/> <TextBox Grid.Row="1" Text="{Binding Anzahl}" Margin="5"/> <ScrollViewer Grid.Row="2"> <TextBlock Text="{Binding Info}" Margin="5"/> </ScrollViewer> </Grid> </Window>
Client ViewModel:
using System; using System.ComponentModel; using System.Diagnostics; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Windows.Input; namespace Client { class ViewModel : INotifyPropertyChanged { SynchronizationContext sc; public ViewModel() { sc = SynchronizationContext.Current; } public ICommand Cmd { get { return new RelayCommand(CmdExec); } } private int _anzahl; public int Anzahl { get { return this._anzahl; } set { this._anzahl = value; OnPropertyChanged(); } } private string _info; public string Info { get { return this._info; } set { this._info = value; OnPropertyChanged(); } } private void CmdExec(object obj) { (new Thread(new ParameterizedThreadStart((s) => { var client = new TcpClient() { ReceiveTimeout = 1000 }; client.BeginConnect(Properties.Settings.Default.targetaddress, Properties.Settings.Default.targetport, new AsyncCallback(LoadAsync), client); }))).Start(); } private void LoadAsync(IAsyncResult ar) { var err = false; var client = ar.AsyncState as TcpClient; try { var stream = client.GetStream(); Byte[] buf = new Byte[100]; StringBuilder sb = new StringBuilder(); stream.Write(System.Text.Encoding.ASCII.GetBytes("Get"), 0, 3); stream.Read(buf, 0, 4); Anzahl = int.Parse(System.Text.Encoding.ASCII.GetString(buf, 0, 4)); var rest = Anzahl; while (rest > 0) { var l = stream.Read(buf, 0, buf.Length); sb.Append(System.Text.Encoding.ASCII.GetString(buf, 0, l)); Info = sb.ToString(); rest -= l; } } catch (Exception ex) { Trace.WriteLine(ex.Message); err = true; } finally { client.Close(); } if (err) { client = new TcpClient(); client.BeginConnect(Properties.Settings.Default.targetaddress, Properties.Settings.Default.targetport, new AsyncCallback(LoadAsync), client); } } #region OnPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propname = "") => sc.Post(new SendOrPostCallback((s) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname))), null); #endregion } }
Server als Konsolenanwendung:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Server { class Program { static void Main(string[] args) { var server = new TcpListener(IPAddress.Parse(Properties.Settings.Default.serveraddress), Properties.Settings.Default.serverport); server.Start(); server.BeginAcceptTcpClient(new AsyncCallback(CB), server); Console.WriteLine("Beenden mit beliebiger Taste"); Console.ReadKey(); } static void CB(IAsyncResult ar) { Random rnd = new Random(); Byte[] buf1 = new byte[100]; var server = ar.AsyncState as TcpListener; var client = server.EndAcceptTcpClient(ar); client.SendTimeout = 2000; try { var stream = client.GetStream(); var i1 = stream.Read(buf1, 0, 50); using (StreamReader rdr1 = new StreamReader("XmlDaten.xml")) { var data1 = rdr1.ReadToEnd(); var data2 = System.Text.Encoding.ASCII.GetBytes(data1); stream.Write(System.Text.Encoding.ASCII.GetBytes(data2.Length.ToString("0000")), 0, 4); using (StringReader rdr2 = new StringReader(data1)) { while (rdr2.Peek() > -1) { Char[] buf2 = new Char[100]; var i2 = rdr2.ReadBlock(buf2, 0, buf2.Length); var data3 = System.Text.Encoding.ASCII.GetBytes(buf2, 0, i2); stream.Write(data3, 0, data3.Length); // Simulation der langsamen Arbeit Thread.Sleep(200); // Simulation eines Timeouts if (rnd.NextDouble() < .1) Thread.Sleep(10000); } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { client.Close(); server.BeginAcceptTcpClient(new AsyncCallback(CB), server); } } } }
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Als Antwort markiert Ulrich Stippe Sonntag, 20. März 2016 14:49
-
Hi Ulrich,
ich kann ein Beispiel erstellen. Es bleiben aber zwei Fragen:1. Was soll das Beispiel letztendlich machen?
2. Wer bezahlt den Aufwand, der natürlich vom Umfang abhängt (s. 1.)?
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Bearbeitet Peter Fleischer Samstag, 19. März 2016 12:11
- Als Antwort markiert Ulrich Stippe Samstag, 19. März 2016 12:34
-
Hi Ulrich,
die Lernkurve bei WPF ist nicht zu unterschätzen. Für den Ein- oder Umstieg empfehle ich immer eine Schulung, angepasst auf den vorhandenen Kenntnisstand. Das ist am effektivsten und ermöglicht eine sofortige interaktive Klärung offener Fragen. Andere mögen lieber dicke Bücher oder auch tausend Beispiele, an denen sie lernen. Was für Dich optimal ist, musst Du selbst entscheiden. Information gibt es im Internet zu Hauf.Stolpersteine sind immer fehlende Kenntnisse der Grundlagen. Wie viele das sind, weißt Du bestimmt besser als ich.
RelayCommand ist eine Hilfsklasse zu Anbinden von Objekten, die besonders bei Buttons genutzt wird. Dies Klasse kannst Du einfach ins Projekt einbinden:
using System; using System.Windows.Input; namespace WpfApplication1 { public class RelayCommand : ICommand { private readonly Predicate<object> _canExecute; private readonly Action<object> _execute; public event EventHandler CanExecuteChanged; public RelayCommand(Action<object> execute) : this(execute, null) { } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { _execute = execute; _canExecute = canExecute; } public bool CanExecute(object parameter) { if (_canExecute == null) return true; return _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); } } }
Die Visual Studio 2015 Community Edition ist das Beste, was man ohne Kosten bekommen kann und sie reicht für die meisten Anwendungsarten völlig aus.
Ich nehme schon den Designer, nur nicht für die Beispiele, die ich hier poste. Um ein Beispiel nachzuvollziehen, ist es aufwändig, alle Designerschritte zu beschreiben, weshalb ich die üblicherweise vom Designer erzeugten Befehle direkt im geposteten Code schreibe. Damit ist das Beispiel ohne große Anpassungen nach dem Kopieren lauffähig. Meist sind nur die in der eigenen Anwendung abweichenden Namensräume anzupassen, wie z.B. bei der obigen RelayCommand Klasse.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Als Antwort markiert Ulrich Stippe Mittwoch, 23. März 2016 17:56
Alle Antworten
-
Hi Ulrich,
mit der unendlichen Schleife (bis IsCancellationRequested) schaffst Du Dir eine enorme CPU-Last (hoher Stromverbrauch, es kommt ja genügend Strom aus der Steckdose :-). Da helfen auch die 10 Millisekunden Wartezeit in jedem Schleifendurchlauf wenig. Ich würde ganz auf asynchrone Arbeit umstellen (Begin-Methoden). Wenn dabei eine Verbindung abgebrochen wird (Timeout-Fehler) wird zurückgesetzt und neu aufgebaut. Hilfreich dabei ist, erst einmal ein logisches Protokoll zu formulieren, welches beide Seiten realisieren, insbesondere, was beim Timeout und bei sonstigen Beendigungen zu machen ist.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hilfreich dabei ist, erst einmal ein logisches Protokoll zu formulieren, welches beide Seiten realisieren, insbesondere, was beim Timeout und bei sonstigen Beendigungen zu machen ist.
Hallo Peter,
es gibt viele Beispiele im Netz.
Leider nicht so, wie ich es benötige.
Du sagst selbst, ist nicht die beste Lösung, der beste Ansatz.
Somit stellt sich die Frage, wie macht man es richtig?Die Anforderung sind ja nicht ganz abwegig. Das müssen andere doch auch in ähnlicher Form verwenden,
was die Frage aufwirft, wie macht man es gleich richtig.
Danke für konkrete Tipps im Voraus.Die Anforderung.
Die Übertragung der Nachrichten erfolgt über TCP/IP. Dabei wird der eigentlichen Nachricht ein 4-Byte großer
Nachrichtenkopf vorangestellt, welcher die Gesamtgröße der zu übertragenden Nachricht enthält.
Die Gesamtgröße setzt sich auch:
-Größe der Nachricht in Bytes
-Größe des Nachrichtenkopfs (4 Byte)
Die Größe muss in Network byte order (Big Edian) übertragen werden. Durch die Verwendung
von TCP/IP ist keine Größenbeschränkung der Nachricht vorgesehen.Automatisches Wiederverbindung bei Verbindungsabbruch.Viele Grüße Ulrich
-
Hi Ulrich,
ich meinte nicht die Struktur der Daten, sondern das logische Handshaking zwischen Client und Server. Es gibt die Möglichkeit, das eine Verbindung aufgebaut wird und dann im Ping-Pong-Verfahren wechselseitig Nachrichten geschickt werden. Die andere Variante ist, dass der Client für jede Sendung eine neue Verbindung aufbaut, die nach der Übertragung einer Nachricht beendet wird. Festzulegen ist in jedem Fall, wie die Verbindung beendet wird. Festzulegen ist außerdem, ob und wie eine Übertragung bestätigt oder abgelehnt wird. Gemeint ist nicht die physische Bestätigung eines Paketes, sondern die Bestätigung der logischen Verarbeitung, wenn dies gewünscht ist.Erst, wenn diese Details alle festgelegt sind, dann kann man nach einer Variantenbetrachtung auch sagen, was "richtig" ist bzw. am besten passt. Basis für die Betrachtungen kann das ISO-7-Schichten-Modell sein.
Wie ich bereits geschrieben habe, solltest Du anstelle einer Polling-Schleife mit Verzögerung eine asynchrone Arbeitsweise mit den Begin-Methoden anwenden. Das ist zwar etwas komplexer zu programmieren, ermöglicht aber eine bessere Trennung der Prozessschritte., insbesondere wenn langfristig nicht nur eine Client-Server-Kommunikation zu realisieren ist.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Bearbeitet Peter Fleischer Mittwoch, 16. März 2016 17:41
-
Hallo Peter,also, es handelt sich in erster Linie um ein Frage Antwort Spiel.
Client ---- Server
--> Frage
Antwort <--
Der Server kann an den Client einen Event auslösen, der Info enthält,
die jedoch nicht quittiert, beantwortet werden müssen.Die Verbindung soll während der gesamten Laufzeit bestehen bleiben,
ist ja weniger zeitkritisch.Verbindung wird im Normalfall mit beenden des Client beendet.
Es kann ja sein, der Client stürzt ab, Neustart Client, dann sollte alles wieder gehen.
Oder aber auch Netzkabel herausgezogen, wieder eingesteckt, etc.Gern nehme ich Deinen Vorschlag an und verwende die asynchrone Arbeitsweise
und genau hierzu suche ich Vorlagen.
Wenn Du hierzu was hast, wäre ich sehr dankbar, da es ja doch recht komplex ist.----
Klar kennt man das ISO Modell, irgendwo halt muss der String, das Telegramm
mehr oder weniger von A nach B mit Auswertung, wie das im Hintergrund abläuft
muss man ja meist nicht wissen, das bietet ja die Entwicklungsumgebung viel.
Nur wie wendet man es korrekt an, das ist meist die Frage.
----
Wenn Du sagst ISO, ok, für welche Schichten stehen welche VS C# Klassen?
Konkret also, ich möchte Schicht 4 implementieren, dann nehme ich die C# Klasse TcpClient-Klasse
Socket
oder
Net SocketDa tue ich mir schwer, den richtigen Ansatz zu finden und verliere immer sehr viel Zeit.Viele Grüße Ulrich -
Hi Ulrich,
zusammengefasst willst Du auf Layer 4 aufsetzen und ab Sitzungsebene alles selbst implementieren. Dazu willst Du die TCP-Klassen nutzen (Layer 4). Es soll eine Verbindung zum Programmstart oder nach Timeout aufgebaut werden und erst am Programmende oder nach Timeout abgebaut werden. Es soll nur ein bilateraler halbduplexer Nachrichtenverkehr organisiert werden, der ausschließlich vom Client ausgeht und der Server lediglich auf jede Anfrage des Clients antwortet, um dann die Verbindung für die nächste Anfrage von Client zu halten.Wenn das so ist, dann formuliere mal bitte Deine konkrete Frage etwas genauer.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hallo Peter,
ob Layer 4 Sinn macht, weiß ich eben nicht. War nur als Beispiel angedacht.
Wenn ich das nehmen würde, welche Library bietet das an. Welche Namespace, welche Assembly.
Konkret die Frage.
Was empfehlen die Experten für diesen Anwendungsfall?
Server lediglich auf jede Anfrage des Clients antwortet --- JA!
Lediglich könnte der Server Events absenden, rein informativ ohne Antwort.
Bin gespannt, was Du mir empfiehlst. Danke aber jetzt schon im Voraus.
Viele Grüße Ulrich
-
Hi Ulrich,
ich kann Dir nichts empfehlen, da mir die weiteren Anforderungen unbekannt sind. Wie der Layer 4 die Tätigkeit "Sinn machen" realisieren soll, weiß ich auch nicht :-)Der Anwendungsfall ist viel zu ungenau beschreiben, dass ich ein konkrete Empfehlung geben kann.
Ich empfehle Dir, für die einzelnen Ebenen Klassen aufzubauen und in jeder Ebene die darunter liegende Ebene aufzurufen. Das bedeutet, dass für jede Ebene eine Spezifikation erarbeitet wird, die dann in der entsprechenden Klasse umgesetzt wird.
Wenn der Client keine Server-Funktionalität hat, dann ist nur halbduplex möglich und der Server hat keine Möglichkeit, Ereignisse auf dem Client auszulösen. Der Client kann lediglich zyklisch (Polling) fragen, ob etwas anliegt.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hallo Peter,also, wie gesagt soll es eine Request/Repsonse Anwendung werden. 90%>Wenn der Client keine Server-Funktionalität hat, dann ist nur halbduplex möglich
>und der Server hat keine Möglichkeit, Ereignisse auf dem Client auszulösen.
>Der Client kann lediglich zyklisch (Polling) fragen, ob etwas anliegt.Server muss die Möglichkeit haben einen Event auszulösen.
Also werde ich Vollduplex benötigen.Du sagst ereignisorientiert wäre besser, hast Du hierzu eine Demoapplikation?>Ich empfehle Dir, für die einzelnen Ebenen Klassen aufzubauen und
>in jeder Ebene die darunter liegende Ebene aufzurufen.Ich/wir haben die ISO Schichten, sicher, aber wie wird das in .NET abgebildet,
welche Klassen sind da zuständig.
Theorie - Praxis
Vielleicht hast Du noch etwas, sonst belassen wir es halt.
Welche Infos wären denn noch wichtig?
Frage - Antwort Kommunikation.
Die Telegramme beinhalten diverse Infos, Parameter, Variablen, die für den
weiteren Ablauf benötigt werden, zunächst gilt es nur die Infos auszutauschen.Viele Grüße Ulrich -
Hi Ulrich,
wenn es erforderlich ist, dass sich der Server beim Client meldet, dann muss der Client darauf vorbereitet sein. Entweder fragt der Client zyklisch den Server, ob dieser etwas zu melden hat (Ereignis) oder beide Seiten haben sowohl Server- als auch Client-Funktionalität. Im zweiten Fall ist auch ein Vollduplex-Betrieb möglich.Ich kann mich nicht erinnern, etwas von ereignisorientierter Arbeitsweise zwischen Server und Client geschrieben zu haben. Was meinst Du damit?
Die Abbildung des ISO-7-Schichten-Modells in C# setzt voraus, dass es für jede Schicht eine Spezifikation gibt. Z.B. kann man für die Schicht 6 die Methoden beschreiben, die ganze Datenbestände lesen und schrieben. Diese Methoden rufen im Objekt vom Typ der Schicht 5 Methoden auf, die z.B. Streams liefern oder annehmen. Die Klasse der Schicht 5 kümmert sich daneben auch noch um die Stabilität der Verbindung, das Caching, die Zuordnung, die ggf. auch Ereignisse für die darüber liegende Schicht auslöst.
Ein Beispiel für eine Vollduplex-Arbeitsweise in VB.NET habe ich auf meiner Homepage.
Wichtige Infos sind bei solch allgemeinen Fragen die Pflichten- und Lastenhefte, aus denen abgeleitet werden kann, welcher Lösungsweg effektiv sein sollte.
Dass die übertragenen Daten diverse Infos usw. enthalten, ist klar. Andernfalls bräuchte man sie nicht. Wichtig ist, wie diese Infos in den darüber liegenden Schichten zu verarbeiten sind.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hallo Peter,>Ich kann mich nicht erinnern, etwas von ereignisorientierter Arbeitsweise zwischen Server
>und Client geschrieben zu haben. Was meinst Du damit?
>>Da helfen auch die 10 Millisekunden Wartezeit in jedem Schleifendurchlauf wenig. Ich
>>würde ganz auf asynchrone Arbeit umstellen (Begin-Methoden). Wenn dabei eine Verbindung
>>abgebrochen wird (Timeout-Fehler) wird zurückgesetzt und neu aufgebaut.Ich meine damit Event (Asynchron) gesteuert. Hast Du evtl. ein Beispiel für C# (Vollduplex) parat?
Dann könnte ich das als Basis her nehmen. Sicher kann ich es auch konvertieren,
dann ist aber auch bestimmt wieder was ungünstig.Die Schnittstelle, der Austausch muss einfach sauber gehen. Die Hauptarbeit sehe ich
in der Verarbeitung der Daten. Wie die von A nach B kommen ist logischerweise wichtig,
aber da suche ich einfach fertige Konzepte, getestete Ansätze.Somit wäre es schön, eine Vollduplex Applikation in C# zu haben.
Ich kann selbst nicht mehr sagen, mehr Infos habe ich auch nicht,
weil Du immer sagst es fehlt etwas für ein Konzept.Byte Länge (NetworkOrder) + XML StreamSagt Dir das etwas? Bzw. wie setzt man das korrekterweise um?---------------------------------------------------
>Die Abbildung des ISO-7-Schichten-Modells in C# setzt voraus, dass es für jede Schicht
>eine Spezifikation gibt. Z.B. kann man für die Schicht 6 die Methoden beschreiben,
>die ganze Datenbestände lesen und schrieben. Diese Methoden rufen im Objekt vom Typ
>der Schicht 5 Methoden auf, die z.B. Streams liefern oder annehmen. Die Klasse der
> Schicht 5 kümmert sich daneben auch noch um die Stabilität der Verbindung, das
>Caching, die Zuordnung, die ggf. auch Ereignisse für die darüber liegende Schicht
>auslöst
Du hast hier Recht, nur weder ein Prof. an der Hochschule, noch ein Fachbuch
konnte mir konkret sagen, welche Assemblies ist hierzu nehmen muss bezw. kann.
Man tappt da im Dunkeln und probiert aus, was der Grund von n Beispielen ist
und irgendwie funktioniert es nicht perfekt, jeder probiert anstatt es ingenieurmäßig
anzugehen.>Wichtige Infos sind bei solch allgemeinen Fragen die Pflichten- und Lastenhefte,
>aus denen abgeleitet werden kann, welcher Lösungsweg effektiv sein sollte.
Schön, hast Du da ein Beispiel, Vorlage etc.?Viele Grüße Ulrich -
Hu Ulrich,
asynchrone Arbeitsweisen haben mit Ereignissen wenig gemein, außer vielleicht die Nutzung von Delegates.Bei einem Stream ist immer wichtig zu wissen, wann ein Teilstück zu Ende ist. Da ein XML-Stream beliebig lang sein kann, kann über die Byte-Länge das Ende ermittelt werden.
Aufgabenstellungen so zu formulieren, dass daraus dann ein Algorithmus entsteht, ist eine Kunst. Gelehrt wird aber nur das Handwerkszeug, damit man z.B. eine Assembly in ein Projekt einbinden kann. Welche Funktionen die Assembly haben muss, ist aus dem Algorithmus abzuleiten.
Ingenieurmäßig herangehen bedeutet zuerst die Erarbeitung eines Konzeptes. Beispiel siehe z.B. hier
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Aufgabenstellungen so zu formulieren, dass daraus dann ein Algorithmus entsteht, ist eine Kunst. Gelehrt wird aber nur das Handwerkszeug, damit man z.B. eine Assembly in ein Projekt einbinden kann. Welche Funktionen die Assembly haben muss, ist aus dem Algorithmus abzuleiten.
Ingenieurmäßig herangehen bedeutet zuerst die Erarbeitung eines Konzeptes. Beispiel siehe z.B. hier
Hallo Peter,
Danke für den Link.
Nichtsdestotrotz hast Du konkret für C# ein Vollduplex Projekt. (ereignisorientierter Arbeitsweise zwischen Server und Client)
Dann könnte man das als Vorlage heranziehen. Danke vorab.
Viele Grüße Ulrich
-
Hi Ulrich,
ich kann ein Beispiel erstellen. Es bleiben aber zwei Fragen:1. Was soll das Beispiel letztendlich machen?
2. Wer bezahlt den Aufwand, der natürlich vom Umfang abhängt (s. 1.)?
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Bearbeitet Peter Fleischer Samstag, 19. März 2016 12:11
- Als Antwort markiert Ulrich Stippe Samstag, 19. März 2016 12:34
-
1. Was soll das Beispiel letztendlich machen?
2. Wer bezahlt den Aufwand, der natürlich vom Umfang abhängt (s. 1.)?
Hallo Peter,
Die Anforderung.
zu 1)
Die Übertragung der Nachrichten erfolgt über TCP/IP. Dabei wird der eigentlichen Nachricht ein 4-Byte großer
Nachrichtenkopf vorangestellt, welcher die Gesamtgröße der zu übertragenden Nachricht enthält.
Die Gesamtgröße setzt sich auch:
-Größe der Nachricht in Bytes
-Größe des Nachrichtenkopfs (4 Byte)
Die Größe muss in Network byte order (Big Edian) übertragen werden. Durch die Verwendung
von TCP/IP ist keine Größenbeschränkung der Nachricht vorgesehen.Automatisches Wiederverbindung bei Verbindungsabbruch.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zu 2) OK, sicher. Beantwortet.
Grüße Ulrich
- Bearbeitet Ulrich Stippe Samstag, 19. März 2016 12:33 Korrektur
-
Hi Ulrich,
nachfolgend eine kleine Demo zur Anregung. Ich habe ein paar Festlegungen getroffen und im Code implementiert, um das Grundprinzip darzustellen, ohne den Code zu optimieren und ausreichend auszutesten. Je nach Deinen Anforderungen ist es möglich, dass die endgültige Lösung anders und komplexer aussehen kann.Client XAML:
<Window x:Class="Client.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Client" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:ViewModel x:Key="vm"/> </Window.Resources> <Grid DataContext="{Binding Source={StaticResource vm}}"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition/> </Grid.RowDefinitions> <Button Content="Abrufen" Command="{Binding Cmd}" Margin="5"/> <TextBox Grid.Row="1" Text="{Binding Anzahl}" Margin="5"/> <ScrollViewer Grid.Row="2"> <TextBlock Text="{Binding Info}" Margin="5"/> </ScrollViewer> </Grid> </Window>
Client ViewModel:
using System; using System.ComponentModel; using System.Diagnostics; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Windows.Input; namespace Client { class ViewModel : INotifyPropertyChanged { SynchronizationContext sc; public ViewModel() { sc = SynchronizationContext.Current; } public ICommand Cmd { get { return new RelayCommand(CmdExec); } } private int _anzahl; public int Anzahl { get { return this._anzahl; } set { this._anzahl = value; OnPropertyChanged(); } } private string _info; public string Info { get { return this._info; } set { this._info = value; OnPropertyChanged(); } } private void CmdExec(object obj) { (new Thread(new ParameterizedThreadStart((s) => { var client = new TcpClient() { ReceiveTimeout = 1000 }; client.BeginConnect(Properties.Settings.Default.targetaddress, Properties.Settings.Default.targetport, new AsyncCallback(LoadAsync), client); }))).Start(); } private void LoadAsync(IAsyncResult ar) { var err = false; var client = ar.AsyncState as TcpClient; try { var stream = client.GetStream(); Byte[] buf = new Byte[100]; StringBuilder sb = new StringBuilder(); stream.Write(System.Text.Encoding.ASCII.GetBytes("Get"), 0, 3); stream.Read(buf, 0, 4); Anzahl = int.Parse(System.Text.Encoding.ASCII.GetString(buf, 0, 4)); var rest = Anzahl; while (rest > 0) { var l = stream.Read(buf, 0, buf.Length); sb.Append(System.Text.Encoding.ASCII.GetString(buf, 0, l)); Info = sb.ToString(); rest -= l; } } catch (Exception ex) { Trace.WriteLine(ex.Message); err = true; } finally { client.Close(); } if (err) { client = new TcpClient(); client.BeginConnect(Properties.Settings.Default.targetaddress, Properties.Settings.Default.targetport, new AsyncCallback(LoadAsync), client); } } #region OnPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propname = "") => sc.Post(new SendOrPostCallback((s) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname))), null); #endregion } }
Server als Konsolenanwendung:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Server { class Program { static void Main(string[] args) { var server = new TcpListener(IPAddress.Parse(Properties.Settings.Default.serveraddress), Properties.Settings.Default.serverport); server.Start(); server.BeginAcceptTcpClient(new AsyncCallback(CB), server); Console.WriteLine("Beenden mit beliebiger Taste"); Console.ReadKey(); } static void CB(IAsyncResult ar) { Random rnd = new Random(); Byte[] buf1 = new byte[100]; var server = ar.AsyncState as TcpListener; var client = server.EndAcceptTcpClient(ar); client.SendTimeout = 2000; try { var stream = client.GetStream(); var i1 = stream.Read(buf1, 0, 50); using (StreamReader rdr1 = new StreamReader("XmlDaten.xml")) { var data1 = rdr1.ReadToEnd(); var data2 = System.Text.Encoding.ASCII.GetBytes(data1); stream.Write(System.Text.Encoding.ASCII.GetBytes(data2.Length.ToString("0000")), 0, 4); using (StringReader rdr2 = new StringReader(data1)) { while (rdr2.Peek() > -1) { Char[] buf2 = new Char[100]; var i2 = rdr2.ReadBlock(buf2, 0, buf2.Length); var data3 = System.Text.Encoding.ASCII.GetBytes(buf2, 0, i2); stream.Write(data3, 0, data3.Length); // Simulation der langsamen Arbeit Thread.Sleep(200); // Simulation eines Timeouts if (rnd.NextDouble() < .1) Thread.Sleep(10000); } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { client.Close(); server.BeginAcceptTcpClient(new AsyncCallback(CB), server); } } } }
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Als Antwort markiert Ulrich Stippe Sonntag, 20. März 2016 14:49
-
Hallo Peter,dann bedanke ich mich hiermit recht herzlich.Wie gesagt, Danke für das Beispiel.
Ich möchte Dich auch nicht zu sehr 'löchern', da ich weiß,
es ist Deine Zeit.
Vielleicht kannst mir abschließend noch auf die Sprünge helfen.A) Ich denke, es geht in die richtige Richtung.
B) Wäre blöd, es nicht zum Laufen zu bringen.
Ich habe bis dato nur in WinForms Erfahrung.
Hast Du ein WPF App erstellt?Auf was ist zu achten, damit ich es testen kann?
Müsste mir evtl. das auch noch anschauen?
Oder kennst Du einen besseren Einstieg?
WPF
Viele Grüße Ulrich -
Hi Ulrich,
solange es keine anderen Vorgaben gibt, nutze ich die modernere WPF-Technologie. Damit kann man sich einfacher auf die Programmlogik konzentrieren und schneller stabilere Anwendungen erstellen, die so flexibel sind, dass sie mit relative wenig Aufwand an neue Anforderungen angepasst werden können.In meinem geposteten Beispiel brauchst Du Dich nicht mit WPF abzugeben, wenn Du da noch wenig Erfahrung hast. Du kannst den ViewModel auch in einer Windows Form Anwendung nutzen. Du brauchst nur Anzahl an eine Label, Info an eine mehrzeilige TextBox und CmdExec an einen Button binden.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hallo Peter,wie immer, hast Du Recht.
Wir können es somit abschließen. Danke.
Was empfiehlst Du für den Einstieg/Umstieg in WPF?
Kennst Du evtl. gute Tutorials, Webcasts?
Hast Du evtl. auf was man als Umsteiger achten muss,
was evtl. Stolpersteine sind?
RelayC.
Ich bekomme es nicht zum Laufen, ist ja recht schade, Deine Vorlage sieht ja gut aus.
Des weiteren muss ich, denke ich, jetzt auf diese Version umsteigen.
Studio
Dann sollte Dein Beispiel gehen.
Wäre noch was zu beachten? Du hast dann einfach eine WPF Anwendung erstellt?
Die Client heißt?Hintergrund ist einfach, Du nimmst teils ungern die Vorlagen, den Wizard.
namespace WpfApplication { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
Viele Grüße Ulrich
-
Hi Ulrich,
die Lernkurve bei WPF ist nicht zu unterschätzen. Für den Ein- oder Umstieg empfehle ich immer eine Schulung, angepasst auf den vorhandenen Kenntnisstand. Das ist am effektivsten und ermöglicht eine sofortige interaktive Klärung offener Fragen. Andere mögen lieber dicke Bücher oder auch tausend Beispiele, an denen sie lernen. Was für Dich optimal ist, musst Du selbst entscheiden. Information gibt es im Internet zu Hauf.Stolpersteine sind immer fehlende Kenntnisse der Grundlagen. Wie viele das sind, weißt Du bestimmt besser als ich.
RelayCommand ist eine Hilfsklasse zu Anbinden von Objekten, die besonders bei Buttons genutzt wird. Dies Klasse kannst Du einfach ins Projekt einbinden:
using System; using System.Windows.Input; namespace WpfApplication1 { public class RelayCommand : ICommand { private readonly Predicate<object> _canExecute; private readonly Action<object> _execute; public event EventHandler CanExecuteChanged; public RelayCommand(Action<object> execute) : this(execute, null) { } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { _execute = execute; _canExecute = canExecute; } public bool CanExecute(object parameter) { if (_canExecute == null) return true; return _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); } } }
Die Visual Studio 2015 Community Edition ist das Beste, was man ohne Kosten bekommen kann und sie reicht für die meisten Anwendungsarten völlig aus.
Ich nehme schon den Designer, nur nicht für die Beispiele, die ich hier poste. Um ein Beispiel nachzuvollziehen, ist es aufwändig, alle Designerschritte zu beschreiben, weshalb ich die üblicherweise vom Designer erzeugten Befehle direkt im geposteten Code schreibe. Damit ist das Beispiel ohne große Anpassungen nach dem Kopieren lauffähig. Meist sind nur die in der eigenen Anwendung abweichenden Namensräume anzupassen, wie z.B. bei der obigen RelayCommand Klasse.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Als Antwort markiert Ulrich Stippe Mittwoch, 23. März 2016 17:56
-
Hallo Peter,ja ok, dann können wir es hiermit mal beenden.
Ist sicher gut programmiert, teils natürlich nicht einfach zu verstehen.
Sicher in keinem Buch so beschrieben.
Schwierige Syntax. Ich teste es über die Ostertage mal mit dem neuen Studio.
Viele Grüße Ulrich -
Hi Ulrich,
nimm das aktuelle Visual Studio 2015 (ggf. in der kostenlosen Community Edition), dann laufen auch meine Beispiele. Ansonsten musst Du auf die Neuerungen im Framework 4.5 und höher und auch auf C# 6.0 verzichten. Um die Beispiele zu verstehen, ist es aber empfehlenswert, sich mit der aktuellen Syntax von C# auseinanderzusetzen.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
nimm das aktuelle Visual Studio 2015 (ggf. in der kostenlosen Community Edition), dann laufen auch
Hallo Peter,
wie gesagt habe ich es über die Tage mit VS2015 testen können.
Ja sicher es klappt, teils kommt dann diese Meldung.
Du überträgst Blöcke a 100 Byte.
Könnte Du die ein oder andere Codezeile noch erläutern?Wäre nett und denke, dass sich @Raimo auch freuen würde.
Wir sind nicht so tief drin. Ich denke, Teile muss man halt gegeben hinnehmen.Viele Grüße Ulrich