Laden...

Deserialisierung über TCP: Das Datenstromende wurde erreicht, bevor Verarbeitung abgeschlossen ...

Erstellt von Coooder vor 9 Jahren Letzter Beitrag vor 9 Jahren 5.953 Views
Hinweis von herbivore vor 9 Jahren

Thread verschoben, nachdem sich die Ursache geklärt hat.

C
Coooder Themenstarter:in
180 Beiträge seit 2011
vor 9 Jahren
Deserialisierung über TCP: Das Datenstromende wurde erreicht, bevor Verarbeitung abgeschlossen ...

Hallo leute,

ich habe ein kleines Problem bei der Deserialisierung einer List ...
Erstmal kurz der code...

Client

public string SendMessage(List<FileVersion> message)
		{
			var networkStream = _tcpClient.GetStream();
			
			if (networkStream.CanWrite)
			{
				BinaryFormatter bf = new BinaryFormatter();
				bf.Serialize(networkStream, message);
				//networkStream.Close();
			}

			return "";
		}

Server

private static void HandleConnection(Socket socket)
		{
			byte[] byteArray = new byte[byte.MaxValue];
			int byteCount = socket.Receive(byteArray);

			Test(byteArray);
}

private static void Test(byte[] byteArray)
		{

			MemoryStream ms = new MemoryStream(byteArray);
			ms.Position = 0;

			BinaryFormatter bf = new BinaryFormatter();
			object test = bf.Deserialize(ms);
			

		}

Das Merkwürdige ist, das ich bei dem gleichen Code willkürlich verschiedene fehlermeldungen kommen ... und auch bytecount ist nicht immer gleich ...> Fehlermeldung:

Das Datenstromende wurde erreicht, bevor die Verarbeitung abgeschlossen wurde.

oder> Fehlermeldung:

Der binäre Datenstrom "0" enthält keinen gültigen BinaryHeader. Möglicherweise ist der Datenstrom ungültig oder die Objektversion wurde zwischen der Serialisierung und der Deserialisierung

byteCount hat manchmal den wert 1, 17, 255, 97, 19 ... und das beim gleichen code ...

Mach ich das mit der Serilisierung falsch? Benutzt ich den TCP stream nicht richtig? Die Klasse FileVersion hab ich mit [Serializable] gekenzeichnet und die is natürlich auch bei Client und Server bekannt.

edit: war mir jetzt nich ganz sicher ob es hier her gehört oder zu Netzwerktechnologien da ich nich weiß was davon das problem is ... 😕

F
10.010 Beiträge seit 2004
vor 9 Jahren

Benutzt ich den TCP stream nicht richtig?

Ja, dir fehlen die absoluten Grundlagen zu Netzwerk Kommunikation.

  1. Du kannst nicht auf der einen Seite einen Socket nehmen und auf der anderen TcpClient.
  2. Selbst wenn es funktionieren würde, TCP stellt nicht sicher das du die Daten in einem Packet bekommst.
  3. Weshalb du natürlich das lesen der Daten "ausserhalb" des Serialisieren machen musst.
C
Coooder Themenstarter:in
180 Beiträge seit 2011
vor 9 Jahren

Ah ok ... ich hab mich an diesem beispiel orientiert
Introduction to TCP client server in C#
Da wurde es auch so gemacht, und das hatte auch wunderbar geklappt ... Die probleme gabs halt erst als ich es mit objecten versucht habe ... 😕

Hast du vieleicht ne empfehlung was ich mir diesbezüglich dann durchlesen sollte?

C
Coooder Themenstarter:in
180 Beiträge seit 2011
vor 9 Jahren

Ok ich habs hinbekommen! Danke für den denkansatz ^^

TcpClient client = tcpListener.AcceptTcpClient();

vom client dann den stream benutzen und schon gehts!

Aber vieliecht mal so zum verständnis ... ist dann das beispiel in dem Link auch generell schlecht? Oder hat es einen guten Grund das es dort so gemacht wurde?

