Laden...

Probleme mit asynchronem TCP-Empfang

Erstellt von larsen78 vor 17 Jahren Letzter Beitrag vor 17 Jahren 2.306 Views
L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren
Probleme mit asynchronem TCP-Empfang

Hallo zusammen,

ich habe einen TCP-Client, welcher Probleme mit dem Empfang hat. Bei schnellem Empfang (der Server sendet ca. aller 2 ms, wenn nicht noch schneller) klappt das mit dem Receive-Event nicht, das Event kommt immer erst, wenn schon mehr als ein Telegramm empfangen worden ist. Hatte jemand damit schon ähnliche Probleme und hat sie in den Griff gekriegt?

die Konfiguration des Clients:

m_soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse("127.0.0.1");
int iPort = 7234;
IPEndPoint IPEnd = new IPEndPoint(ip, iPort);
m_soc.Connect(IPEnd);
WaitForData();

das Ereignis:


m4d_CallBack = new AsyncCallback(OnDataReceived);
m_asyncResult = m_soc.BeginReceive(byDataBuffer, 0, byDataBuffer.Length, SocketFlags.None, m4d_CallBack, null);

Der Puffer hat eine Länge von (mittlerweile) 4096 Byte.

Die Routine zum Ereignis:


public void OnDataReceived(IAsyncResult asyn)
        {
            int iRx = 0;
            iRx = m_soc.EndReceive(asyn);
            createRStelegram(byDataBuffer, iRx);
            byEmpty.CopyTo(byDataBuffer, 0);
            WaitForData();
        }

Wieso kriege ich nicht bei jedem übertragenem Paket ein Ereignis? Liegt das vielleicht am Host? Soweit ich weiß, werden beim Host ankommende Daten sofort weitergesendet. Die beim Client ankommenden Nachrichten sollten so zwischen 9 und 50 Byte groß sein...

viele Grüße

larsen

S
8.746 Beiträge seit 2005
vor 17 Jahren

Du kriegst nicht bei jedem Paket ein Ereignis, weil es keine Pakete gibt. Das ist ein Stream. Du bekommst Ereignisse, die soviel Daten enthalten wie gerade verfügbar.

L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren

Kann ich das irgendwie so einrichten, dass ich bei jedem Paket ein Ereignis kriege? Wenn ich die Pakete von Hand schicke(also langsamer), kriege ich doch auch jedesmal ein Ereignis. Was kann ich also tun?

L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren

Hat denn keiner einen Ratschlag für mich?

L
58 Beiträge seit 2006
vor 17 Jahren

Der Puffer hat eine Länge von (mittlerweile) 4096 Byte.

Dann wird BeginReceive() versuchen, die 4096 Bytes auf vollzubekommen. Und das geht nicht, wenn du nur 9 bis 50 Bytes pro Datenstruktur, die du versendest, hast. Hast du mal probiert, die Anzahl der zu empfangenden Bytes auf die Größe eines Pakets zu reduzieren?

L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren

Nein. Das geht auch nicht, denn die Größe der Pakete ist veränderlich. Aber ich habe festgestellt, dass ich von Zeit zu Zeit ein Ereignis kriege, egal, ob der Puffer voll ist oder nicht. Das Problem war nur, dass ich bei einem vollen Puffer Gefahr laufe, dass manche Pakete nur halb drin stecken, ich die dann nicht auseinandernehmen kann und diese Daten damit für mich verloren sind. Wenn ich ein Ereignis mit nicht vollem Puffer bekomme, dann kann ich sicher sein, dass dort nur "ganze" Pakete drin stecken. Soweit meine Theorie 😉

Viele Grüße

larsen

L
58 Beiträge seit 2006
vor 17 Jahren

Hmm, was spricht dagegen, ein unvollständiges Paket zu puffern und mit dem nächsten Schwung an Daten zu interpretieren?

S
8.746 Beiträge seit 2005
vor 17 Jahren

