# libnodave timeout Problem



## Key (26 Januar 2009)

Hallo Community,

erst einmal Danke an Zottel für libnodave. Ich habe bisher alle Probleme die ich so hatte (meistens Userprobleme^^) umschiffen können. Nun habe ich aber evtl. ein Problem in libnodave selbst.

Verbindungsaufbau, Lesen, Schreiben, Abbau, alles prima. Nun habe ich durch einen Zufall folgendes festgestellt.

In dem Augenblick wo ich ein readManyBytes() von einem ca. 4k Großen Block(braucht ca. 1 sek) und genau in diesem Lesevorgang die Verbindung auf SPS-Seite getrennt wird, passiert garnichts mehr. Ich erhalte auch nach mehreren Minuten kein Timeout oder etwas in der Art.

Wichtig ist dabei die folgende Config.

PC mit libnodave -> verbunden mit Switch -> verbunden mit CP343-1

wird die Verbindung auf PC Seite getrennt und steht somit keine Netzwerkverbindung mehr(die zwei kleinen Cumputer unten rechts in der Ecke mit nem X) so kommt ein Connect Timeout (libnodave Fehler -1025), soweit OK.

wird die Verbindung auf SPS Seite getrennt(SPS zu Switch), hat der PC ja noch eine Verbindung mit dem Netzwerk, und es taucht beschriebenes Problem auf. Es kommt kein Fehler und er hängt in der readManyBytes() Funktion von libnodave.

Das ganze ist aufgefallen als meine Applikation in einen Thread lief und die SPS genau in dieser Leseanfrage Stromlos war und somit auch die CP343-1.

Kennt jemand das Problem oder kann mir sagen wie ich es beseitigen kann??? Soweit ich weiß hatte ja die Timeout Option in libnodave ja keine Relevanz für TCP on ISO oder?!

Verwende Version 0.8.4.4 .NET DLL
Visual C#.NET 2005

Gruß Key


----------



## Key (28 Januar 2009)

Problem ist immer noch aktuell.

Keiner da der helfen kann???


----------



## Rainer Hönle (28 Januar 2009)

Grundsätzlich sieht es so aus, dass es im TCP/IP-Stack einen eigenen Timeout gibt. Dieser liegt nach meinen Erfahrungen im Bereich von 40-45 Sekunden. Dann wird von diesem ein Fehler gemeldet. Meldet libnodave sich  trotzdem nach dieser Zeit nicht mit Fehler zurück?
Was bedeutet "Soweit ich weiß hatte ja die Timeout Option in libnodave ja keine Relevanz für TCP on ISO oder?!"? Schon einmal getestet und festgestellt oder ist dies eine reine Vermutung?


----------



## Key (28 Januar 2009)

Hallo Herr Hönle,

wie gesagt es tut sich auch nach mehreren Minuten nichts.


```
[SIZE=2]di = [SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] libnodave.daveInterface(fds, [/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]"IF1"[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2], 0, libnodave.daveProtoISOTCP, libnodave.daveSpeed187k);[/SIZE]
[SIZE=2]di.setTimeout(1000000);[/SIZE]
[/SIZE]
```
 
entspricht dem Standard laut Doku. Ich hatte diesen Wert schon in beide Richtungen verändert. Es hat sich jedoch nichts getan. Bin mir allerdings nicht sicher ob es Millisekunden sein sollen oder Ticks. Das wären dann entweder 1000. Sek oder 1. Sek. was bei MS ein langes Timeout wäre. Werde es noch mal prüfen.

Was mir noch aufgefallen ist
*Version 0.8.1*

Fixes a bug in MPI which made applications wait forever if PLC doesn't respond

Kann sich da etwas in die neuere Version eingeschlichen haben?


----------



## Key (28 Januar 2009)

So ich wieder.

Hab jetzt folgendes probiert.

Hab mal das einfache TCP_ISO Beispiel genommen um evtl. Fehler in meiner App auszuschließen.