W
872 Beiträge seit 2005
vor 9 Jahren

Für mich sieht das alles immer noch etwas komisch aus und das Beispiel ist sehr unsauber, da mit der magischen Zahl 1000 gearbeitet wird.
Bei binären Daten solltest Du im Normalfall immer zuerst die Länge übertragen und dann die eigentlichen Daten, so daß der Empfänger genau weiß, wann die Daten zu Ende sind.
Ansonsten sollte es Dir irgendwann mal passieren, daß Du zu wenig liest, wenn die Liste zu lange wird oder das Netzwerk mal langsam ist.
Bei Text wird meist mit Trennzeichen/Zeilenumbruch gearbeitet, aber das geht bei binären Daten nicht.

C
Coooder Themenstarter:in
180 Beiträge seit 2011
vor 9 Jahren

Hey danke für den Tipp!
Habs jetzt so hinbekommen das der client die daten zum Server schickt und der Server schickt dann eine antwort zurück ... dabei hab ich die byte array größe mal nen stück zu hoch gesetzt und da sieht man schön wie alles mit 0 aufgefüllt wird ^^

Ich werde es nun so erweitern das vor den eigentlichen daten einfach die byte anzahl gesendet wird und dannach eben erst die Daten
Zumindest wenn es sich um strings handelt ... beim Serialisieren brauch ich es ja nicht mehr ^^

4.942 Beiträge seit 2008
vor 9 Jahren

Doch, auch beim Serialisieren brauchst du das - denn du weißt ja sonst nicht, wann die Nachricht komplett übertragen wurde (es kann immer sein, daß eine Nachricht nur in Teilen empfangen wird und bei mehreren Nachrichten hinereinander kann es sein, daß du evtl. das Ende einer Nachricht und den Anfang der nächsten Nachricht ausliest).
Netzwerkübertragung ist immer komplett asynchron bzgl. Senden und Empfangen - ohne ein eigenes Protokoll (mit Übergabe der Datengröße) wird es nie zuverlässig funktionieren!

F
10.010 Beiträge seit 2004
vor 9 Jahren

@coooder:

Aber vieliecht mal so zum verständnis ... ist dann das beispiel in dem Link auch generell schlecht?

Der Artikel ist von 2001, damals war so ein Schund noch OK. Ich kann die ganzen neuen positiven Bewertungen da nicht im Ansatz verstehen.

So wie weismat und Th69 schon sagten, bei der Übertragung kannst du nicht davon ausgehen das du am stück alle Daten bekommst, also musst du vor dem deserialisieren die Daten zusammenhängend empfangen.
Dazu musst du selber sicherstellen das die Anzahl auch übertargen wurde.
Dazu musst du aber auch die Anzahl wissen, also zu erst übertragen.

M
171 Beiträge seit 2012
vor 9 Jahren

Bei der TCP-Kommunikation gibt es eigentlich zwei Möglichkeiten. Entweder Du sendest mit Deinem Paket die Länge des Pakets mit. Der Empfänger liest dann erst die Länge aus (4 byte bei einem Int) und weiß dann, wieviele bytes er für das Paket lesen muss.

In dem Fall liest Du dann in einer Schleife und summierst die Bytecounts auf, bis deine gesamte Paketlänge erreicht ist.

Oder Du benutzt ein Trennzeichen zwischen zwei Paketen. Eine sehr simple Möglichkeit wäre, einen StreamWriter zu verwenden, um Deine Daten zu verschicken. Damit kannst Du mit WriteLine Deine Daten in den Stream schreiben und auf der Gegenseite die Daten mit StreamReader.ReadLine auslesen. Das NewLine der Methoden ist dann quasi das Trennzeichen.
Funktioniert natürlich nur, wenn in Deinem Paket keine weiteren NewLines vorkommen, ansonsten musst Du die vorher escapen.