在开发可以交换某些数据的示例客户端服务器应用程序之后,我正在尝试在其中实现重试机制。目前我的申请是遵循以下协议:
对于上面的实现代码部分看起来像下面这样:
现在我正在根据套接字函数的返回类型进行重试,如果send()或recv()失败,我正在重试相同的方法。但不记得connect()。
我通过在数据传输之间重新启动服务器测试了这个东西,结果客户端无法与服务器通信并且在几次重试后退出,我相信这是因为没有connect()调用重试方法。
有什么建议吗?
接收套接字数据的示例代码
bool CTCPCommunication::ReceiveSocketData(char* pchBuff, int iBuffLen)
{
bool bReturn = true;
//check whether the socket is ready to receive
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
//if socket is not ready this line will be hit after 3 sec timeout and go to the end
//if it is ready control will go inside the read loop and reads data until data ends or
//socket error is getting triggered continuously for more than 3 secs.
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
DWORD dwStartTime = GetTickCount();
DWORD dwCurrentTime = 0;
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
dwCurrentTime = GetTickCount();
//receive failed due to socket error
if (iRcvLen == SOCKET_ERROR)
{
if((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000)
{
WRITELOG("Call to socket API 'recv' failed after 3 secs continuous retries, error: %d", WSAGetLastError());
bReturn = false;
break;
}
}
//connection closed by remote host
else if (iRcvLen == 0)
{
WRITELOG("recv() returned zero - time to do something: %d", WSAGetLastError());
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
else
{
WRITELOG("Call to API 'select' failed inside 'ReceiveSocketData', error: %d", WSAGetLastError());
bReturn = false;
}
return bReturn;
}
答案 0 :(得分:1)
目前我的申请符合以下协议:
- 客户端连接到服务器(非阻塞模式),超时3秒,重试次数为2次。
醇>
您无法重试连接。您必须关闭连接尝试失败的套接字,创建一个新套接字,然后再次调用connect()
。
- 开始从客户端发送固定长度的数据。发送有一些错误,检查是否正在发送完整数据。
醇>
在阻止模式下这不是必需的:POSIX标准保证阻塞模式send()
将发送所有数据,或者因错误而失败。
- 从服务器接收响应(超时:3秒)并验证。如果收到错误响应,请重新发送数据并等待响应。如果失败,重复两次。
醇>
这是一个坏主意。很可能所有数据都将到达,包括所有重试,或者没有。如果您使用此技术,则需要确保您的交易是 idempotent 。您还需要密切关注实际的超时时间。一般来说3秒是不够的。起点是预期服务时间的两倍。
对于上面的实现代码部分看起来像下面这样:
connect() and select() for opening connection select() and send() for data send select() and recv() for data receiving
在阻止模式下,您不需要select()
。您可以使用SO_RCVTIMEO.
现在我根据套接字函数的返回类型进行重试,如果send()或recv()失败,我会重试相同的方法。但不记得connect()。
我通过在数据传输之间重新启动服务器测试了这个东西,结果客户端无法与服务器通信并且在几次重试后退出,我相信这是因为没有connect()调用重试方法。
如果这是真的,你会得到一个错误。