Ich habe es mit ReadManyBytes und ReadBytes probiert. Selbes Resultat. Auch das Ändern der Timeout Werte in Schritten bis auf 10 runter.

Sobald die Verbindung direkt am PC getrennt wird kommt nach ein paar Sekunden die bekannte Timeout Meldung -1025. Soweit OK.

Trenne ich die Verbindung auf SPS Seite passiert nichts mehr. Er bleibt einfach in dem Read Befehl stecken. Gleiches gilt übrigends auch für beide Writes wie ich festgestellt habe. Ich habe auch nochmal den HUB gegen einen Switch getauscht. Bringt auch nix.

Muss ich das jetzt irgendwie selbst prüfen???


----------



## Rainer Hönle (28 Januar 2009)

An dieser Stelle müssen jetzt die libnodave-Profis einspringen (zottel, Ralle, afk, .. um nur wenige zu nennen). 
Ich weiss nur, wie es bei unser Bibliothek realisiert ist, dass mir dieses Problem auch aufgefallen ist und ich für diesem Fall eine Timeout-Meldung implementiert habe.


----------



## Key (28 Januar 2009)

Ich vermute mal auch so etwas. Ich warte mal noch was die Profis dazu meinen.

Ansonsten werd ich wohl oder übel das ganze noch mit ein paar Events erweitern müssen.


----------



## Zottel (28 Januar 2009)

Key schrieb:


> Ich vermute mal auch so etwas. Ich warte mal noch was die Profis dazu meinen.
> 
> Ansonsten werd ich wohl oder übel das ganze noch mit ein paar Events erweitern müssen.


Es ist wohl bei der Umsetzung Linux/Windows was schiefgelaufen:

settimeout setzt einen Wert, der unter Linux verwendet wird, um mittels select() darauf zu warten, daß Zeichen zum Lesen verfügbar sind. 
Der Wert ist in us (Mikrosekunden).
Dann wird mit read() gelesen. Dies wird für serielle Schnittstellen und sockets einheitlich gehandhabt.

Unter Windows geht das nicht, daher wird von sockets mit recv() gelesen. recv() kehrt nicht zurück, wenn nicht die nötige Zahl von Zeichen da ist.
Bei den Net-Link Protokollen wird mittels select() ermittelt, ob Zeichen verfügbar sind, bevor recv() aufgerufen wird. Bei ISO_TCP aber nicht!!

Ich habe mal schnell eine Version erstellt, die das auch bei ISO_TCP macht.

Bitte die Endung .doc entfernen, die ist nur damit das Forum nicht meckert! Der richtige Filename ist libnodave.dll !


----------



## Key (28 Januar 2009)

Habe es gerade mit einem Timer implementiert der das ElapsedEvent feuert wenn er abgelaufen ist. Funktioniert so einwanfrei. Nur schade dass es nicht direkt von der Lib so kommt.

Edit: OK versuche ich es mal damit. Ist also ein Bug gewesen?


----------



## Rainer Hönle (28 Januar 2009)

Key schrieb:


> Edit: OK versuche ich es mal damit. Ist also ein Bug gewesen?


[Satire An]Nein ein Feature.[Satire Aus] Dies würde jedenfalls so von manchen großen Unternehmen dargestellt. Ich meine die mit mindestens einem S im Namen ;-).


----------



## Key (28 Januar 2009)

Sorry Zottel, aber damit funktioniert es auch nicht. Sicher die neue Version??? Oder doch noch etwas anderes?


----------



## Zottel (28 Januar 2009)

Womit testest du? Mir fällt gerade ein, daß testISO_TCP die dll gar nicht nutzt. Es ist statisch gelinkt. Daher hänge ich es hier an.


----------



## Key (28 Januar 2009)

Nene ich nutze das SourceBeispiel für C#.NET "simpleISO_TCP.cs" also nimmt er schon die richtige DLL.

Edit: Ich lese bei mir im Original einen Block von ca. 3k Bytes. In der Test lasse ich es in iner Schleife laufen.


