1
votes

Trying to write a client which will try to receive data till 3 seconds. I have implemented the connect method using select by below code.

//socket creation 
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

m_stAddress.sin_family           = AF_INET;
  m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchIP);
m_stAddress.sin_port             = htons(iPort);

m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
m_stTimeout.tv_usec = 0;

//connecting to server
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);

connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));

long iMode = 0;
iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);

fd_set stWrite;
FD_ZERO(&stWrite);
FD_SET(m_hSocket, &stWrite);

iResult = select(0, NULL, &stWrite, NULL, &m_stTimeout);            

if((iResult > 0) && (FD_ISSET(m_hSocket, &stWrite)))
  return true;

But I cannot figure out what I am missing at receiving timeout with below code? It doesn't wait if the server connection got disconnected. It just returns instantly from select method. Also how can I write a non blocking socket call with timeout for socket send.

long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);

fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);

int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);

if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
  while ((iBuffLen-1) > 0)
  {
    int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
    if (iRcvLen == SOCKET_ERROR)
    {
      return false;
    }
    else if (iRcvLen == 0)
    {
      break;
    }

    pchBuff  += iRcvLen;
    iBuffLen -= iRcvLen;
  }
}    
2
You have not presented the code by which you create the socket, so I don't know what kind it is. Inasmuch as you seem to think that there is a meaningful sense in which it can be "disconnected", however, it must be some type of stream socket. In that case, why should your program continue to try to read from it after a disconnection? No data will ever be forthcoming until the socket is reconnected, and that doesn't happen automatically.John Bollinger
I think I am exiting the receive loop once I get a SOCKET_ERROR and thats the wrong. As for a single read it might return SOCKET_ERROR and evetually for next reads it might report a success, so I guess here I need to check for time elapsed. Is it so?hypheni
Use write and error sets: select(<MAXSOCK+1>, <read-set>, <write-set>, <error-set>, timeout);fukanchik

2 Answers

1
votes

The first parameter to select should not be 0. Correct usage of select can be found here : http://developerweb.net/viewtopic.php?id=2933

the first parameter should be the max value of your socket +1 and take interrupted system calls into account if it is non blocking:

/* Call select() */
do {
   FD_ZERO(&readset);
   FD_SET(socket_fd, &readset);
   result = select(socket_fd + 1, &readset, NULL, NULL, NULL);
} while (result == -1 && errno == EINTR);

This is just example code you probably need the timeout parameter as well. If you can get EINTR this will complicate your required logic, because if you get EINTR you have to do the same call again, but with the remaining time to wait for.

0
votes

I think for non blocking mode one needs to check the recv() failure along with a timeout value. That mean first select() will return whether the socket is ready to receive data or not. If yes it will go forward else it will sleep until timeout elapses on the select() method call line. But if the receive fails due to some uncertain situations while inside read loop there we need to manually check for socket error and maximum timeout value. If the socket error continues and timeout elapses we need to break it.

I'm done with my receive timeout logic with non blocking mode. Please correct me if I am wrong.

  bool bReturn = true;
  SetNonBlockingMode(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);

  DWORD dwStartTime = GetTickCount();
  DWORD dwCurrentTime = 0;

  //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)))
  {
    while ((iBuffLen-1) > 0)
    {
      int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
      dwCurrentTime = GetTickCount();

      if ((iRcvLen == SOCKET_ERROR) && ((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000))
      {
        bReturn = false;
        break;
      }
      else if (iRcvLen == 0)
      {
        break;
      }

      pchBuff  += iRcvLen;
      iBuffLen -= iRcvLen;
    }
  }

  SetNonBlockingMode(false);
  return bReturn;