Sabya
Sabya

Reputation: 11894

socket "read" hanging if the MacBook sleeps more than 10 minutes

I am writing an app, where a socket is connecting to a host and downloading a file. The application runs in Mac.

Now, while the app is downloading, if I put the MacBook in sleep mode for more than 10 minutes, 60% of the time the app hangs when the computer wakes up.

The stack trace shows that, it has hanged in the "read" call. I am able to reproduce this with a sample program also. Below, I have pasted the code of the sample program and the stack where it is hanging. How to solve this hanging?

Also, this is not just TCP/IP waiting that will come out in few minutes. I have waited for more than 12 hours, it did not come out.

The stack trace: -

Call graph:
    2466 Thread_2507
      2466 start
        2466 read$UNIX2003
          2466 read$UNIX2003

The program :-

#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>

#define buflen 131072
unsigned int portno = 80;
char hostname[] = "192.168.1.9";

int main() 
{
    int sd = socket(AF_INET, SOCK_STREAM, 0);  /* init socket descriptor */    
    struct sockaddr_in sin;
    struct hostent *host = gethostbyname(hostname);
    char buf[buflen];
    int len;
    int ret;
    FILE *fp;
    int i;

    if(sd == -1){
        printf("Could not create client socket\n");
        return 1;
    }

    /*set keep alive*/
    int optval = 1;
 int optlen = sizeof(optval);
    ret = setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
    if(ret != 0){
        printf("could not set socket option.\n");
        return 1;
    }

    /*** PLACE DATA IN sockaddr_in struct ***/
    memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
    sin.sin_family = AF_INET;
    sin.sin_port = htons(portno);

    /*** CONNECT SOCKET TO THE SERVICE DESCRIBED BY sockaddr_in struct ***/
    if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        perror("connecting");
        return 1;
    }

    char *str = "GET /general-log.exe / HTTP/1.0\n\n";

    ret = write(sd, str, strlen(str));
    if(ret < 0){
        printf("error while writing\n");
        return 1;
    }


    fp = fopen("downloaded.file", "wb+");
    if(fp == NULL){
        printf("not able to open the file.\n");
        return 1;
    }

    i = 0;
    while ((len = read(sd, buf, buflen)) > 0) {        
        printf("%d\t%d\n", i++, len);
        fwrite(buf, len, 1, fp); //we should check for return
    }
    if(len < 0){
        printf("Error while reading\n");
    }

    fclose(fp);   
    close(sd);
    return 0;
}

Update apparently the SO_RCVTIMEOUT is solving the problem.

struct timeval tv;
tv.tv_sec=10;
tv.tv_usec=0;
setsockopt ( m_sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof ( tv ) );

Is it okay to use SO_RCVTIMEO?

Upvotes: 1

Views: 530

Answers (2)

Sabya
Sabya

Reputation: 11894

I couldn't solve it using blocking sockets. I had to change the IMAP library to non-blocking sockets.

Upvotes: 0

Aaron Digulla
Aaron Digulla

Reputation: 328754

TCP/IP connections don't survive sleep mode. SO_KEEPALIVE doesn't help in this case since it has no effect on the server side. Just wait two minutes and the read will time out. After the timeout, you can connect again.

And that sleep(1) is unnecessary. The server will respond as soon as the data is available. If you don't fetch is right away, you'll allocate a connection on the server for longer than you need.

Upvotes: 1

Related Questions