Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting time out for connect() function tcp socket programming in C breaks recv()

Tags:

c

tcp

In my program If the server is not reachable the connect function take too much time. So i try to give time out to connect using select(). Now the problem is that when i try to receive data from server using recvfrom() i got error "EAGAIN". here is code used to connect and receive data from server.

int sock;
struct sockaddr_in addr;
int connectWithServer
{

    int status;

    struct timeval  timeout;
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    addr.sin_port = htons(port);
    sock = socket (AF_INET,SOCK_STREAM,0);
    inet_pton(AF_INET,serverIP,&addr.sin_addr);

    fd_set set;
    FD_ZERO(&set);
    FD_SET(sock, &set);

    fcntl(sock, F_SETFL, O_NONBLOCK);

    if ( (status = connect(sock, (struct sockaddr*)&addr, sizeof(addr))) == -1)
    {
        if ( errno != EINPROGRESS )
            return status;

    }
    status = select(sock+1, NULL, &set, NULL, &timeout);

    return status;
}


long int receiveResponse (void *response , unsigned int length)
{
    socklen_t sockLen = sizeof(struct sockaddr);
    long int received = recvfrom(sock, response, length, 0,(struct sockaddr *)&addr,  &sockLen);
    printf("Received %ld bytes...  err %d\n",received, errno);

    return received;
}
like image 538
Rajesh Avatar asked Jan 10 '13 08:01

Rajesh


4 Answers

Setting time out for connect() function tcp socket programming in C is not working

Correction. Setting the connect timeout is working. What 'isn't working' is the subsequent recvfrom(), and that's because you left the socket in non-blocking mode and you don't know what to do with the resulting EAGAIN. So, either handle that, by using select() to tell you when the socket is ready to read, or else put the socket back into blocking mode after finishing the connect.

like image 103
user207421 Avatar answered Oct 17 '22 02:10

user207421


The first successful select means the connect operation is complete but does not necessarily mean it succeed, from connect man page, you should check SO_ERROR to make sure it completed successfully

It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).

So in your code you should do something like this:

int ret;
ret=select(sockfd+1, NULL, &wfds, NULL, NULL); //should use timeout
if(ret==1 && getSocketOpt(sockfd, SO_ERROR) ==0) {
    return 0; //successfully connected
}

Then, as mentioned in the other answer you should call select again before writing or reading from the socket.

like image 43
iabdalkader Avatar answered Oct 17 '22 01:10

iabdalkader


You receive EAGAIN because there's no data to read from socket buffer and your socket was set as nonblocking. Since you're not connected with the peer, i'm not surprised with it.

Look at this from man recvfrom:

If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno set to EAGAIN. The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

Another case could be the following:

  • Your socket may be connected but you're too fast checking if something is received. To avoid this, put another select before recvfrom in order to extract the packet from the socket buffer (calling readfrom or just read) only when your're sure you received something.
like image 5
Davide Berra Avatar answered Oct 17 '22 01:10

Davide Berra


The socket should be set to blocking mode again before calling recv().

fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) & ~O_NONBLOCK);
like image 5
jungwook Avatar answered Oct 17 '22 01:10

jungwook