Problem connecting to TCP server - task freezes

pnn
2012-12-04
2018-01-29
  • pnn - 2012-12-04

    Hi,

    I'm trying the TcpIp.pro example project from here
    ftp://ftp2.3s-software.com/pub/Examples ... ion/TcpIp/

    I need to use the TCPClient() program to connect to a TCP server.
    It works OK if the server is present & listening. But if the server is not present, the task to which TCPClient() program belongs freezes really badly - in the Task Configuration, for a given period, the run counter for this task increments by 7-8 while the counter for another task with equal priority and cycle time increments by 1000 for the same period. This is if I load the project to PLCWinNT emulator.

    If I load the project to a real PLC, the task freezes completely - run counter increments by 1 every 50 - 60 seconds.
    As far as I can tell, the problem is in the function call
    SysSockConnect(diSocket, ADR(sa), SIZEOF(sa))
    which returns after a really huge period of time if there is nothing to connect to.

    So my question is, what can I do to avoid this task freezing? SysSockConnect doesn't have a timeout parameter.
    Is there a simple way to just check if a device is present, like ping ?

    Thanks for any suggestions.

     
  • Strucc - 2012-12-04

    That's right, by default all the calls of SysLibSockets library are blocking.

    I recommend to check the OSCAT networking libray, see http://www.oscat.de, and browse the source for solution.
    In the IP_CONROL FB (under the HARDWARE folder), section TC_INIT, you will see the right way to use SysSockConnect.
    I think, the relevant part is:

    IF plc_841 OR plc_881 THEN
       SysSockSetOption(socket,6,SOCKET_TCP_NODELAY, ADR(dint_true), SIZEOF(dint_true)); (* Set Push-Bit *)
    END_IF;
    SysSockIoctl(socket, SOCKET_FIONBIO, ADR(dint_true)); (* NonBlocking mode *)
    

    And then, once you told ioctl to go to nonblocking mode, "select" might have called after SysSockConnect. (and also before read and write operations) The code used in OSCAT tries to determine if "select" is needed...

          IF c_select = FALSE THEN
             IF SysSockConnect(socket,ADR(sockaddr),SIZEOF(sockaddr)) THEN
                c_ready := TRUE; (* Connected *)
                state:= C_WAIT;
             ELSE
                (* Connect durchgeführt, aber noch nicht verbunden, dann optional mit select-abfrage weitermachen *)
                c_select := SYSLIBSOCKETS_OPTION.0; (* Bit 0 = TCP-Client - use SysSockselect *)
             END_IF;
          ELSE
             c_Timeout.tv_sec := 0;
             c_Timeout.tv_usec := 0;
             c_fdWrite.fd_count := 1;
             c_fdWrite.fd_array[0] := socket;
             IF SysSockSelect(SOCKET_FD_SETSIZE,0,ADR(c_fdWrite),0, ADR(c_Timeout)) > 0 THEN
                c_ready := TRUE; (* Connected *)
                state:= C_WAIT;
             END_IF;
          END_IF;
    

    The other option, is to use SysLibSocketsAsync.lib, but there are some platforms where it's not available. In this case, instead of functions function blocks are called...

    (I hope it's not a problem to quote from OSCAT. It's almost like a cookbook for many of us, thanks for the authors. Btw: Does someone know, what are plc_841 and plc_881 bool variables in OSCAT IP_CONTROL? And what is the difference between IP_CONTROL and IP_CONTROL2?

    (This could be asked at the OSCAT forum... but once we are here...)

     
  • t.lundahl - 2012-12-05

    Wago have a PLC called 750-881 and a programmable controler with the number 750-841.
    Don't know what it has to do with the code.

     
  • pnn - 2012-12-05

    thanks for the reply.
    I got OSCAT networking library, checked the code and tried some stuff. But this was pure guess-work, and indeed didn't work

    Isn't there a simple non-blocking way to only check if there is something at given IP address?

     
  • Strucc - 2012-12-06

    The following code is non-blocking, works fine. It has to be called until xConnected or a timeout reached (a TON for example).
    On your platform the SOCKET_TCP_NODELAY might not be called.

    xConnected := FALSE;
    IF diSocketID <=0 THEN
       saSocketAddress.sin_addr   := SysSockInetAddr(sIPAddr);
       saSocketAddress.sin_port   := SysSockHtons(uiPort);
       saSocketAddress.sin_family := SOCKET_AF_INET;
       diSocketID:=SysSockCreate(diAddressFamily := SOCKET_AF_INET,
                         diType := SOCKET_STREAM,
                         diProtocol          := 0);
       IF diSocketID = SOCKET_INVALID THEN
          RETURN;
       END_IF;
       SysSockSetOption(diSocketID, SOCKET_SOL, SOCKET_SO_SNDBUF, ADR(Settings.diMaxDataSize), SIZEOF(Settings.diMaxDataSize));
       SysSockSetOption(diSocketID, SOCKET_SOL, SOCKET_SO_RCVBUF, ADR(Settings.diMaxDataSize), SIZEOF(Settings.diMaxDataSize));
       SysSockSetOption(diSocketID, SOCKET_SOL, SOCKET_SO_LINGER, ADR(Settings.lOptLinger), SIZEOF(Settings.lOptLinger));
       SysSockSetOption(diSocketID, SOCKET_IPPROTO_TCP, SOCKET_TCP_NODELAY, ADR(Settings.xOptNoDelay),    SIZEOF(Settings.xOptNoDelay));
       SysSockIoctl(diSocketID, SOCKET_FIONBIO, ADR(Settings.diNonBlocking));
       xSelect := FALSE;
    END_IF
    IF NOT xSelect THEN
       IF SysSockConnect(diSocketID, ADR(saSocketAddress), SIZEOF(saSocketAddress)) THEN
          xConnected := TRUE;
       ELSE
          xSelect := Settings.xUseSelect;
       END_IF
    ELSE
       fdWrite.fd_count := 1;
       fdWrite.fd_array[0] := diSocketID;
       IF SysSockSelect(SOCKET_FD_SETSIZE,0,ADR(fdWrite),0, ADR(Settings.tvTimeout)) > 0 THEN
          xConnected := TRUE;
       END_IF;
    END_IF
    
     
  • fulminator - 2015-11-09

    Hello, the ftp link seems broke. Can you guys help me with a functional example of socket TCP_IP communication on SysLibSocketsAsync.lib library? Thank you.

     
  • danyamian - 2018-01-29

    Strucc hat geschrieben:
    The following code is non-blocking, works fine. It has to be called until xConnected or a timeout reached (a TON for example).
    On your platform the SOCKET_TCP_NODELAY might not be called.

    xConnected := FALSE;
    IF diSocketID <=0 THEN
       saSocketAddress.sin_addr   := SysSockInetAddr(sIPAddr);
       saSocketAddress.sin_port   := SysSockHtons(uiPort);
       saSocketAddress.sin_family := SOCKET_AF_INET;
       diSocketID:=SysSockCreate(diAddressFamily := SOCKET_AF_INET,
                         diType := SOCKET_STREAM,
                         diProtocol          := 0);
       IF diSocketID = SOCKET_INVALID THEN
          RETURN;
       END_IF;
       SysSockSetOption(diSocketID, SOCKET_SOL, SOCKET_SO_SNDBUF, ADR(Settings.diMaxDataSize), SIZEOF(Settings.diMaxDataSize));
       SysSockSetOption(diSocketID, SOCKET_SOL, SOCKET_SO_RCVBUF, ADR(Settings.diMaxDataSize), SIZEOF(Settings.diMaxDataSize));
       SysSockSetOption(diSocketID, SOCKET_SOL, SOCKET_SO_LINGER, ADR(Settings.lOptLinger), SIZEOF(Settings.lOptLinger));
       SysSockSetOption(diSocketID, SOCKET_IPPROTO_TCP, SOCKET_TCP_NODELAY, ADR(Settings.xOptNoDelay),    SIZEOF(Settings.xOptNoDelay));
       SysSockIoctl(diSocketID, SOCKET_FIONBIO, ADR(Settings.diNonBlocking));
       xSelect := FALSE;
    END_IF
    IF NOT xSelect THEN
       IF SysSockConnect(diSocketID, ADR(saSocketAddress), SIZEOF(saSocketAddress)) THEN
          xConnected := TRUE;
       ELSE
          xSelect := Settings.xUseSelect;
       END_IF
    ELSE
       fdWrite.fd_count := 1;
       fdWrite.fd_array[0] := diSocketID;
       IF SysSockSelect(SOCKET_FD_SETSIZE,0,ADR(fdWrite),0, ADR(Settings.tvTimeout)) > 0 THEN
          xConnected := TRUE;
       END_IF;
    END_IF
    

    Hi, I'm working on a similar project. I have a raspberry in which I need to run a TCP server where I will receive messages from clients by sockets. I can not get it to work properly, have you managed to get it to work properly? Do you have more information on the server or a example project?

    Thank you very much.

     

Log in to post a comment.