views:

95

answers:

2

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?

+1  A: 

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.

Aaron Digulla
thanks. updated my question.
Sabya
SO_KEEPALIVE doesn't help in this case since it has no effect on the server side.
Aaron Digulla
I have waited more than 12 hours, the program did not come out.
Sabya
What happens if you remove the SO_KEEPALIVE?
Aaron Digulla
Still hangs. Actually I added SO_KEEPALIVE later for testing only.
Sabya
And you put the mac to sleep while it downloads?
Aaron Digulla
Yes. I put it to sleep while the downloading is going on.
Sabya
A: 

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

Sabya