Guten Tag
Ich bin neu in diesem Forum und habe mir einmal gedacht da ich hier schon für einige Probleme wirklich gute Hilfestellungen bekommen habe, bitte ich einmal hier um eure Vorschläge.
Ich suche einen Weg, möglichst performant Daten über eine TCP-Verbindung zu empfangen. Ich habe dafür um ein paar Versuche zu unternehmen einen Server und eine Client geschrieben der Dummydaten sendet.
Hier einmal die Methode vom Server welche Daten empfängt:
using (Stream stream = client.GetStream())
{
bool first = false; //Bestimmt ob es das erste Paket ist welches die Paketlänge enthält
int streamlänge = 0; //Streamlänge die das Paket hat (wird im ersten Paket mit geschickt)
int ausgelesen = 0; //Wie viele von diesem Paket bereits ausgelesen wurden
byte[] finalmessage = new byte[0]; //zusammengebaute finale Nachricht
while (LÄUFT) //Bis listening = false
{
byte[] buffer = new byte[4096];
int ausgelesenAktuell = 0; //Anzahl der im Aktuellen Paket enthalten bytes
//===========>>>>>>>>>>ERSTES PAKET (LÄNGE) ========<<<<<<<<<=======
if (first == false) //Wenn First noch nicht ausgelesen
{
byte[] länge = new byte[4]; //Byte Länge der Längeninformation
stream.Read(länge, 0, länge.Length); //Länge auslesen
streamlänge = BitConverter.ToInt32(länge, 0); //Convertieren in Int
finalmessage = new byte[streamlänge]; //neue Message definieren mit Streamlänge
if (streamlänge != 0) //Wenn Stream informationen enthält
first = true; //Erste Paket gelesen
}
//===========>>>>>>>>>>ERSTES PAKET (LÄNGE) ========<<<<<<<<<=======
//===========>>>>>>>>>>RESTLICHE PAKETE========<<<<<<<<<=======
else
{
int differenz = streamlänge - ausgelesen;
if (differenz < buffer.Length)
ausgelesenAktuell = stream.Read(buffer, 0, differenz);
else
ausgelesenAktuell = stream.Read(buffer, 0, buffer.Length);
}
//===========>>>>>>>>>>RESTLICHE PAKETE========<<<<<<<<<=======
Buffer.BlockCopy(buffer, 0, finalmessage, ausgelesen, ausgelesenAktuell);//Kopiert die angekommenen bytes in der FinalMessage
ausgelesen = ausgelesen + ausgelesenAktuell; //der bereits ausgelesene Index wird erhöht
if (ausgelesen == streamlänge && streamlänge != 0) //Wenn alle zu diesem Paket gehörigen bytes gelesen deserialize
{
first = false;
ausgelesen = 0;
//ClientMessage clientmessage;
////Deserialisieren der Resultate
//using (MemoryStream memorystream = new MemoryStream(finalmessage))
//{
// BinaryFormatter binaryformater = new BinaryFormatter();
// binaryformater.Binder = new AllowAllAssemblyVersionsDeserializationBinder();
// clientmessage = (ClientMessage)binaryformater.Deserialize(memorystream);
//}
//finalmessage = new byte[0];
//if (String.IsNullOrEmpty(HeadBenutzername) == true)
// SetHeaderInfos(clientmessage);
//else if (clientmessage.pingDate.ToString() != HeadpingDate)
//{
// HeadpingDate = clientmessage.pingDate.ToString();
// SetActualPing(HeadpingDate);
//}
}
}
}
Und hier der Code des Clients der die Daten sendet:
using (TcpClient client = new TcpClient("10.18.61.182", 4000))
{
using(Stream stream = client.GetStream())
{
ClientMessage clientmessage = new ClientMessage();
while(true)
{
byte[] buffer;
using (MemoryStream memorystream = new MemoryStream())
{
BinaryFormatter binaryforamter = new BinaryFormatter();
binaryforamter.Serialize(memorystream, clientmessage);
buffer = memorystream.ToArray();
}
byte[] messagelänge = BitConverter.GetBytes(buffer.Length);
byte[] finalmessage = new byte[buffer.Length + 4];
Buffer.BlockCopy(messagelänge, 0, finalmessage, 0, 4);
Buffer.BlockCopy(buffer, 0, finalmessage, 4, buffer.Length);
stream.Write(finalmessage, 0, finalmessage.Length);
stream.Flush();
}
}
}
}
Das Senden von Daten funktioniert schon sehr gut und schnell hier ein paar Statistiken:
Zeit: 1.332
Anz Durchläufe/Pakete: 50001
Anz bytes: 22450449
Durchschn. Durchläufe / Sek: 37538 Durchläufe/Pakete
Durchschn. anz Bytes / Sek: 16459.66KB
Allerdings bekomme ich nach dem Senden von einigen 1000 Paketen meist einen Fehler (System.OutOfMemoryException).
Der Fehler liegt daran, dass an dieser Stelle vom Server an dem eine neue finalmessage instanziert wird, die Falsche länge angegeben wird. Also anstatt der Länge von ca 455 bytes steht dann eine Zahl von mehreren 100K bytes. Also geht irgend was beim Auslesen der Paketlänge schief da meine Applikation mit den ersten 4 bytes immer vorausschickt, wie lange das Paket ist welche empfangen wird und damit der Server dann an der richtigen Stelle aufhört zu lesen.
Wenn ich allerdings an der Stelle an der der Kommentar (Deserialisieren der Resultate) steht, ein Thread.Sleep(1) angebe kommt der Fehler nicht mehr vor. Ich habe also das Gefühl ich lese zu schnell Daten aus dem Stream oder kann mir hier jemand helfen, wieso dieser Fehler kommt wenn ich zu schnell lese?.
Abschliessend noch einmal meine 2 Fragen:
Ich danke schon einmal im Voraus für Tipps und/oder Infos.
@SwitzerChees
Dein Code ist sehr unschön und sollte im Idealfall auch auf Umlaute verzichten.
Die Kommentare machen das Lesen ebenfall nicht einfacher.
Ansonsten würde ich mal vermuten, dass deine Verarbeitung hier nicht sauber ist.
Hast du mal versucht zu Debuggen welche Daten vom Server empfangen werden?
Stimmt hier ggf. die Reihenfolge der empfangenen Daten nicht(Network Byte Order)?
Ich hatte ein diesen Fehler auch häufiger bei einigen Verarbeitungen.
Hast du auch geprüft ob alle Daten in der richtigen Reihenfolge gesendet werden vom Client?
Ggf. fehlt hier ein Paket und er liest dann das falsche aus.
Ansonsten macht Thread.Sleep nur wenig Sinn.
Darauf sollte man auch verzichten können.
Anbei ist die Art wie du den Stream ausliest auch Suboptimal.
Lies alle Bytes mit einmal aus und splitte den Block dann auf die Pakete.
Aktuell schreibst du einmal den Block im Client und liest mehrfach beim Server.
Kann auch eine Fehlerquelle sein.
T-Virus
Developer, Developer, Developer, Developer....
99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
TCP garantiert eine Reihenfolge, schnelles Lesen ist nur wichtig, damit der Buffer nicht voll wird, wenn viele Daten kommen, nicht um die Reihenfolge einzuhalten.
Du hast in deinem Code eine Magic Number von 4K beim Lesen, wo irgendwo dann etwas schief läuft, da Du in 4k Blöcken liest.
Das TCP Receive Window ist mittlerweile bis zu 16 MB groß, insofern sind die 4k eher kontraproduktiv.
Dein Server Programm ist auch kompliziert, weil Du nur eine while schleife statt zwei Schleifen (eine aussen für listening/eine innere für Lesen der Message) benutzt.
Ich würde an Deiner Stelle erstmal mit einfachen Read/Write anfangen, wenn Deine Nachrichten kleiner als 16 MB sind.
Hallo,
ausserdem würde ich noch nen Pre & Suffix + crc drüber ziehen.. das macht zwar das tcp protokoll schon, aber so kannst dir wenigstens Sicher sein das deine Implementation von Server und client auch übernander passt.
Ausserdem würde ich schauen ob wcf + net.tcp binding nicht auch deinen Ansprüchen genügt. Man muss ja nicht alles neu erfinden.
Hallo Leute
Danke erstmal für eure Anmerkungen. Der Code ist nicht sauber das weiss ich werde den auch noch einmal sauber schreiben wenn ich ihn dann brauche. Wie gesagt ist das erst ein Testaufbau.
Ich habe es noch einmal versucht, diesmal aus dem Stream zu lesen bis nichts mehr kommt dann das Empfangene zu verarbeiten. Das Funktioniert auch wieder gut für eine bestimmte Anzahl von Paketen und irgend einmal kommt es wieder zum gleichen Problem. Die bytes die ausgelesen werden sind nicht mehr diese welche für das Bestimmen der Paketlänge bestimmt sind.
Ich habe auf der Clientseite überprüft die Daten werden in der richtigen Reihenfolge gesendet. Die Möglichkeit, dass Pakete verloren gehen habe ich in Betracht gezogen allerdings weiss ich nicht, wie ich das ermitteln soll denn es passiert ja meist erst nach ein paar Tausend Paketen da habe ich keine Ahnung wie ich das Debuggen soll.
Leider brauche ich für das was ich machen will, einen permanenten Objectstream also WCF fällt da weg.
Ich verstehe einfach nicht, wo diese Verwechslung oder diese fehlerhafte Verschiebung zustande kommt, dass plötzlich die bytes welche für die Paketlänge zuständig sind nicht mehr an der richtigen stelle sind. Und das passiert ja nur, wenn ich zu schnell aus dem Stream lese.
Mein aktueller Servercode sieht jetzt so aus wenn jemand noch eine Idee hätte wie dieses Problem zustande kommt, wäre ich froh.
public void ClientReader()
{
using (Stream stream = client.GetStream())
{
byte[] lastPaket = new byte[0];
int lastPaketLenght = 0;
int zähler = 0;
while (LÄUFT) //Bis listening = false
{
byte[] buffer = new byte[16777216];
int lenght = 0;
lenght = stream.Read(buffer, 0, buffer.Length);
int index = 0;
byte[] finalBuffer = new byte[lastPaket.Length + lenght];
int finalStartIndex = 0;
Buffer.BlockCopy(lastPaket, 0, finalBuffer, 0, lastPaket.Length);
finalStartIndex = lastPaket.Length;
Buffer.BlockCopy(buffer, 0, finalBuffer, finalStartIndex, finalBuffer.Length-finalStartIndex);
lenght += lastPaket.Length;
while (index < lenght)
{
int packageLength = 0;
if (lastPaketLenght == 0)
{
byte[] packageLengthBytes = new byte[4];
for (int i = 0; i < 3; i++)
{
packageLengthBytes[i] = finalBuffer[index + i];
}
index += 4;
packageLength = BitConverter.ToInt32(packageLengthBytes, 0); //Convertieren in Int
}
else
{
packageLength = lastPaketLenght;
lastPaketLenght = 0;
}
if ((finalBuffer.Length - index) < packageLength)
{
lastPaket = new byte[finalBuffer.Length - index];
for (int i = 0; i < lastPaket.Length; i++)
{
lastPaket[i] = finalBuffer[i + index];
}
lastPaketLenght = packageLength;
break;
}
else
{
byte[] package = new byte[packageLength];
Buffer.BlockCopy(finalBuffer, index, package, 0, package.Length);
index += packageLength;
zähler++;
}
}
}
}
Der Code ist nicht sauber das weiss ich werde den auch noch einmal sauber schreiben wenn ich ihn dann brauche.
Nichts hält sich so lange wie provisorischer Code.
Du musst bedenken: Du erwartest Hilfe von den Leuten.
Dann strukturier und dokumentiert doch Dein Code ein wenig mehr, sodass die Leute nicht erst 30 Minuten brauchen, bis sie verstehen, was Du tust / tun willst.
Wenns so komplizierter und unübersichtlicher Code einfach hingeklatscht wird, lesen sich 98% der Leute (mir inklusive) nicht mal Deinen Text (richtig) durch.
Ist also ein gut gemeinter Rat, wenn Du Hilfe erwartest und auch noch in 4 Wochen verstehen willst, was Du da an Code verbrochen hast.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ja da hast du recht ich sollte den zweiten Code ein wenig besser kommentieren.
Allerdings hat der bei meinem Problem auch keine Verbesserung gebracht da ist mein erster Code den ich gepostet habe noch um einige besser und dieser sollte eigentlich genug gut kommentiert und dokumentiert sein.
Ich habe gehofft, jemand hatte das gleich Problem auch schon einmal und könnte mir erklären, warum es zu diesem "Paketverlust" kommt wenn es wirklich einer ist denn der Code an sich ist nicht fehlerhaft er macht was er soll allerdings nur solange die Geschwindigkeit nicht zu hoch ist mit der er seine Aufgabe erledigen soll.