# TCP Socketverbindung zu S7/300 CP343-1 Lean



## ssound1de (15 Februar 2011)

Hi,

ich möchte Daten über TCP vom PC zur S7 und umgekehrt senden bzw. empfangen.
Das ganze funktioniert soweit auch.

Habe auf der S7 Seite eine Send/Receive TCP Verbindung und auf der PC Seite Socket (VB6).

Mein Problem liegt im Verbindungsaufbau. Der erste funktioniert einwandfrei und sofort.
Ein zweiter dauert teilweise bis zu 2 Min.
Evtl. hängt das mit einer XP Sicherheitsfunktion zusammen, das ein Port nach der Verwendung für eine bestimmte Zeit blockiert wird.
Auf der Socket Seite (wenn PC = Client) tritt dann der Fehler 100048 (Adresse ist bereits in Gebrauch) auf.

Hatte irgendjemand schonmal ähnliche Probleme und eine Lösung (evtl. auch eine andere Lösung) dafür?
Und wenn ja, welche?

Danke für Eure Hilfe.

Gruß


----------



## Rainer Hönle (15 Februar 2011)

Wird für den weiteren Verbindungsaufbau dasselbe socket-Objekt verwendet oder ein neues? Wie wurde die "alte" Verbindung geschlossen?


----------



## ssound1de (16 Februar 2011)

Guten Morgen Rainer,

die Verbindung wird beim Beenden des Programms mit 'socket.close' geschlossen.
Es ist dasselbe socket-objekt, gleicher Port.
Hier mal ein Code-Ausschnitt ...

server1 = socket-objekt auf der Form


```
Private Sub Form_Load()
    server1.Protocol = sckTCPProtocol
    server1.RemoteHost = "a.b.c.d"
    server1.LocalPort = 2001
    server1.RemotePort = 2000
    server1.Listen
End Sub
 
Private Sub server1_ConnectionRequest(ByVal requestID As Long)
    If server1.State <> sckClosed Then server1.Close
    server1.Accept requestID
End Sub
 
Private Sub Form_Unload(Cancel As Integer)
   If server1.State = sckConnected Then
       server1.Close
   End If
End Sub
 
Private Sub server1_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
    MsgBox "Socket Error " & Number & ": " & Description
    server1.Close
End Sub
```


----------



## argv_user (16 Februar 2011)

Versuch mal den localport unspezifiert zu lassen.
Eventuell =0 setzen, könnte helfen.


----------



## ssound1de (16 Februar 2011)

argv_user schrieb:


> Versuch mal den localport unspezifiert zu lassen.
> Eventuell =0 setzen, könnte helfen.


Das funktioniert zwar (diesen Vorschlag hab ich auch schon auf der Microsoft Website gelesen), hat aber den Nachteil, dass sich jeder mit dem CP verbinden kann.
Oder gibt es eine Möglichkeit, auf S7-Seite nur die IP ohne Port Nr. einzugeben? In Netpro kommt dann immer die Meldung ungültige Port Nummer.


----------



## Rainer Hönle (16 Februar 2011)

Such mal nach setsockopt mit SO_REUSEADDR, das könnte helfen.


----------



## argv_user (16 Februar 2011)

ssound1de schrieb:


> Das funktioniert zwar (diesen Vorschlag hab ich auch schon auf der Microsoft Website gelesen), hat aber den Nachteil, dass sich jeder mit dem CP verbinden kann.
> Oder gibt es eine Möglichkeit, auf S7-Seite nur die IP ohne Port Nr. einzugeben? In Netpro kommt dann immer die Meldung ungültige Port Nummer.



Tatsächlich kann sich jeder mit dem CP verbinden, der Port und IP kennt.
Die Simatic-Steuerungen kennen da nach meinem bisherigen Kenntnisstand
keine Zugriffsbeschränkungen. OK, mag sein, dass die RemotePort-Nummer als PIN durchgeht. Aber das hilft ja hier nicht weiter.

