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
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.
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?
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?
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
Hmm, was spricht dagegen, ein unvollständiges Paket zu puffern und mit dem nächsten Schwung an Daten zu interpretieren?
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.
@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
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 ?
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
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
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.
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