Laden...

Stream funktioniert lokal; Daten gehen übers Netzwerk jedoch verloren.

Erstellt von Frokuss vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.820 Views
F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren
Stream funktioniert lokal; Daten gehen übers Netzwerk jedoch verloren.

Guten Abend,

ich hoffe ich bin hier im richtigen Teil gelandet... Ich habe mir da mal eine eigene Klasse gebastelt (MyStream). Nun merke ich aber, dass ich mit dieser Klasse etwas an die Grenzen stoße. Solange ich das ganze lokal an meinem Laptop (Windows 10) teste, klappt das ganze auch ziehmlich solide. Sobald ich dies aber über das Netzwerk (Window 7) teste, treten einige Probleme auf. Meine Vermutungen dem bezüglich sind:

1.) Mein Server (Lokal, Windows 10) sendet die Daten zu schnell nacheinander, so dass der Client (Netzwerk Win7) die Daten teils verschluckt
2.) Der Client hat zu große Thread-Sleeps weshalb er das ganze verschluckt.
3.) Der Client stürzt manchmal ab und will einen Report an Windows schicken (ist nen Windows 7 rechner)
4.) Ich mache großen Quatsch (was auch nicht unwahrscheinlich ist 😄)

Dies ist meine Vermutungen, weshalb ich das ganze etwas in Zweifel ziehe. Ich lade die Daten aus der Datenbank raus, und schicke jede einzelne Zeile über diese Klasse an den Client. Dazu durchlaufe ich eine Foreach-Schleife und rufe jedes mal MyStream.Senden(<<Befehl>>, <<Information>>) auf.

Als Beispiel:

MyStream ms = new MyStream(myServer);//Socket myServer
string id = 5;
ms.Senden(CMD.LoginOk, id);

