Well I'm wrting a C++ client application which will send some data to server and wait for its response. Now the protocol is to wait for specific timeout and then retry for specific times. If all goes wrong client will report a failure communication.
I have implemented the whole matter in a non blocking socket operation. I have some doubts on my send/receive methods whether my approach is correct or not.
Below is my code for tcp communication which is written in VC++ 2005 on Windows platform.
//Socket parameters building method
bool CTCPCommunication::OpenConnection(bool bRetryConnect)
{
//create the socket handle and config its paramaters
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_hSocket == INVALID_SOCKET)
{
WRITELOG("Call to API 'socket' failed, error: %d", WSAGetLastError());
return false;
}
//setting socket address
CT2CA serverIP(m_csServerIP);
char* pchServerIP = serverIP;
m_stAddress.sin_family = AF_INET;
m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchServerIP);
m_stAddress.sin_port = htons(m_iServerPort);
//setting socket timeout
m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
m_stTimeout.tv_usec = 0;
//set socket to non blocking mode
unsigned long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
if (iResult != NO_ERROR)
{
WRITELOG("Call to API 'ioctlsocket' failed, error: %d", WSAGetLastError());
return false;
}
bool bSuccess = false;
//Called for the first time when starting server connection
if (bRetryConnect == false)
{
bSuccess = InitialConnect();
}
//For all the other time when client detects a failure in communication and makes a retry
else
{
bSuccess = Connect();
}
return bSuccess;
}
//Connection building method
bool CTCPCommunication::Connect()
{
ReportStatus(App_Stat_Connect);
CT2CA serverIP(m_csServerIP);
char* pchserverIP = serverIP;
WRITELOG("Connecting to server %s:%d", pchserverIP, m_iServerPort);
//try to connect server
connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));
//check and wait for the socket to be ready with timeout
fd_set fdWrite;
FD_ZERO(&fdWrite);
FD_SET(m_hSocket, &fdWrite);
int iRet = select(0, NULL, &fdWrite, NULL, &m_stTimeout);
//decide success or failure
if((iRet > 0) && (FD_ISSET(m_hSocket, &fdWrite)))
{
return true;
}
return false;
}
//Reinitiate connection for a retry
bool CTCPCommunication::RetryConnection()
{
bool bSuccess = CloseConnection();
if (bSuccess == false)
{
ReportStatus(App_Err_Retry);
WRITELOG("Unabled to attempt retry as existing connection could not be closed, error: %d", WSAGetLastError());
return bSuccess;
}
bSuccess = OpenConnection(true);
return bSuccess;
}
//Upload data to server
bool CTCPCommunication::UploadDataPacket(char* pchSendData, int iSendDataLen, MessageID eSendMessageID, CString csSendPacketGUID)
{
bool bSuccess = false;
m_iRetryCount = 0;
while (m_iRetryCount <= MAX_RETRY)
{
// Pushing data packet to socket
bSuccess = SendSocketData(pchSendData, iSendDataLen);
if (bSuccess == true)
{
// Receive data from socket
char chRecvBuff[MAX_RECV_LEN+1] = {0};
bSuccess = ReceiveSocketData(chRecvBuff, MAX_RECV_LEN+1);
// Verify response packet for proper GUID
if (bSuccess == true)
{
CString csRecvBuff = CString(chRecvBuff);
bSuccess = ValidateACK(eSendMessageID, csRecvBuff, csSendPacketGUID);
if (bSuccess == true)
{
break;
}
}
}
if (bSuccess == false)
{
RetryConnection();
m_iRetryCount++;
if(m_iRetryCount <= MAX_RETRY)
{
ReportStatus(App_Stat_Retry, m_iRetryCount);
WRITELOG("Attempting retry %d", m_iRetryCount);
}
}
}
return bSuccess;
}
//Send socket data
bool CTCPCommunication::SendSocketData(char* pchData, int iBuffLen)
{
bool bSuccess = true;
while (iBuffLen > 0)
{
//check whether the socket is ready to write data
fd_set fdWrite;
FD_ZERO(&fdWrite);
FD_SET(m_hSocket, &fdWrite);
int iRet = select(0, NULL, &fdWrite, NULL, &m_stTimeout);
if ((iRet > 0) && (FD_ISSET(m_hSocket, &fdWrite)))
{
int iSentLen = send(m_hSocket, pchData, iBuffLen, 0);
//sending failed due to socket error
if (iSentLen == SOCKET_ERROR)
{
WRITELOG("Call to socket API 'send' failed, error: %d", WSAGetLastError());
bSuccess = false;
break;
}
pchData += iSentLen;
iBuffLen -= iSentLen;
}
else
{
WRITELOG("Call to socket API 'select' failed inside send method, error: %d", WSAGetLastError());
bSuccess = false;
break;
}
}
return bSuccess;
}
//Receive socket data
bool CTCPCommunication::ReceiveSocketData(char* pchBuff, int iBuffLen)
{
bool bSuccess = true;
while ((iBuffLen-1) > 0)
{
//check whether the socket is ready to read data
fd_set fdRead;
FD_ZERO(&fdRead);
FD_SET(m_hSocket, &fdRead);
int iRet = select(0, &fdRead, NULL, NULL, &m_stTimeout);
if ((iRet > 0) && (FD_ISSET(m_hSocket, &fdRead)))
{
int iRcvdLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
//receive failed due to socket error
if (iRcvdLen <= 0)
{
WRITELOG("Call to socket API 'recv' failed, error: %d", WSAGetLastError());
bSuccess = false;
break;
}
pchBuff += iRcvdLen;
iBuffLen -= iRcvdLen;
}
else
{
WRITELOG("Call to socket API 'select' failed inside recv method, error: %d", WSAGetLastError());
bSuccess = false;
break;
}
}
return bSuccess;
}