```
[SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]for[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] ([/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] i = 0; i < 1000; i++)[/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2]res = dc.readBytes([/SIZE][SIZE=2][COLOR=#2b91af][SIZE=2][COLOR=#2b91af]libnodave[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].daveDB, 702, 0, 200, buffer);[/SIZE]
[SIZE=2][COLOR=#008000] 
[/COLOR][/SIZE][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000][/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] (res == 0)[/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2]a = dc.getS32();[/SIZE]
[SIZE=2][COLOR=#2b91af][SIZE=2][COLOR=#2b91af]Console[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].WriteLine([/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]"FD0: "[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] + a);[/SIZE]
[SIZE=2]}[/SIZE]
[SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]else[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]{[/SIZE]
[SIZE=2][COLOR=#2b91af][SIZE=2][COLOR=#2b91af]Console[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].WriteLine([/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]"error "[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] + res + [/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]" "[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] + [/SIZE][SIZE=2][COLOR=#2b91af][SIZE=2][COLOR=#2b91af]libnodave[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].daveStrerror(res));[/SIZE]
[SIZE=2]}[/SIZE]
[SIZE=2]}[/SIZE]
```


----------



## Zottel (28 Januar 2009)

Wie unterbrichst du die Verbindung?


----------



## Key (28 Januar 2009)

Stecker ziehen^^. Ne spass bei Seite. Wie schon ganz vorn beschrieben kommt die Timeoutmeldung -1025 nur wenn ich das LAN Kabel auf PC seite ziehe. Also Kein Netz. Wenn ich das hinter dem Switch auf SPS Seite mache hat der PC ja noch Netz aber der Griff geht ins leere. Und genau da kommt keine Meldung zurück. Er bleibt im Read bzw. Write stecken und kommt nicht mehr raus.


----------



## Rainer Hönle (28 Januar 2009)

Zottel schrieb:


> Wie unterbrichst du die Verbindung?



Hallo Zottel,
schön wieder mehr von  Dir zu lesen.
Bei meinen Versuchen habe ich festgestellt, dass bei Ziehen des Kabels jenseits des nächsten Überganges (in der Regel Switch) der Line Break nicht erkannt wird (wie auch). Wird das direkte Kabel gezogen, kommt sofort eine Fehlermeldung vom TCP/IP-Stack zurück.


----------



## Key (28 Januar 2009)

Kann man das überhaupt richtig abfragen außer mit einem Timer. Weil eine Meldung kommt doch sicher nicht. Oder hat das was mit dem TTL usw. zu tun und ist es darüber evtl. steuerbar? Hab da nicht so die Ahnung


----------



## Zottel (28 Januar 2009)

Key schrieb:


> Habe es gerade mit einem Timer implementiert der das ElapsedEvent feuert wenn er abgelaufen ist. Funktioniert so einwanfrei. Nur schade dass es nicht direkt von der Lib so kommt.


Sehe ich nicht so. Ich meine, die Bibliothek sollte sich auf das Wesentliche beschränken. Wenn du C programmierst, bekommst du auch zwischen 
openSocket und daveNewInterface das socket handle "in die Hand" und kannst mit setsockopt() timeout oder O_NONBLOCK einstellen. Ob es in deinem Fall nützen würde und ob es aus Dot.NET funktioniert, weiß ich gerade nicht...
Was passiert aber in einem "fetten" Programm wie EXCEL, wenn ein VBA Makro eine C-Funktion ruft, die einen timer startet und zurückkehrt, bevor er abläuft?
Und: Je weniger Betriebssystemfunktionen einen Bibliothek nutzt, desto leichter ist sie portierbar und desto eher läuft sie  auf neuen Versionen.


----------



## Key (28 Januar 2009)

Den Timer hab ich in C#.NET

An der Lib selbst habe ich nichts geändert. Möchte ich ja auch nicht. Es soll auch keine Kritik sein. Meine Frage war nur ob ich in so einem Fall nicht etwas von dem Read bzw. Write zurückerhalten sollte.

ich mache im grunde bei mir nichts anderes als


