Question

I 'm writing a C++ client program quering a postgreSQL database via the internet. I want to handle the event when while waiting for an answer, a network connection problem occurs and so the client can't receive any messages from the database server. But when I manually turn off the internet connection the program remains idle even if I turn the connection on again later. Is there a way to catch this event or at least set a timeout on client-side so that it stops waiting for an answer after it?

Was it helpful?

Solution 2

After searching around following private data public channel 2 suggestion to use socket operations I've reached to the following solution:

pqxx::connection conn("host=blabla.com "
                    "port=5432 "
                    "user=abla "
                    "password=abla "
                    "dbname=ablabla");


        const int val1 = 1;

        if ( setsockopt(conn.sock(), SOL_SOCKET, SO_KEEPALIVE, &val1, sizeof(val1)) < 0){
            perror( "setsockopt failed\n");
        }

        const int val2 = 5;

        if ( setsockopt(conn.sock(), IPPROTO_TCP, TCP_KEEPCNT, &val2, sizeof(val2)) < 0){
            perror( "setsockopt failed\n");
        }
        const int val3 = 2;

        if ( setsockopt(conn.sock(), IPPROTO_TCP, TCP_KEEPINTVL, &val3, sizeof(val3)) < 0){
            perror( "setsockopt failed\n");
        }
        const int val4 = 10;
        if ( setsockopt(conn.sock(), IPPROTO_TCP, TCP_KEEPIDLE, &val4, sizeof(val4)) < 0){
            perror( "setsockopt failed\n");
        }

        TransactorImpl transac();
        conn.perform(transac,10);

This way 10 seconds after I disconnect from the network the socket starts sending probes to ensure the connection is OK and after a while it aborts the connection and my overriden on_abort() function is called and after that libpqxx retries to execute the query. The number of retries is specified by the second argument of the perform() function (in this case 10).

OTHER TIPS

You can set a timeout for any socket operation (different from connect) using timeval structure and call to setsockopt.

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

if ( setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
            sizeof(timeout)) < 0)
    error( "setsockopt failed\n");

SO_RCVTIMEO option is used to indicate input operation.

You can also make a select calls to non blocking socket with a timeout parameter:

const int timeout_msecs = 5000;
struct timeval tval;
tval.tv_usec = 1000 * (timeout_msecs % 1000);
tval.tv_sec = timeout_msecs / 1000;

fd_set waitSet;
FD_ZERO( &waitSet );
FD_SET( fd, &waitSet );

int ret;
ret = select( fd + 1, &waitSet, NULL, NULL, &tval );

Finally, the technique showed by Richard Stevens in his "Unix Networking Programming" involves usage of system interrupts:

static void
sig_alrm(int signo)
{
    return;         /* just interrupt the recvfrom() */
}

void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
    int n;
    char    sendline[MAXLINE], recvline[MAXLINE + 1];

    Signal(SIGALRM, sig_alrm);

    while (Fgets(sendline, MAXLINE, fp) != NULL) {

        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

        alarm(5);
        // call recvfrom with 5 seconds timeout
        if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
            if (errno == EINTR)
                fprintf(stderr, "socket timeout\n");
            else
                err_sys("recvfrom error");
        } else {
            alarm(0);
            recvline[n] = 0;    /* null terminate */
            Fputs(recvline, stdout);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top