【发布时间】:2015-07-07 02:18:23
【问题描述】:
在开发了一个可以交换一些数据的示例客户端服务器应用程序之后,我正在尝试在其中实现重试机制。目前我的应用程序遵循以下协议:
- 客户端连接到服务器(非阻塞模式),超时时间为 3 秒,重复次数为 2。
- 开始从客户端发送固定长度的数据。 Send 有一些错误检查它是否正在发送完整的数据。
- 从服务器接收响应(超时:3 秒)并验证。如果收到不正确的响应,则重新发送数据并等待响应。如果失败,重复此操作两次。
对于上面的实现代码部分如下所示:
- connect() 和 select() 用于打开连接
- select() 和 send() 用于数据发送
- select() 和 recv() 用于数据接收
现在我正在根据套接字函数的返回类型进行重试,如果 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;
}
【问题讨论】:
-
您需要检查 fatal 错误,如果有任何错误,您需要
close连接并创建一个新连接。您不能继续在死连接上发送。 (你也不能select连接死机——没有什么可等待的。) -
@DavidSchwartz:你能告诉我更多关于致命错误的信息吗?你在说哪个 SOCKET_ERROR 代码?我不清楚,何时使用 send/recv 重试以及何时使用 connect() 重试,即从头开始构建套接字。
-
您应该重试
send或recv的唯一情况是它是否被信号中断或被阻塞。所有其他错误对连接都是致命的。 -
对于非阻塞套接字,是否可以调用 select 并检查读/写能力,然后启动 recv/send 并检查 SOCKET_ERROR。如果找到,只需重新连接新的套接字。
-
不,有两个原因。首先,您可能会被信号打断。其次,点击
select并不能保证后续操作不会因EWOULDBLOCK而失败。举一个明显的例子,假设你得到一个写入命中,然后尝试写入 64MB。