```
[SIZE=2]m_Timer.Start();[/SIZE]
[SIZE=2]res = dc.readManyBytes(area, areaNumber, startByte, length, buffer);[/SIZE]
[SIZE=2]m_Timer.Stop();[/SIZE]
```
 
läuft der Timer ab wird das Event gefeuert. Bin mir aber sicher das es noch eleganter geht^^


----------



## Zottel (28 Januar 2009)

Key schrieb:


> Kann man das überhaupt richtig abfragen außer mit einem Timer. Weil eine Meldung kommt doch sicher nicht. Oder hat das was mit dem TTL usw. zu tun und ist es darüber evtl. steuerbar? Hab da nicht so die Ahnung


TTL ist nur dafür da, zu verhindern, daß Pakete ewig im Netz zirkulieren: Bei komplizierten Wegen über Gateways/Router oder wenn während des Pakettransports die Routingtabellen eines Teilnehmers/Gateways/Routers verändert werden, könnten geschlossene Kreise enstehen. 
Daher zählt jeder Knoten, der ein Paket weiterreicht,  TTL herunter und wenn es 0 erreicht, schmeißt er es weg.

Das Verhalten deines TCP/IP-Stacks läßt sich eventuell über setsockopt() beeinflussen.


----------



## Zottel (28 Januar 2009)

Key schrieb:


> Den Timer hab ich in C#.NET
> 
> An der Lib selbst habe ich nichts geändert. Möchte ich ja auch nicht. Es soll auch keine Kritik sein. Meine Frage war nur ob ich in so einem Fall nicht etwas von dem Read bzw. Write zurückerhalten sollte.


Normalerweise kehrt recv() zurück, wenn die angeforderten Zeichen gelesen wurden. 
Wenn das Kabel am PC gezogen wird, ist der TCP/IP Stack wohl "schlau" genug, um die Verbindung zu beenden, das socket zu schließen und anhängige recv()-Aufrufe zu beenden.
Libnodave realisiert das timeout wie gesagt mit select(), indem es vorher schaut, ob Zeichen da sind oder innerhalb der Timeout-Zeit verfügbar werden.
Bleibt zu fragen, was denn passiert, wenn zwar Zeichen da sind, aber nicht so viele wie recv() lesen will.
Das sollte m.E. nicht passieren können, da der TCP Stack auf jeden Fall nur vollständige Ethernet-Pakete zurückliefert (wenn du das Kabel mitten im Paket ziehst, wird das Paket komplett verworfen).
Eine andere Frage ist, ob ein ISO-Paket auf mehrere TCP-Pakete aufgeteilt werden könnte...?


----------



## Rainer Hönle (28 Januar 2009)

Zottel schrieb:


> Eine andere Frage ist, ob ein ISO-Paket auf mehrere TCP-Pakete aufgeteilt werden könnte...?


Geht selbstverständlich, das macht aber ausschließlich die CP bzw. der PC.


----------



## argv_user (28 Januar 2009)

Zottel schrieb:


> Normalerweise kehrt recv() zurück, wenn die angeforderten Zeichen gelesen wurden.
> Wenn das Kabel am PC gezogen wird, ist der TCP/IP Stack wohl "schlau" genug, um die Verbindung zu beenden, das socket zu schließen und anhängige recv()-Aufrufe zu beenden.
> Libnodave realisiert das timeout wie gesagt mit select(), indem es vorher schaut, ob Zeichen da sind oder innerhalb der Timeout-Zeit verfügbar werden.
> Bleibt zu fragen, was denn passiert, wenn zwar Zeichen da sind, aber nicht so viele wie recv() lesen will.
> ...



Es besteht die Möglichkeit beim Verbindungsaufbau eine geringere Länge
der ISO-Pakete auszuhandeln. Allerdings bringt das keine Lösung, denn
Du weißt nicht ob das alles in ein TCP-Paket passt.

Eine Möglichkeit für das Timeout sehe ich eigentlich nur darin, dass libnodave NONBLOCKING verwendet; nur dürfte das etwas viel Arbeit sein,
die ganze Geschichte umzustellen...


