I have downloaded an example project for a TCP socket server running on a Raspberry Pi. Code copied from the example for socket communication Linux SL.
See the code below:
TON1(IN:=xOpen, PT:=TIME#30S); IF hMySocket = SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE AND TON1.Q THEN // create a socket for an IPv4 TCP stream hMySocket := SysSockCreate( iAddressFamily := SOCKET_AF_INET, (* SOCKET_AF_INET for ip4 connection. We do it locally, so it is a loopback *) diType := SOCKET_STREAM, diProtocol := SOCKET_IPPROTO_TCP, pResult := ADR(result)); xIsBound := FALSE; xOpen := FALSE; END_IF // go through the process of binding the port IF hMySocket <> SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE AND NOT xIsBound THEN // set up port to reuse address so we can bind to ANY ip (0.0.0.0) result := SysSockSetOption( hSocket := hMySocket, diLevel := SOCKET_SOL, // socket level diOption := SOCKET_SO_REUSEADDR, // allow using an addres (e.g. localhost or ANY) multiple times pdiOptionValue := ADR(diReuseAddr), // enable diOptionLen := SIZEOF(diReuseAddr)); // fill out the struct used to pass along the binding information socketAddress.sin_family := SOCKET_AF_INET; // same as on creation socketAddress.sin_addr.ulAddr := SOCKET_INADDR_ANY; // we bind to any interface socketAddress.sin_port := SysSockHtons(8000); // the port number we chose, converted to ethernet byte order // now do the actual binding result1 := SysSockBind( hSocket := hMySocket, pSockAddr := ADR(socketAddress), diSockAddrSize := SIZEOF(socketAddress)); xIsBound := result1 = Errors.ERR_OK; // set socket to start listening for incoming connections result := SysSockListen( hSocket := hMySocket, diMaxConnections:= 1); xIsBound := xIsBound AND result = Errors.ERR_OK; // set the option for non-blocking so accept() returns immediatly instead of waiting until a connection is made diNonblocking := 1; result := SysSockIoctl( hSocket := hMySocket, diCommand := SOCKET_FIONBIO, // non-blocking option pdiParameter := ADR(diNonblocking)); // set it to 1 xIsBound := xIsBound AND result = Errors.ERR_OK; END_IF // accept incoming connection IF hMySocket <> SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE AND xIsBound AND NOT xIsConnected THEN // accept the connection if possible. As an output we get information about the socket we are connectiong to hAcceptedSocket := SysSockAccept( hSocket := hMySocket, pSockAddr := ADR(saConnectionPartner), pdiSockAddrSize := ADR(diSaConnectionPartnerSize), pResult := ADR(result)); xIsConnected := hAcceptedSocket <> SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE; // set the option for non-blocking so accept() returns immediatly instead of waiting until a connection is made diNonblocking := 1; result := SysSockIoctl( hSocket := hAcceptedSocket, diCommand := SOCKET_FIONBIO, // non-blocking option pdiParameter := ADR(diNonblocking)); // set it to 1 END_IF // use the handle of the accepted connection to read the data that is arriving. IF hAcceptedSocket <> SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE AND xIsConnected THEN // Send data to the connected client TON2(IN:=NOT xSendData, PT:=TIME#200MS); xSendData:=TON2.Q; IF (xSendData) THEN xiBytesSend:=SysSockSend(hAcceptedSocket, pWriteData, SizeWriteData, 0, ADR(resultSend)); END_IF // build the data structure needed to read a socket socketSet.fd_count := 1; socketSet.fd_array[0] := hAcceptedSocket; // check if there is anyhting to be read result := SysSockSelect( pfdRead := ADR(socketSet), // pointer to set of the sockets we want to select on pfdWrite := 0, // not used pfdExcept := 0, // not used diWidth := SOCKET_FD_SETSIZE, // maximum number of sockets in socketSet ptvTimeout := ADR(tvSelectTimeout), pdiReady := ADR(diSocketsReadyToReceive)); IF diSocketsReadyToReceive > 0 THEN // read the bytes waiting in the socket. xiBytesReceived := SysSockRecv( hSocket := hAcceptedSocket, // the connected socket to read from pbyBuffer := ADR(uiMessage), // where we read to diBufferSize := SIZEOF(uiMessage), // how much we read diFlags := SOCKET_MSG_NONE, // no flags pResult := ADR(result)); END_IF // if we don't have a timeout or we would not block, something failed, so connection must have dropped IF result <> Errors.ERR_OK AND result <> Errors.ERR_TIMEOUT AND result <> Errors.ERR_SOCK_WOULDBLOCK AND result <> Errors.ERR_SOCK_TIMEDOUT THEN xIsConnected := FALSE; SysSockClose(hSocket := hAcceptedSocket); hAcceptedSocket := SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE; END_IF END_IF // close the socket. IF hMySocket <> SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE AND xClose THEN SysSockClose(hSocket := hMySocket); SysSockClose(hSocket := hAcceptedSocket); xClose := FALSE; xIsBound := FALSE; xIsConnected := FALSE; hMySocket := SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE; hAcceptedSocket := SysSocket.SysSocket_Interfaces.RTS_INVALID_HANDLE; END_IF
The problem with this code arrises when bind is called after a complete download of my project. I've connected xOpen and xClose to the start en stop system events of the controller (see TaskConfiguration.png). So after a download xOpen will become active for 30 sec. and it will start the server. This works when the controller is stopped and started.
The problem is that after a download a created socket will remain active on the OS. So we use the socket option SOCKET_SO_REUSEADDR. However the bind function will fail with error code 519 Address in use. So it doesn't reuse the address. You can also specify SOCKET_SO_REUSEPORT however that results in an error.
I thought i solved the problem by creating a System event stopping the server just before a download commences. With the idea in mind that the socket will also be released on the OS.
However I've tried several system events without any result. The system events i've tried are: PrepareDownload and PrepareExitTasks and PrepareOnlineChange.
Hi!
Unfortunately you asked this question in the wrong place. This is the support ticket system for the CODEYS Forge web page.
You may want to ask this question in CODESYS Talk