Folgend poste ich meine komplette Klasse:

		public enum CMD {Update, next, disco, LoginOk, LoginErr, LoginManuell, LoginAuto, SynchSortFreunde, AddSortFreunde, DelSortFreunde, EditSortFreund, SynchFreunde, AddFreunde, DelFreunde, EditFreund, SynchGruppe, AddGruppe, DelGruppe, EditGruppe, SynchTermin, AddTermin, DelTermin, EditTermin, SynchNachrichtInfo, SynchNachricht, AddNachricht, DelNachricht, NeuNachricht, OkNachricht, SynchUmfrage, AddUmfrage, DelUmfrage, EditUmfrage, UserDisco, SynchSortGruppe, AddSortGruppe, DelSortGruppe, EditSortGruppe, FehlerUnbenannt, FehlerStreamLaenge, FehlerLaengeMin};
	
	public class MyStream
	{
		//ENUM aus Excel kopiert
		public static Dictionary<CMD, string> CmdBuch = new Dictionary<CMD, string>(){
			{CMD.Update, "0000"}, {CMD.next, "0001"}, {CMD.disco, "0002"},
			{CMD.LoginOk, "0010"},{CMD.LoginErr, "0011"},{CMD.LoginManuell, "0012"},{CMD.LoginAuto, "0013"},
			{CMD.SynchFreunde, "0200"},{CMD.AddFreunde, "0201"},{CMD.DelFreunde, "0202"},{CMD.EditFreund, "0203"},
			{CMD.SynchSortGruppe, "0300"},{CMD.AddSortGruppe, "0301"},{CMD.DelSortGruppe, "0302"},{CMD.EditSortGruppe, "0303"},
			{CMD.SynchTermin, "0400"},{CMD.AddTermin, "0401"},{CMD.DelTermin, "0402"},{CMD.EditTermin, "0403"},
			{CMD.SynchNachrichtInfo, "0499"},{CMD.SynchNachricht, "0500"},{CMD.AddNachricht, "0501"},{CMD.DelNachricht, "0502"}, {CMD.NeuNachricht, "0503"}, {CMD.OkNachricht, "0504"},
			{CMD.SynchUmfrage, "0600"},{CMD.AddUmfrage, "0601"},{CMD.DelUmfrage, "0602"},{CMD.EditUmfrage, "0603"},
			{CMD.SynchGruppe, "0700"},{CMD.AddGruppe, "0701"},{CMD.DelGruppe, "0702"},{CMD.EditGruppe, "0703"},
			{CMD.SynchSortFreunde, "0800"},{CMD.AddSortFreunde, "0801"},{CMD.DelSortFreunde, "0802"},{CMD.EditSortFreund, "0803"},
			{CMD.UserDisco, "0900"},
			{CMD.FehlerUnbenannt, "9000"},
			{CMD.FehlerStreamLaenge, "9998"}, {CMD.FehlerLaengeMin, "9999"}
		};
		
		public static Dictionary<string, CMD> CmdBuchInv = new Dictionary<string, CMD>();

		public NetworkStream ns;
		public StreamReader sr;

		public MyStream(Socket sock){
			if(sock.Connected){
				ns = new NetworkStream(sock);
				sr = new StreamReader(ns);
			
				if(!CmdBuchInv.ContainsKey("0000")){
					foreach(KeyValuePair<CMD, string> item in CmdBuch){
						CmdBuchInv.Add(item.Value, item.Key);
					}
				}
			}
			else{
				ns = null;
				sr = null;
			}
		}

		public bool DataAvailable(){
			if(ns != null)
				return ns.DataAvailable;
			else
				return false;
		}

		public void Senden(string txt){
			string key = string.Empty;//TODO
			Byte[] senden = Encoding.ASCII.GetBytes(txt+"\r\n");
			//TODO Verschlüsselung hier einbauen...
			//TODO Login darf aber nur teilverschlüsselt sein...
			//TODO senden = Verschlüsseln(senden)
			
			senden = Verschluesseln(senden, key);
			
			try{
				if(ns != null)
					ns.Write(senden, 0, senden.Length);
			}catch (Exception ex){//Fehler bei abgebrochener/beendeter Verbindung
				Console.WriteLine(ex.ToString());
			}
			Thread.Sleep(100);
		}

		public void Senden(CMD cmd, string info){
			Senden(Prepare(cmd, info));
		}

		public Nachricht Empfangen(){
			string key = string.Empty;//TODO
			//TODO Entschlüsseln
			//TODO ausgenommen der Teilverschlüsselung (Login)
			if(ns != null){
				if(ns.DataAvailable)
					return (new Nachricht(GetClientCMD(Entschluesseln(sr.ReadLine(), key))));
				else
					return (new Nachricht(GetClientCMD(string.Empty)));
			}
			else
				return (new Nachricht(GetClientCMD(string.Empty)));
		}

		protected static string[] GetClientCMD(string cmd){
			int i=0;
			if(cmd.Length < 8)//Fehler: Unter mindestlänge von 9
				return new string[]{"9999", "0000", ""};
			
			string pre, l, post;
			pre = cmd.Substring(0,4);
			l = cmd.Substring(4,4);
			
			while(i != 3 && l.Length > 0 && l.Substring(0,1).Equals("0")){
				l = l.Substring(1, l.Length -1);
				i++;
			}
			
			//Fehler: Stream inhalt fehlt - inkorrekte Länge
			if(cmd.Length != int.Parse(l) + 8)
				return new string[]{"9998", "0000", ""};
			
			if(l.Length == 0)
				l = "0";
			
			post = cmd.Substring(8, int.Parse(l));
			
			return CutValuesCMD(new string[]{pre, l, post});
		}

		//Ich verwende die Raute (#) als Trennzeichen, d.h.: info1#info2#...
		protected static string[] CutValuesCMD(string[] values){
			string[] cuts = Regex.Split(values[2], "#");
			string[] temp = {values[0], values[1]};
			int l = cuts.Length + temp.Length;
			string[] ret = new string[l];
			
			Array.Copy(temp, ret, 2);
			Array.Copy(cuts, 0, ret, 2, cuts.Length);
			return ret;
		}

		public static string Prepare(CMD cmd, string info){
			string pre = "", post = "", preL = "";

			pre = CmdBuch[cmd];
			post = info;

			preL = post.Length.ToString();
			while(preL.Length < 4)
				preL = "0" + preL;

			return pre + preL + post;
		}

		//TODO... keine blassen schimmer...
		public Byte[] Verschluesseln(Byte[] txt, string key){
			return txt;
		}
		
		public string Entschluesseln(string txt, string key){
			return txt;
		}
		
		public void Dispose(){
			if(ns != null)
				ns.Dispose();
		}
	}