Besser: Nicht die "Form" die Verbindung aufbauen und schliessen lassen,
denn das ist Murks. Der Anwender kennt nämlich das Zeitverhalten der SPS nicht. Die braucht unter Umständen etwas interne Zeit, um eine Verbindung als beendet zu erklären. Stattdessen klickt da einer rum und wundert sich warum die SPS nicht schnell genug Antwort gibt.

Lösung: Tatsächlich beim Programmstart, oder eben beim erstmaligen Aufruf der Form, die Verbindung aufmachen, das Zumachen aber aufheben bis zum Programmende, nicht nur bis zum Formende. Also einfach auflassen bis es keiner mehr braucht; das klappt bestimmt. Ist ein Merker.


----------



## ssound1de (17 Februar 2011)

Ich kann doch nicht der einzige sein, der dieses Problem hat ... 

Danke für Eure Antworten.

@Rainer
Habe setsockopt probiert. Das Bit SO_REUSEADDR kann ich auch setzen.
Das ganze funktioniert aber nur über die Bind Methode - da gibt es dann ein anderes Prob.
Die Connect Methode liefert als Handle -1 was logischweise für setsockopt ungültig ist.
Über Bind bekomme ich zwar ein Handle, Socket ist dann aber nicht im Socket.State = Connected (7) sondern im Socket.State = Connecting (6).
Und bleibt da auch. Jetzt hänge ich an dem Punkt wieder fest. 

@argv_user
Mein Testprog hat nur eine Form. Programmstart = Formload und Form unload = Ende und close. 

Noch irgendeine Idee?
Wenn ichs nicht hinbekomme müssen wir halt mit den max. 2 Min. Verzögerung leben. Ist jetzt auch nicht superschlimm, aber trotzdem nervig.

Und auf der S7 Seite nur die IP des Zielrechners ohne Port eingeben geht wohl nicht, oder? Dann wäre nämlich für die S7 klar, das nur eine Verbindung von Rechner ABC erlaubt ist und von sonst niemandem.


----------



## pvbrowser (17 Februar 2011)

Ich kann mich düster daran erinnern früher auch mal so ein problem gehabt zu haben.
Heute sieht das bei mir so aus:

        setsockopt(os,SOL_SOCKET,SO_REUSEADDR,(const char *) &option,sizeof(option));
<snip>
        ret = bind(os, (struct sockaddr *) &localAddr, sizeof(localAddr));
<snip>  
       ret = listen(os, 5);
<snip>  
    s = accept(os, (struct sockaddr *) &sockaddr[0], &socklen);

Also: SO_REUSEADDR ist wichtig und evtl. auch listen(os,5) damit er mehrere anliegende verbindungswünsche akzeptiert.


----------



## pvbrowser (17 Februar 2011)

Ach, noch was:

Bei der 2-ten verbindung muss nur noch accept aufgerufen werden.

Nicht:setsockopt, bind und listen


----------



## pvbrowser (17 Februar 2011)

Hier ist der vollständige quellcode:

http://pvbrowser.org/pvbrowser/sf/m...Socket.html#ad30990162e716dce512116d9336de89e


----------



## ssound1de (18 Februar 2011)

Hi pvbrowser,

Danke für Deine Antwort.

Habe den Quellcode durchgeackert. Das hilft mir schon etwas weiter.

Habe aber doch noch ein paar Fragen dazu.

Ich sehe/finde keine Eventhandler.
1. Woher weiß ich ob ein Client connecten will um dann accept auszuführen?
2. Woher weiß ich ob neue Daten angekommen sind? read über timer-interval aufrufen?
3. Worauf verweist "(struct sockaddr *) &sockaddr[0]" in Line 00307? Einfach ein Zeiger auf sockaddr? (Bin leider kein C-ler)

Sorry für die vielen Fragen 

Danke und Gruß


