nxdrvlinux/examples/tcpserver/tcp_connector.c

336 lines
10 KiB
C
Raw Normal View History

// SPDX-License-Identifier: MIT
/**************************************************************************************
*
* Copyright (c) 2024, Hilscher Gesellschaft fuer Systemautomation mbH. All Rights Reserved.
*
* Description: TCP/IP connector for Hilscher marshaller
*
* Changes:
*
* Version Date Author Description
* ----------------------------------------------------------------------------------
* 1 02.01.24 SD changed licensing terms
*
**************************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <unistd.h>
#include "tcp_connector.h"
#include "MarshallerErrors.h"
extern void* g_pvMarshaller;
/* flag to display once the network traffic */
extern int g_fTrafficOnce;
extern unsigned short g_usPortNumber;
/*****************************************************************************/
/*! Function called from marshaller when data is to be sent to interface
* \param ptBuffer Buffer to send
* \param pvUser TCP connector internal data */
/*****************************************************************************/
static uint32_t TCPConnectorSend(HIL_MARSHALLER_BUFFER_T* ptBuffer, void* pvUser)
{
TCP_CONN_INTERNAL_T* ptTcpData = (TCP_CONN_INTERNAL_T*)pvUser;
unsigned long ulDataLen = sizeof(ptBuffer->tTransport) +
ptBuffer->tMgmt.ulUsedDataBufferLen;
ptTcpData->ulTxCount += ulDataLen;
/* If EINTR is returned try sending the packets again. */
while(-1 == send(ptTcpData->hClient, (char*)&ptBuffer->tTransport, ulDataLen, 0) && EINTR == errno);
HilMarshallerConnTxComplete(ptBuffer->tMgmt.pvMarshaller,
ptTcpData->ulConnectorIdx,
ptBuffer);
return MARSHALLER_NO_ERROR;
}
/*****************************************************************************/
/*! TCP connector uninitialization
* \param pvUser Pointer to internal connector data */
/*****************************************************************************/
static void TCPConnectorDeinit(void* pvUser)
{
TCP_CONN_INTERNAL_T* ptTcpData = (TCP_CONN_INTERNAL_T*)pvUser;
/* Check if data is valid */
if(NULL != ptTcpData)
{
pthread_t hClientThread = ptTcpData->hClientThread;
ptTcpData->fRunning = 0;
if(0 != hClientThread)
{
pthread_join(ptTcpData->hClientThread,NULL);
}
if(INVALID_SOCKET != ptTcpData->hClient)
close(ptTcpData->hClient);
if(0 != ptTcpData->hServerThread)
{
pthread_join(ptTcpData->hServerThread,NULL);
}
if(INVALID_SOCKET != ptTcpData->hListen)
close(ptTcpData->hListen);
/* Unregister from Marshaller */
if(ptTcpData->ulConnectorIdx != (unsigned long int)~0)
{
HilMarshallerUnregisterConnector(ptTcpData->pvMarshaller, ptTcpData->ulConnectorIdx);
}
free(ptTcpData);
}
}
/*****************************************************************************/
/*! Thread handling connection with client
* \param pvParam Pointer reference to TCP connection structure
* \return Always 0 */
/*****************************************************************************/
void* ClientThread(void* pvParam)
{
TCP_CONN_INTERNAL_T* ptTcpData = (TCP_CONN_INTERNAL_T*)pvParam;
/* add here code to create timer event to display network traffic
callback function -> TrafficTimer */
while(ptTcpData->fRunning)
{
TYPE_FD_SET tRead, tExcept;
struct timeval tTimeout = {0};
int iErr = 0;
tTimeout.tv_sec = 5;
FD_ZERO(&tRead);
FD_ZERO(&tExcept);
FD_SET(ptTcpData->hClient, &tRead);
FD_SET(ptTcpData->hClient, &tExcept);
iErr = select(ptTcpData->hClient + 1, &tRead, NULL, &tExcept, &tTimeout);
if(0 < iErr)
{
if(FD_ISSET(ptTcpData->hClient, &tRead))
{
/* We have data to read */
unsigned long ulDataLen;
unsigned char* pbData;
int iRecv;
ioctl(ptTcpData->hClient,FIONREAD,&ulDataLen);
pbData = (unsigned char*)malloc(ulDataLen);
/* If EINTR is returned try receiving the packets again. */
while(-1 == (iRecv = recv(ptTcpData->hClient, (char*)pbData, ulDataLen, 0)) && EINTR == errno);
if( (SOCKET_ERROR == iRecv) ||
(0 == iRecv) )
{
/* Gracefully closed socket */
close(ptTcpData->hClient);
ptTcpData->hClient = INVALID_SOCKET;
free(pbData);
break;
} else
{
unsigned long ulDataLen = (unsigned long)iRecv;
ptTcpData->ulRxCount += ulDataLen;
HilMarshallerConnRxData(ptTcpData->pvMarshaller,
ptTcpData->ulConnectorIdx,
pbData,
ulDataLen);
}
/* Exmaple: show traffic (but only call it once) */
/* its better to call it in a cyclic timer function, like noted above*/
//if (!g_fTrafficOnce) TrafficTimer((void*)ptTcpData);
free(pbData);
}
if(FD_ISSET(ptTcpData->hClient, &tExcept))
{
/* Socket has been closed */
close(ptTcpData->hClient);
ptTcpData->hClient = INVALID_SOCKET;
break;
}
} else if (iErr == 0)
{
/* Timeout -> close socket */
close(ptTcpData->hClient);
ptTcpData->hClient = INVALID_SOCKET;
break;
}
}
ptTcpData->hClientThread = 0;
/* add code here to Kill network traffic timer event */
printf("Connection closed!\n");
return 0;
}
/*****************************************************************************/
/*! Thread serving incoming TCP connections
* \param pvParam Pointer reference to TCP connection structure
* \return Always 0 */
/*****************************************************************************/
void* ServerThread(void* pvParam)
{
TCP_CONN_INTERNAL_T* ptTcpData = (TCP_CONN_INTERNAL_T*)pvParam;
while(ptTcpData->fRunning)
{
TYPE_FD_SET tRead;
struct timeval tTimeout = {0};
tTimeout.tv_sec = 1;
FD_ZERO(&tRead);
FD_SET(ptTcpData->hListen, &tRead);
if(0 < select(ptTcpData->hListen + 1, &tRead, NULL, NULL, &tTimeout))
{
if(FD_ISSET(ptTcpData->hListen, &tRead))
{
/* We have a client to accept */
struct sockaddr_in tSockAddr = {0};
socklen_t iSockAddrLen = sizeof(tSockAddr);
SOCKET hClient = accept(ptTcpData->hListen,
(struct sockaddr*)&tSockAddr,
&iSockAddrLen);
if(ptTcpData->hClient != INVALID_SOCKET)
{
/* We already have a client, so reject this one */
close(hClient);
} else
{
char szHostname[NI_MAXHOST];
int iNoDelay = 1;
ptTcpData->ulRxCount = 0;
ptTcpData->ulTxCount = 0;
/* print host-ip */
printf("Connected with client : %s\n", inet_ntoa(tSockAddr.sin_addr));
/* Query remote name */
if (0 == getnameinfo( (struct sockaddr *) &tSockAddr, sizeof (struct sockaddr),
szHostname,NI_MAXHOST, NULL, 0, 0))
{
printf("Host Name : %s\n", szHostname);
} else
{
printf("Host name unknown!\n");
}
if ( setsockopt(hClient, IPPROTO_TCP, TCP_NODELAY, (void*)&iNoDelay, sizeof(iNoDelay)) != 0 )
{
printf("The server is not able to send small packets.\n");
printf("So the communication could be very slow!\n");
}
ptTcpData->hClient = hClient;
pthread_create(&ptTcpData->hClientThread, NULL,ClientThread, (void*)ptTcpData);
}
}
}
}
return 0;
}
/*****************************************************************************/
/*! TCP connector initialization
* \param ptParams Marshaller specific parameters (e.g. timeout)
* \param ptConfigData UART configuration data
* \param pvMarshaller Handle to the marshaller, this connector should be added
* \return MARSHALLER_NO_ERROR on success */
/*****************************************************************************/
uint32_t TCPConnectorInit(const HIL_MARSHALLER_CONNECTOR_PARAMS_T* ptParams, void* pvMarshaller)
{
HIL_MARSHALLER_CONNECTOR_T tMarshConn;
TCP_CONN_INTERNAL_T* ptTcpData = NULL;
uint32_t eRet;
if(NULL == (ptTcpData = (TCP_CONN_INTERNAL_T*)malloc(sizeof(*ptTcpData))))
{
eRet = HIL_MARSHALLER_E_OUTOFMEMORY;
} else
{
struct sockaddr_in tSockAddr = {0};
memset(&tMarshConn, 0, sizeof(tMarshConn));
memset(ptTcpData, 0, sizeof(*ptTcpData));
ptTcpData->ulConnectorIdx = (uint32_t)~0;
ptTcpData->pvMarshaller = pvMarshaller;
ptTcpData->hClient = INVALID_SOCKET;
ptTcpData->fRunning = 1;
tSockAddr.sin_addr.s_addr = INADDR_ANY;
tSockAddr.sin_port = htons(g_usPortNumber);
tSockAddr.sin_family = AF_INET;
if(INVALID_SOCKET == (ptTcpData->hListen = socket(AF_INET, SOCK_STREAM, 0)))
{
eRet = HIL_MARSHALLER_E_OUTOFRESOURCES;
} else if(SOCKET_ERROR == bind(ptTcpData->hListen, (struct sockaddr*)&tSockAddr, sizeof(tSockAddr)))
{
eRet = HIL_MARSHALLER_E_OUTOFRESOURCES;
} else if(SOCKET_ERROR == listen(ptTcpData->hListen, 0))
{
eRet = HIL_MARSHALLER_E_OUTOFRESOURCES;
} else if (0 != (pthread_create(&ptTcpData->hServerThread, NULL,ServerThread, (void*)ptTcpData)))
{
eRet = HIL_MARSHALLER_E_OUTOFRESOURCES;
} else
{
tMarshConn.pfnTransmit = TCPConnectorSend;
tMarshConn.pfnDeinit = TCPConnectorDeinit;
tMarshConn.pvUser = ptTcpData;
tMarshConn.ulDataBufferSize = ptParams->ulDataBufferSize;
tMarshConn.ulDataBufferCnt = ptParams->ulDataBufferCnt;
tMarshConn.ulTimeout = ptParams->ulTimeout;
eRet = HilMarshallerRegisterConnector(pvMarshaller,
&ptTcpData->ulConnectorIdx,
&tMarshConn);
}
/* Something has failed, so uninitialize this connector instance */
if(eRet != MARSHALLER_NO_ERROR)
{
TCPConnectorDeinit(ptTcpData);
}
}
return eRet;
}