----------



## afk (25 März 2009)

Nach längerer Abstinenz melde ich mich hiermit im Forum zurück ... und hoffe, daß ich für diesen Thread nicht viel zu spät bin.

Das Problem kenne ich, das hat nichts mit libnodave zu tun, sondern ist ein konzeptbedingtes Problem bei jeder TCP/IP-Kommunikation. Wenn ein Teilnehmer (in diesem Fall libnodave) auf Daten einer Gegenstelle (hier die SPS) wartet, und die Verbindung wird irgendwo unterwegs unterbrochen, dann wird dieser Wartezustand vom TCP/IP-Stack bis zum vielgerühmten "Sankt Nimmerleinstag" aufrecht erhalten. Ein Verbindungsfehler irgendwo "unterwegs" wird nur vom sendenden Teilnehmer erkannt (weil kein ACK zurückkommt), ein Teilnehmer, der nur auf eingehende Daten wartet, ohne zwischendurch selbst etwas zu senden, wartet eben ewig auf das nächste Datenpaket.

Bei libnodave kommt hinzu, daß beim Übertragen großer Datenmengen per S7-Protokoll der PC relativ lange auf die Antwort der SPC warten muß, aber nach dem Eingang der Antwort relativ schnell die die nächste Anfrage schickt. Dadurch ist die Wahrscheinlichkeit recht hoch, daß libnodave gerade auf Daten von der SPS wartet, wenn die Verbindung unterbrochen wird.

Soweit die Theorie, jetzt zur praktischen Lösung des Problems:

Das TCP/IP-Protokoll kennt sogenannte Keep-Alive-Pakete, das sind "leere" Datenpakete, die vom TCP/IP-Stack automatisch verschickt werden, um die Verbindung aufrecht zu halten und zu überwachen. Diese Funktion ist bei Windows per Default deaktiviert, kann aber per Registry-Eintrag aktiviert werden, was man IMHO aber lassen sollte, da davon alle TCP-Verbindungen betroffen sind. Man kann diese Funktion aber auch per API-Aufruf mit der Funktion *WSAIoctl* in der *ws2_32.dll* für einen einzelnen TCP-Socket aktivieren. Wird die Funktion mit dem Handle des Sockets, über den libnodave mit der SPS kommuniziert, dem Control-Code *SIO_KEEPALIVE_VALS* und den entsprechenden Parametern für die Timeouts aufgerufen, wird eine unterbrochene Verbindung vom TCP/IP-Stack korrekt erkannt und der entsprechende Fehlercode an libnodave weitergegeben.

Sorry, daß ich so spät damit dran bin. 


Gruß Axel


----------



## Key (30 März 2009)

Also wäre eine Möglichkeit libnodave so anzupassen, das man über die Funktion Timeout(), die ja für Serielle Verbindungen funktioniert, das sie ein Keep-Alive abfragt. Muss natürlich für die jeweilige Socketverbindung aktiviert werden. Aber denke ich könnte ganz hilfreich sein.

Wäre das möglich?


----------



## afk (31 März 2009)

Möglich wäre das schon, aber da für den KeepAlive noch 2 zusätzliche Timeout-Werte vorgegeben werden müssen, die libnodave so (noch) nicht kennt, geht das genauso gut von "außen", da die libnodave-Funktion OpenSocket ja das Socket-Handle zurückliefert.


Gruß Axel


----------



## Key (31 März 2009)

Danke afk.

Ich glaube ich habe etwas passendes gefunden.

Socket.IOControl
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.iocontrol.aspx

Standradmäßig ist es über die Registry möglich die Werte für das Keep-Alive zu aktivieren. Das gilt dann aber für alle Sockets was ja nicht unbedingt erwünscht ist.

Als Code Variante habe ich hier einen passenden Artikel in dem es gelöst wird.
http://social.microsoft.com/Forums/en-US/netfxnetcom/thread/d5b6ae25-eac8-4e3d-9782-53059de04628/

Gruß Key


----------