Vielleicht kann ja jemand mal dadrüber gucken, und mir sagen, was ich besser machen könnte/sollte.

Besten Gruß Frokuss

16.841 Beiträge seit 2008
vor 6 Jahren

Bitte trenne, falls es aufkommt, das Thema "Review" von konkreten Problemen und beschränkte Dich gemäß [Hinweis] Wie poste ich richtig? auf eine Frage pro Thread. Auch nen ordentlicher Thread-Titel wäre nett gewesen.
Oft lösen sich auch die Nachfolgefragen, wenn die Ursprungsfrage beantwortet wurde.

Der Code missachtet jedenfalls ziemlich viele Grundprinzipien der Programmierung, wie Single Responsibility.
Der gesamte Code ist auch nicht annähernd testbar.

Erklär doch mal was Du überhaupt machen willst, bevor wir anfangen das hier zu reparieren.
Vermutlich gibt es schon was fertiges. Vor allem wenn ich sowas wie Login sehe schrillen alle Alarmglocken, da man das nicht mit entsprechenden Fachwissen niemals sicher umsetzen kann.

F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren

Ups... das Tut mir leid!

Der Code missachtet jedenfalls ziemlich viele Grundprinzipien der Programmierung, wie Single Responsibility.

Ich bin eine ziehmliche Leihe... Daher gut, dass du mir selbst dies gesagt hast 😃 Ich versuche mal da in Zukunft etwas drauf zu achten (habe noch nie vorher etwas davon gehöt).

Erklär doch mal was Du überhaupt machen willst[...]

Ich wollte ein Programm schreiben, was über das Netzwerk komuniziert und Daten aus der Datenbank (MySQL) übermittelt. Dabei soll mir genau diese Klasse (MyStream) helfen, die Streams so zu senden, dass der jeweils andere (Client/Server) sie immer in der gleichen Form bekommt. Nun rufe ich aber die Methode Senden für jede Zeile die ich in der Datenbank finde auf, was zu dem Fehler führt, dass der Client nicht alle Daten erhält - Daher es fehlen einige Datensätze, wenn dies über Netzwerk gemacht wird.
Daher meine primäre Frage: Woher kommt es, dass beim Client nicht alle Daten ankommen und wie kann ich dies beheben?

Folgend wie ich mit der Klasse (Serverseitig) sende:

Socket mySocket;//Bei mir schon instanziert
ms = new MyStream(mySocket);
if(ms.DataAvailable()){
	Nachricht nachricht = ms.Empfangen();
	
	if(nachricht.cmd.Equals(CMD.SynchNachricht)){
		long offline = long.Parse(nachricht.werte[0]);
		//List<string> mess = SynchMessagesOG(userID, offline);//Diese Daten kommen aus der DB
		List<string> mess = new List<string>();
		mess.Add("Hallo1");
		mess.Add("Hallo2");
		mess.Add("Hallo3");
		mess.Add("Hallo4");

		//Hier die Schleife, bei
		foreach(string m in mess){
			lock(_lockMS)//Reigenfolge wie in MyMessagePair
				ms.Senden(nachricht.cmd, m);
		}
	}
}

Und hier folgend alles(!) aus my MyStream-Klasse

