我正在编写一个C++客户端应用程序,它将向服务器发送一些数据并等待其响应。现在协议是等待特定的超时,然后重新尝试特定的时间。如果一切出错,客户端将报告通信失败。
我在一个非阻塞套接字操作中实现了整个过程。我对我的发送/接收方法是否正确有一些疑问。
下面是我在Windows平台上用VC++ 2005编写的TCP通信代码。
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;
}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;
}重试
的重新启动连接
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;
}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;
}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;
}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;
}发布于 2015-04-29 20:09:54
首先,不应将值与if语句中的布尔文本进行比较。这最好写成bSuccess,而不是bSuccess == true和!bSuccess,而不是bSuccess == false。
其次,您正在这里修改一个局部变量,一旦离开该方法,它就会超出范围:
else
{
WRITELOG("Call to socket API 'select' failed inside recv method, error: %d", WSAGetLastError());
bSuccess = false;
break;
}在此之后,您就脱离了循环和return bSuccess;。这可以用来显示您正在立即返回这样的故障信号:
else
{
WRITELOG("Call to socket API 'select' failed inside recv method, error: %d", WSAGetLastError());
return false;
}第三,你有这样的片段:
//decide success or failure
if((iRet > 0) && (FD_ISSET(m_hSocket, &fdWrite)))
{
return true;
}
return false;这可以在没有ifs的情况下写成:
return (iRet > 0) && FD_ISSET(m_hSocket, &fdWrite);第一个语句周围的括号是不必要的,但可能有助于可读性。
发布于 2015-04-29 21:19:56
仅测试套接字是否可写是不够的。反过来说,这只意味着send不会阻塞,而在失败的connect之后,它确实会立即返回( errno设置为ENOTCONN)。测试成功的一种标准方法是使用
int error;
int len = sizeof(error);
getsockopt(n_hSocket, SOL_SOCKET, SO_ERROR, &error, &len);如果error为0,则连接成功。
https://codereview.stackexchange.com/questions/88396
复制相似问题