/*********************************************************************************
Copyright (cpp) Novatek Microelectronics Corp., Ltd. All Rights Reserved.
\file client.cpp
\brief achieve two terninal communication
\for Liunx
\chip none
\Date 2024-08-22
\version v0.0.1
*********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/select.h>
#include "PrintLevel.h"
#define BUFFER_SIZE (1024)
#define MAX_CLIENTS (2)
#define INITSOCKETE (-1)
#define MYPORT (2222)
#define IP ("127.0.0.1")
typedef unsigned int u32;
typedef unsigned short u16;
typedef char u8;
typedef int s32;
typedef enum _EN_ERRO_RETURN
{
EN_BIND_FAIL = -2,
EN_LISTENSOC_FAIL = -1,
EN_SUCCESS = 0
}EN_ERRO_RETURN;
s32 gPrintAPILevel = EN_PRINTAPI_MSG;
static s32 giServerSocket = INITSOCKETE;
static s32 giClientSockets[MAX_CLIENTS] = {0};
static s32 giClientCount = 0;
static pthread_mutex_t gClientMutex = PTHREAD_MUTEX_INITIALIZER;
volatile sig_atomic_t vsServerRunSig = 1;
/*!
@brisf:handle_signal
*@para[in] const u8 *message
*@return void:
*/
void HandleSignal(s32 iSig)
{
PRINTAPI_TRC("Enter HandleSignal API\n");
PRINTAPI_DBG("HandleSignal API iSig %d \n", iSig);
if (iSig == SIGINT)
{
PRINTAPI_ERR("\n Server interrupted by user (Ctrl+C). Exiting...\n");
vsServerRunSig = 0;
}
else if (iSig == SIGTSTP)
{
PRINTAPI_ERR("\n Server suspended by user (Ctrl+Z). Exiting...\n");
vsServerRunSig = 0;
}
PRINTAPI_TRC("Out HandleSignal API\n");
return;
}
/*!
@brisf:handle_clien
*@para[in] void *pArg
*@return void*:
*/
void *HandleClient(void *pArg)
{
PRINTAPI_TRC("Enter HandleClient API\n");
u8 cBuff[BUFFER_SIZE] = {0};
s32 iBytesRead = 0;
s32 iClientSocket = 0;
if(nullptr == pArg)
{
PRINTAPI_ERR("HandleClient pArg is nullpointer\n");
return NULL;
}
iClientSocket = *(s32 *)pArg;
PRINTAPI_DBG("HandleClient API iClientSocket is %d \n", iClientSocket);
while(vsServerRunSig)
{
iBytesRead = recv(iClientSocket, cBuff, BUFFER_SIZE - 1, 0);
if(iBytesRead < 0)
{
PRINTAPI_ERR("HandleClient recv fail\n");
break;
}
else if(0 == iBytesRead)
{
PRINTAPI_ERR("Client id %d disconnected\n",iClientSocket);
break;
}
cBuff[iBytesRead] = '\0';
PRINTAPI_MSG("%s\n", cBuff);
//Brocast news to other clients
pthread_mutex_lock(&gClientMutex);
for(s32 i = 0; i < giClientCount; i++)
{
if (giClientSockets[i] != iClientSocket)
{
if (send(giClientSockets[i], cBuff, strlen(cBuff), 0) < 0)
{
PRINTAPI_ERR("HandleClient API send fail");
}
}
}
pthread_mutex_unlock(&gClientMutex);
}
//Remove client from clientlist
pthread_mutex_lock(&gClientMutex);
for(s32 i = 0; i < giClientCount; i++)
{
if(giClientSockets[i] == iClientSocket)
{
for(s32 j = i; j < giClientCount - 1; j++)
{
giClientSockets[j] = giClientSockets[j + 1];
}
giClientCount--;
pthread_mutex_unlock(&gClientMutex);
break;
}
}
pthread_mutex_unlock(&gClientMutex);
if(iClientSocket != INITSOCKETE)
{
close(iClientSocket);
iClientSocket = INITSOCKETE;
}
pthread_exit(NULL);
PRINTAPI_TRC("Out HandleClient API\n");
return NULL;
}
/*!
@brisf:main
*@para[in]
*@return s32:
*\retval -2 If EN_BIND_FAIL
*\retval -1 If EN_LISTENSOC_FAIL
*\retval 0 If EN_SUCCESS.
*/
s32 main()
{
PRINTAPI_TRC("Enter main API\n");
//Create server socket
EN_ERRO_RETURN enRet = EN_SUCCESS;
giServerSocket = socket(AF_INET, SOCK_STREAM, 0);
s32 iClientSocket = INITSOCKETE;
s32 iThreadCount = 0;
pthread_t tidClient[MAX_CLIENTS] = {0};
//Set server address
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;//Use IPV4 address
server_addr.sin_addr.s_addr = inet_addr(IP);//IP
server_addr.sin_port = htons(MYPORT); //Port
//Register signal handler
struct sigaction sa;
sa.sa_handler = HandleSignal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
//Bbind socket to addr
if((bind(giServerSocket, (struct sockaddr *)&server_addr, sizeof(server_addr))) < 0)
{
PRINTAPI_ERR("Server socket bind fail\n");
enRet = EN_BIND_FAIL;
PRINTAPI_DBG("enRet = %d\n", enRet);
goto FREE_END;
}
//Listen connect request
if((listen(giServerSocket, MAX_CLIENTS)) < 0)
{
PRINTAPI_ERR("Server socket listen fail\n");
enRet = EN_LISTENSOC_FAIL;
PRINTAPI_DBG("enRet = %d\n", enRet);
goto FREE_END;
}
PRINTAPI_MSG("Server listening on port %d\n", MYPORT);
//Loop to accpet client connect
while(vsServerRunSig)
{
//Recieive client connect request
iClientSocket = accept(giServerSocket, NULL, NULL);
if(iClientSocket < 0)
{
PRINTAPI_ERR("Server socket accept client fail\n");
continue;;//Enter next loop
}
//Add new client to list
pthread_mutex_lock(&gClientMutex);
if(giClientCount < MAX_CLIENTS)
{
giClientSockets[giClientCount++] = iClientSocket;
PRINTAPI_ERR("New client id :% d connected\n", iClientSocket);
}
else
{
PRINTAPI_ERR("Maximum number of clients reached\n");
pthread_mutex_unlock(&gClientMutex);
if(iClientSocket != INITSOCKETE)
{
close(iClientSocket);
iClientSocket = INITSOCKETE;
}
continue;
}
pthread_mutex_unlock(&gClientMutex);
//Creat pthread of client connect
if((pthread_create(&tidClient[iThreadCount++], NULL, HandleClient, &iClientSocket)) != 0)
{
PRINTAPI_ERR("HandleClient pthread create fail\n");
if(iClientSocket != INITSOCKETE)
{
close(iClientSocket);
iClientSocket = INITSOCKETE;
}
continue;
}
}
for(int i = 0; i < iThreadCount; i++)
{
if((pthread_join(tidClient[i], NULL)) != 0)
{
PRINTAPI_ERR("Thread %d join fail\n", i+1);
}
}
FREE_END:
if(giServerSocket != INITSOCKETE)
{
close(giServerSocket);
giServerSocket = INITSOCKETE;
}
PRINTAPI_TRC("Out main API, enRet = %d \n", enRet);
return enRet;
}