/*
 * Erstellt mit SharpDevelop.
 * Benutzer: Frokuss
 * Datum: 27.09.2017
 * Zeit: 13:48
 * 
 * Sie können diese Vorlage unter Extras > Optionen > Codeerstellung > Standardheader ändern.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace Server_Sockets
{
	/// <summary>
	/// Description of TestStream.
	/// </summary>
	
	public enum CMD {Update, next, disco, LoginOk, LoginErr, LoginManuell, LoginAuto, SynchSortFreunde, AddSortFreunde, DelSortFreunde, EditSortFreund, SynchFreunde, AddFreunde, DelFreunde, EditFreund, SynchGruppe, AddGruppe, DelGruppe, EditGruppe, SynchTermin, AddTermin, DelTermin, EditTermin, SynchNachrichtInfo, SynchNachricht, AddNachricht, DelNachricht, NeuNachricht, OkNachricht, SynchUmfrage, AddUmfrage, DelUmfrage, EditUmfrage, UserDisco, SynchSortGruppe, AddSortGruppe, DelSortGruppe, EditSortGruppe, FehlerUnbenannt, FehlerStreamLaenge, FehlerLaengeMin};
	
	public class MyStream
	{
		//ENUM aus Excel kopiert
		public static Dictionary<CMD, string> CmdBuch = new Dictionary<CMD, string>(){
			{CMD.Update, "0000"}, {CMD.next, "0001"}, {CMD.disco, "0002"},
			{CMD.LoginOk, "0010"},{CMD.LoginErr, "0011"},{CMD.LoginManuell, "0012"},{CMD.LoginAuto, "0013"},
			{CMD.SynchFreunde, "0200"},{CMD.AddFreunde, "0201"},{CMD.DelFreunde, "0202"},{CMD.EditFreund, "0203"},
			{CMD.SynchSortGruppe, "0300"},{CMD.AddSortGruppe, "0301"},{CMD.DelSortGruppe, "0302"},{CMD.EditSortGruppe, "0303"},
			{CMD.SynchTermin, "0400"},{CMD.AddTermin, "0401"},{CMD.DelTermin, "0402"},{CMD.EditTermin, "0403"},
			{CMD.SynchNachrichtInfo, "0499"},{CMD.SynchNachricht, "0500"},{CMD.AddNachricht, "0501"},{CMD.DelNachricht, "0502"}, {CMD.NeuNachricht, "0503"}, {CMD.OkNachricht, "0504"},
			{CMD.SynchUmfrage, "0600"},{CMD.AddUmfrage, "0601"},{CMD.DelUmfrage, "0602"},{CMD.EditUmfrage, "0603"},
			{CMD.SynchGruppe, "0700"},{CMD.AddGruppe, "0701"},{CMD.DelGruppe, "0702"},{CMD.EditGruppe, "0703"},
			{CMD.SynchSortFreunde, "0800"},{CMD.AddSortFreunde, "0801"},{CMD.DelSortFreunde, "0802"},{CMD.EditSortFreund, "0803"},
			{CMD.UserDisco, "0900"},
			{CMD.FehlerUnbenannt, "9000"},
			{CMD.FehlerStreamLaenge, "9998"}, {CMD.FehlerLaengeMin, "9999"}
		};
		
		public static Dictionary<string, CMD> CmdBuchInv = new Dictionary<string, CMD>();
		
		public NetworkStream ns;
		public StreamReader sr;
		
		/// <summary>
		/// Benötigt einen Socket - wegen NetworkStream
		/// </summary>
		/// <param name="sock"></param>
		public MyStream(Socket sock){//Dies ist das Problem...
			if(sock.Connected){
				ns = new NetworkStream(sock);
				sr = new StreamReader(ns);
			
				if(!CmdBuchInv.ContainsKey("0000")){
					foreach(KeyValuePair<CMD, string> item in CmdBuch){
						CmdBuchInv.Add(item.Value, item.Key);
					}
				}
			}
			else{
				ns = null;
				sr = null;
			}
		}
		
		public bool DataAvailable(){
			if(ns != null)
				return ns.DataAvailable;
			else
				return false;
		}
		
		/// <summary>
		/// Sendet eine Nachricht an einen Client
		/// </summary>
		/// <param name="txt">string = Befehlscode + Textlänge + Nachricht</param>
		public void Senden(string txt){
			string key = string.Empty;//TODO
			Byte[] senden = Encoding.ASCII.GetBytes(txt+"\r\n");
			//TODO Verschlüsselung hier einbauen...
			//TODO Login darf aber nur teilverschlüsselt sein...
			//TODO senden = Verschlüsseln(senden)
			
			senden = Verschluesseln(senden, key);
			
			try{
				if(ns != null)
					ns.Write(senden, 0, senden.Length);
			}catch (Exception ex){
				Console.WriteLine("######Fehler######Fehler######Fehler");
				Console.WriteLine("######Fehler######Fehler######Fehler");
				Console.WriteLine(ex.ToString());//Verbindung nicht mehr vorhanden
				Console.WriteLine("######Fehler######Fehler######Fehler");
				Console.WriteLine("######Fehler######Fehler######Fehler");
			}

			Thread.Sleep(100);
		}
		
		public void SendenNext(){
			Senden(CMD.next, "");
		}
		
		/// <summary>
		/// Sendet eine Nachricht an einen Client
		/// </summary>
		/// <param name="cmd">Enum:CMD = Befehlscode</param>
		/// <param name="info">string = Nachricht</param>
		public void Senden(CMD cmd, string info){
			Senden(Prepare(cmd, info));
		}
		
		/// <summary>
		/// Prüft ob der Netzwerkstream eine Nachricht empfangen kann und liefert einen Array der Länge 3+ (Befehlscode, Länge, Nachricht+) zurück
		/// </summary>
		/// <returns></returns>
		public Nachricht Empfangen([System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
        [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
        [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0){
			
Console.WriteLine("Aufgerufen: " + memberName);
			
			string key = string.Empty;//TODO
			//TODO Entschlüsseln
			//TODO ausgenommen der Teilverschlüsselung (Login)
			if(ns != null){
				if(ns.DataAvailable)
					return (new Nachricht(GetClientCMD(Entschluesseln(sr.ReadLine(), key))));
				else
					return (new Nachricht(GetClientCMD(string.Empty)));
			}
			else
				return (new Nachricht(GetClientCMD(string.Empty)));
		}
		
		protected static string[] GetClientCMD(string cmd){
Console.WriteLine("MyStream.cs: <<IN<<: " + cmd);
			int i=0;
			if(cmd.Length < 8)//Fehler: Unter mindestlänge von 9
				return new string[]{"9999", "0000", ""};
			
			string pre, l, post;
			pre = cmd.Substring(0,4);
			l = cmd.Substring(4,4);
			
			while(i != 3 && l.Length > 0 && l.Substring(0,1).Equals("0")){
				l = l.Substring(1, l.Length -1);
				i++;
			}
			
			//Fehler: Stream inhalt fehlt - inkorrekte Länge
			if(cmd.Length != int.Parse(l) + 8)
				return new string[]{"9998", "0000", ""};
			
			if(l.Length == 0)
				l = "0";
			
			post = cmd.Substring(8, int.Parse(l));
			
			return CutValuesCMD(new string[]{pre, l, post});
		}
		
		protected static string[] CutValuesCMD(string[] values){
			string[] cuts = Regex.Split(values[2], "#");
			string[] temp = {values[0], values[1]};
			int l = cuts.Length + temp.Length;
			string[] ret = new string[l];
			
			Array.Copy(temp, ret, 2);
			Array.Copy(cuts, 0, ret, 2, cuts.Length);
			return ret;
		}
		
		/// <summary>
		/// Erstellt einen string für zur Übermittlung
		/// </summary>
		/// <param name="cmd">Enum:CMD = Befehlscode</param>
		/// <param name="info">string = Informationsinhalt</param>
		/// <returns></returns>
		public static string Prepare(CMD cmd, string info){
			string pre = "", post = "", preL = "";

			pre = CmdBuch[cmd];
			post = info;

			preL = post.Length.ToString();
			while(preL.Length < 4)
				preL = "0" + preL;

			return pre + preL + post;
		}
		
		//TODO
		public Byte[] Verschluesseln(Byte[] txt, string key){
			return txt;
		}
		
		public string Entschluesseln(string txt, string key){
			return txt;
		}
		
		public void Dispose(){
			if(ns != null)
				ns.Dispose();
		}
	}
	
	public class Nachricht{
		public CMD cmd;
		public int length;
		public string[] werte;
		
		public Nachricht(string[] mess){
			
			if(MyStream.CmdBuchInv.ContainsKey(mess[0]))
				cmd = MyStream.CmdBuchInv[mess[0]];
			else
				cmd = CMD.FehlerUnbenannt;
			
			//Die länge des (mess[1]) ist nur Datenmüll
			length = mess.Length - 2;
			werte = new string[length];
			for(int i=2; i<mess.Length; i++){
				werte[i-2] = mess[i];
			}
		}
	}
}

An dieser Stelle wollte ich noch kurz erwähnen, dass die Klasse arbeitet - aber wie gesagt - manchmal nicht alle Daten ankommen.

Ich hoffe dies ist nun besser...
Besten Gruß Frokuss

16.841 Beiträge seit 2008
vor 6 Jahren

Dafür würde man keine Sockets verwenden, sondern zB. eine API in Form von ASP.NET Core; als Übermittlungsrahmen dann JSON (bevorzugt) oder XML (alternativ).
Das alles selbst zu machen, was man mit einem fertigen Framework bekommt - vor allem Security - da wirste Dich sehr schnell übernehmen.

Dinge wie Verschlüsselung würde man einfach durch das Aktivieren von HTTPS mit einer Zeile Code durchführen.
Autorisierung einfach mit der schon vorhandenen Auth-Pipeline.

Ich mein, Du programmierst ja offensichtlich schon ein paar Tage... Wenn es keinen Grund gibt: warum das alles selbst machen?
Wieso womöglich zig oder dutzende Tage in womöglich anfälligen Code investieren, wenn es das alles schon gibt und innerhalb Minuten umsetzbar ist?

F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren

Ich glaube von der reinen Logik hast du komplett recht... In der tat habe ich noch nie (bewusst) Templates oder APIs verwendet. Wobei ich nicht so ganz plan habe, was eine API ist.

Der Grund, warum ich sowas nicht verwende ist, dass ich gerne programmiere. Aber bis vor kurzen habe ich nur was mit Webdesign und so gemacht. Ich bin auch (wer konnte es ahnen 😄) nicht vom Fach. Mein Ziel dahinter ist es daher auch etwas zu lernen.

Einfach mal doof gefragt: Gibt es überhaupt sinnvoll Anwendungsfelder für die Streammethode? Denn so wie sich das anhört würde es ja eigentlich immer Sinn machen auf eine API zurückzu greifen?

Bei meinem Code bin ich mir im klaren, dass das ganze unverschlüsselt übertragen wird, was so gesehen, im eigenen Netzwerk egal wäre und eine für mich erst eine Baustelle in fernen Zukunft ist. Welche anderen Möglichkeiten hätte ich denn, dieses Problem zu beheben?

Dies ist zwar nun mehr als nur eine Frage... Aber kannst du mir eine Stelle sagen, wo ich diese Single Responsibility verletzung mache? Ich habe es mir zwar bei Wikipedia angeguckt, aber wie in dem Beispiel sehe ich keine Variable die ich in dieser Weise abändere...

Besten Gruß Frokuss

F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren

Ich konnte das ganze nun auch an meinem eigenen Laptop, also Lokal, simulieren, indem ich in der Methode public void Senden(string txt) den Thread.Sleep(100) auf 2 reduziert habe...

Trotzdem bin ich einer Lösung nicht weiter näher gekommen... SOweit ich weiß, helfen dort auch nicht CanRead und CanWrite weiter.. oder?

Gruß Frokuss

16.841 Beiträge seit 2008
vor 6 Jahren

Klar gibt es sinnvolle Anwendungsfälle für sowas; aber dann eben richtig und für einen anderen Zweck. Spontan zB. Prozess zu Prozess Kommunikation oder eben wenn man wirklich was im Low Level Bereich braucht. Ansonsten: wozu mit Sockets und Streams beschäftigen, wenn es Frameworks gibt, die sich um das alles schon kümmern?
Das hier wird Dir mehr Probleme machen als dass es Dir produktiv hilft.

Konzentrier Dich beim Entwickeln auf die Dinge, die Du wirklich entwickeln musst.
Ein Autobauer kauft auch die Reifen einer Herstellers und baut nicht selbst Gummibäume an, um dann Gummimischungen zu entwickeln und Reifen herzustellen.

Wenn Du was lernen willst, dann benutz es als Prototyp. Absolut legitim. Das hilft ja dem Verständnis.
Aber tu bitte allen potentiellen Nutzern einen gefallen: nutze das nicht produktiv.
Das wäre ein absolutes Risiko.

Das hier ist ein typischer, alltäglicher Fall für eine Web API zB. eben auf Basis von ASP.NET Core.

Single Reponsibility sagt nichts über Variablen aus, sondern, dass eine Klasse zB. nur einen Zweck hat.
Deine Klasse hat aber viele verschiedene Aspekte: Verbindungsaufbau, Lesen, Schreiben, Verschlüsseln, Platzhalter für eine Authentifizierung, Logikelemente wie Online-Stati...

Nur vom Code lesen erkenne ich jetzt auch nicht, woran es liegt. Da müsste man vermutlich schon Zeit rein stecken um das am Rechner zu debuggen.
Dass der Client nicht alle Daten erhält, kann ja bei TCP prinzipiell gar nicht der Fall sein. Das Protokoll sorgt ja bereits dafür, dass wirklich alle Pakete, die korrekt gesendet werden, auch am Client ankommen bzw. notfalls nochmals angefragt werden.
Also entweder die Daten gehen nicht raus, oder der Client Code verarbeitet sie nicht richtig.
Das alles eben zB mit Hilfe von [Artikel] Debugger: Wie verwende ich den von Visual Studio?
Mit Tools wie Wireshark kannst Du auf Netzwerkebene schauen, ob die Pakete wirklich gesendet bzw. empfangen werden, wenn Du denkst, dass am Code alles stimmt.
Würde aber behaupten mal ordentlich zu debuggen wird Dir ne ganze Menge hier bringen.

PS: Wenn Du irgendwo ein Thread.Sleep brauchst, dann ist das ein Hinweis, dass was am Konzept des Codes nicht stimmen kann.

4.941 Beiträge seit 2008
vor 6 Jahren

Hallo Frokuss,

mit welchem ProtocolType hast du den Socket initialisiert, z.B. mit TCP oder UDP? Bei UDP ist nicht garantiert, daß jedes Paket auch ankommt (insb. in größeren Netzwerken).

F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren

Hallo Th69,

Ich benutze an beiden Stellen (Client/Server) die TCP-Variante.

Client:

		private static void ThreadCheckConnection(){
			IPHostEntry hostInfo = Dns.GetHostByName(host);//veraltet
			ep = new IPEndPoint(hostInfo.AddressList[0], port);
			myServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
			
			while(!_beenden){
				if(Connect2Server()){//Versucht eine Verbindung herzustellen - wenn keine da ist
					connectionTrys = 0;
					Thread.Sleep(500);
				}
				
				connectionTrys++;
				Thread.Sleep(500);
			}
			
			//Verbindung schließen/beenden
			try{
				MyStream ms = new MyStream(myServer);

				ms.Senden(CMD.disco, "");
				ms.Dispose();
				myServer.Dispose();
			}
			catch (Exception){}
		}

Server:

		public Verbindungen(){
			ep = new IPEndPoint(IPAddress.Any, port);
			lauscher = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
			anwenderGeraet = new List<Socket>();
			anwenderID = new Dictionary<int, AnwenderThread>();
			
			thread = new Thread(new ThreadStart(StartenVer));
			thread.Start();
		}

@Abt:
Das mit den Threadsleeps... Habe ich eigentlich nur drinnen, damit meine Threadschleifen nicht die ganze Zeit ohne Pause laufen. Dachte das verbraucht unnötig viele Ressourcen.
Aber ich glaube du hast da den Finger schon in der Wunde... Kann es sein, dass unter Umständen der Server mehrere male etwas schickt, während der Client sich in einem Sleep befindet? Daher es würden dann quasi zwei/mehrere Päckchen im gleichen Zyklus verwurstet... Könnte das an sowas liegen?

Gruß Frokuss

D
985 Beiträge seit 2014
vor 6 Jahren

In deinem Server-Code-Beispiel empfängst du eine Nachricht und schickst vier Antworten zurück.

Wie erkennt denn jetzt der Client, dass er auf vier Nachrichten warten soll?

Wenn man sich mal andere Protokolle anschaut, dann ist das da klar geregelt. Nehmen wir mal SMTP als Beispiel.

IdR kommt eine Anforderung vom Client und der Server antwortet mit einer Textzeile, die mit 3 Ziffern beginnt, einer Leerstelle und dann dem Nachrichtentext.


C->S:HELO foobar.example.net
C<-S:250 OK

Besteht das Ergebnis aber aus mehreren Zeilen, dann kann der Client das an der Antwort erkennen


C->S:EHLO client.example.com
C<-S:250-smtp.server.com Hello client.example.com
C<-S:250-SIZE 1000000
C<-S:250 AUTH LOGIN PLAIN CRAM-MD5

Ist also eine ganz einfache Regel, lese solange alle Zeilen ein, bis die 4. Stelle der Zeile ein Leerzeichen enthält.

F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren

Hallo Sir Rufo,
sorry dass meine ANtwort etwas auf sich waren lassen hat... Ich mache so eine Art Ping Pong. Daher der Client schickt etwas an den Server. z.B:

ms.Senden(CMD.SynchNachricht, "");
ms.Senden(CMD.SynchFreunde, "");//oder
ms.Senden(CMD.SynchGruppen, "");//oder

Er befindet sich dann in einer Schleife bis ein bestimmter Code vom Server kommt.

Serverreaktion:

Senden(CMD.SynchFreunde, "freund1");
Senden(CMD.SynchFreunde, "freund2");//etc.
Senden(CMD.SynchFreunde, "freund3");//etc.
Senden(CMD.next, "");//Bestimmter Code - der Client kann den nächsten Befehl senden.

Ich poste gerne etwas von meiner Client Routine - aber nur Ausschnitte, da diese eine Routine bereits 200 Zeile umfasst:

		private static void ThreadSynchSendEmpf(){
			MyStream ms = new MyStream(myServer);
			Nachricht nachricht;
			
			Dictionary<long, object> logCmd = new Dictionary<long, object>();
			
			freunde = new Dictionary<int, Data_F>();
			freundesListe = new Dictionary<int, Data_FL>();

			Thread.Sleep(100);
			
			while(!_auth && !_beenden)//Dieser Thread kann schon gestartet sein, ohne dass der Client Authentifiziert ist...
				Thread.Sleep(100);

			int c=0;
			bool synch = false;
			CMD[] MyDo = new CMD[] {CMD.SynchFreunde, CMD.SynchSortGruppe, CMD.SynchSortFreunde, CMD.SynchNachricht};
			
			//Synchronisation beginnen:
			int synchProblemCounter = 0;//Erst eingefügt weilDaten verloren gegangen sind...
			int synchProblemAbbruch = 0;//Erst eingefügt weilDaten verloren gegangen sind...
			List<MyMessagePair> tempMess = new List<MyMessagePair>();
			while(!_beenden){
				if(!synch){
					//############
					//Dies ist nur eine von zwei Stellen!
					if(!MyDo[c].Equals(CMD.SynchNachricht))
						ms.Senden(MyDo[c], "");
					else
						ms.Senden(MyDo[c], 
					//############
					
					synch = true;
					synchProblemCounter = 0;
				}
				else if(ms.DataAvailable()){
					synchProblemCounter = 0;
					nachricht = ms.Empfangen();
						
					if(nachricht.cmd.Equals(CMD.SynchFreunde)){
						if(!freunde.ContainsKey(int.Parse(nachricht.werte[0])))
							freunde.Add(int.Parse(nachricht.werte[0]), new Data_F(int.Parse(nachricht.werte[0]), bool.Parse(nachricht.werte[1]), nachricht.werte[2]));
					}
					else if(nachricht.cmd.Equals(CMD.SynchSortGruppe)){
						if(!freundesListe.ContainsKey(int.Parse(nachricht.werte[0])))
							freundesListe.Add(int.Parse(nachricht.werte[0]), new Data_FL(int.Parse(nachricht.werte[0]), nachricht.werte[1]));
					}
//.....
//.....
//.....
				}
				else{
					synchProblemCounter++;
					if(synchProblemCounter >= 3000){//1000 * 4ms = 4000ms = 4s
						synchProblemAbbruch++;
						synch = false;
						synchProblemCounter = 0;
					}
				}
				
				if(synchProblemAbbruch >= 5){
					MessageBox.Show(lang["P_Err_SynchClose"]);
					Thread.Sleep(500);
					_beenden = true;
				}
				//Thread.Sleep(constThreadWait);
				Thread.Sleep(1);
			}//While-Ende
			//Synchronisation abgeschlossen
			_synchFinish = true;
			//normal Betrieb
		}

Grüße Frokuss

F
Frokuss Themenstarter:in
158 Beiträge seit 2015
vor 6 Jahren

So, ich habe das ganze nun hinbekommen. Nun mache ich daraus wirklich ein Ping pong - daher nach jedem Stream antwortet der Client. Dachte ich könnte mir das spaaren... Aber anscheinend nicht... Geht jetzt um einiges besser.

Ich danke euch allen für die Mühe 😃

Gruß Frokuss