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