----------



## argv_user (18 Februar 2011)

ssound1de schrieb:


> Ich kann doch nicht der einzige sein, der dieses Problem hat ...
> 
> @argv_user
> Mein Testprog hat nur eine Form. Programmstart = Formload und Form unload = Ende und close.



Das ist dann ja ziemlich eindeutig: Du musst jedenfalls warten bis die 
Steuerung wieder kann.  Eventuell beim Formstart die Sanduhr einblenden. 

Die von Dir genannte Zeit geht kann ich bestätigen.
Und das Problem liegt nicht beim PC!


----------



## pvbrowser (18 Februar 2011)

ssound1de schrieb:


> Hi pvbrowser,
> Habe aber doch noch ein paar Fragen dazu.
> 
> Ich sehe/finde keine Eventhandler.
> ...



(1,2):
Das ist eine Bibliothek für Socket Kommunikation, da ist kein Eventhandler drin.
Ich mache immer einen extra Thread auf, der auf dem Netzwerk wartet.
Dann weiss er auch immer wann Daten gekommen sind.
Man kann aber auch mit select auf Daten warten (mit timeout).
http://pvbrowser.org/pvbrowser/sf/m...Socket.html#a2c2112acf4ae8254866cbb40bdc6596a

(3):
Das ist eine Member Variable von rlSocket.
s = accept(os, (struct sockaddr *) &sockaddr[0], &socklen);
Da steht dann u.a. drin von welcher adresse aus versucht wird zu connecten. Du könntest da z.B. nur Adressen aus dem lokalen Subnetz akzetieren, um das sicherer zu machen.


----------



## pvbrowser (18 Februar 2011)

Unter Windows gibt es noch eine nicht Posix kompatible Socket Schnittstelle, die mit asynchronen Aufrufen arbeitet.
Meine Bibliothek soll aber gerade portabel sein und verwendet daher nur portierbare Funktionen. Die einzelnen Betriebssysteme werden innerhalb der Methoden über #ifdef unterschieden.

Achtung: accept() wartet blockierend bis sich ein Client verbindet. Das wäre tödlich für jeden Eventloop. Daher mache ich accept() in einem eigenen Thread, wenn ich GUI Anwendungen habe.

Genauer gesagt verwende ich Qt für GUI Programme. Der separate Thread überwacht dabei das Netzwerk, während das Hauptprogramm den Benutzer bedient. Wenn dann Daten angekommen sind, wird ein Qt Signal emittiert und das Hauptprogramm kann sich die Daten abholen. Dazu braucht es sich nur mit connect an das emittierte Signal hängen.

Meine Bibliothek verwende ich mit und auch ohne Qt.
Sie enthält evtl. Klassen die auch hier von Interesse sein könnten.
(u.a. SPS Kommunikation)
Siehe:
http://pvbrowser.org/pvbrowser/sf/manual/rllib/html/classes.html


----------



## ssound1de (25 Februar 2011)

Wow - hab nicht gedacht, dass ichs noch hinkriegen würde, aber jetzt läufts.

Vielen Dank an Alle - speziell @pvbrowser

Das ganze sieht jetzt wie folgt aus ...


```
'Vorbereitung
wsastartup (...)
<snip>
socket (...)
<snip>
setsockopt (...)
<snip>
bind (...)
<snip>
 
listen (...)
<snip>
wsaasyncselect (...) Event für accept festlegen
<snip>
 
'Connection Request
accept (...)
<snip>
wsaasyncselect (...) Event für receive festlegen
<snip>
 
'Receive/Send
recv (...)
<snip>
send (...)
<snip>
 
'Beenden
closesocket (...)
<snip>
wsacleanup ()
```
 
Habe in meiner Test-S7 jetzt 2 CP's (343-1 Lean).
Beide verbinden sich zügig mit dem socket. Auch nach Programm Neustart.

Danke nochmal und Gruß.


----------

