4

インターネット経由で postgreSQL データベースにクエリを実行する C++ クライアント プログラムを作成しています。応答を待っている間にネットワーク接続の問題が発生し、クライアントがデータベース サーバーからメッセージを受信できない場合に、イベントを処理したいと考えています。しかし、インターネット接続を手動でオフにすると、後で接続をオンにしても、プログラムはアイドル状態のままになります。このイベントをキャッチする方法、または少なくともクライアント側でタイムアウトを設定して、その後の応答の待機を停止する方法はありますか?

4

2 に答える 2

3

timeval構造体と への呼び出しを使用して、任意のソケット操作 (接続とは異なる) のタイムアウトを設定できます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オプションは、入力操作を示すために使用されます。

selectタイムアウト パラメータを使用して、非ブロッキング ソケットを呼び出すこともできます。

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 );

最後に、リチャード・スティーブンスが彼の「Unix Networking Programming」で示したテクニックには、システム割り込みの使用が含まれます。

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);
        }
    }
}
于 2014-05-06T10:37:28.667 に答える
0

ソケット操作を使用するためのプライベート データ パブリック チャネル 2 の提案に従って検索した後、次の解決策にたどり着きました。

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);

このようにして、ネットワークから切断してから 10 秒後に、ソケットがプローブの送信を開始して接続が正常であることを確認し、しばらくすると接続が中止され、オーバーライドさon_abort()れた関数が呼び出され、その後 libpqxx がクエリの実行を再試行します。再試行の回数は、perform()関数の 2 番目の引数 (この場合は 10) によって指定されます。

于 2014-05-06T14:37:54.293 に答える