Nochmal: In dieser Form gibt es keine Pakete. Wenn du Pakete aus einem Strom basteln willst, dann nur per Hand (lese alles in einen Zwischenpuffer und lasse diesen von einem Extra-Thread nach Paketen untersuchen und bei Auffinden einen Event auslösen). Oder du weichst auf ein IP-Protokoll aus, welches Pakete kennt.

L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren

@svenson: Das habe ich verstanden. Ich mache das jetzt schon so, wie du eben vorgeschlagen hast, d.h. mit Zwischenpuffer. Mit "Pakete" sind schon gefundene Pakete gemeint. Und da habe ich eben das Problem, dass am Ende meiner Daten ein Datenpaket nur unvollständig im Zwischenpuffer vorliegt. Aber ich mache das jetzt so, wie Löwenherz schon sagte. Ich puffere das Paket nochmal extra zwischen. Klappt auch so weit. Trotzdem die Frage: Nach welchen Kriterien werden denn dann Ereignisse beim Empfang ausgelöst? Ich bekomme nämlich auch Ereignisse, wenn der Puffer nicht voll ist.

Viele Grüße

larsen

L
58 Beiträge seit 2006
vor 17 Jahren

Hm, die MSDN sagt folgendes dazu:

Bei einem verbindungsorientierten Socket liest die BeginReceive-Methode alle verfügbaren Daten bis zu der Anzahl der Bytes, die im size-Parameter angegeben wurde.

Daher sollte der Buffer eigentlich randvoll gemacht werden, bevor die Callback-Methode aufgerufen wird. Oder hast du vielleicht einen Empfangstimeout eingestellt ?

L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren

Nein, habe ich nicht. Bei Size steht bei mir auch DataBuffer.Length, deshalb sollte der Puffer voll sein. Vielleicht liegt es aber auch am debuggen, dass ich solche Events kriege, und die kommen dann im realen Batrieb nicht vor. Wie auch immer, schade, dass es nicht Paketweise geht. Ist halt umständlicher, die Pakete selber rauszusuchen. Vielen Dank für die Tipps und die Anteilnahme 😉!

Viele Grüße

larsen

L
667 Beiträge seit 2004
vor 17 Jahren

Kannst Doch zwischen den Paketen ein "Trennzeichen" über den Stream schicken. Dann brauch Dein Thread, der auf dem Zwichenpuffer nach Paketen sucht nur immer alles zwischen zwei Trennzeichen zu nehmen.

Das Event würde ich dann garnicht beim Empfangen irgendwelcher Daten über den Stream auslösen, sondern sobald Dein Thread, der den Zwischenpuffer nach Paketen absucht, eins gefunden hat. Also nicht das Receive-Event des Streams abfangen sondern ein eigenens Event verschicken, wenn ein neues Paket angekommen ist.

"It is not wise to be wise" - Sun Tzu

S
8.746 Beiträge seit 2005
vor 17 Jahren

Die Buffersize des Streams sollte kleiner oder gleich der Paketgröße sein (bei variabler Länge die kleinstmögliche). Dann sollte es klappen. Trennzeichen müssen nicht sein, wenn die Pakete konstant groß sind. Sonst muss man sich was zum Trennen überlegen.

L
larsen78 Themenstarter:in
17 Beiträge seit 2006
vor 17 Jahren

Ich kann an den Daten, die ich empfange, nichts ändern, da als Server ein kommerzielles Programm läuft, das sich über TCP bedienen lässt und auch Daten schickt. Daher keine Trennzeichen. Allerdings haben die Paketanfänge ein muster, welches ich erkennen und als "Trennzeichen" benutzen kann. Soweit läuft jetzt mein Programm also. Wenn ich noch einen besseren Weg finde (auch, was die Puffergröße angeht), melde ich mich. Danke soweit, das Forum hier ist echt klasse.

Viele Grüße

larsen