0

毎秒数のリクエストを処理するプロセスを作成しようとしています。リクエストごとに新しいスレッドが作成されます。次に、各スレッドはアドレス (http ポート) へのソケット接続を開き、HEAD 要求を送信し、応答を取得してソケットを閉じます。
私が抱えている問題は、1秒あたり3つ以上のリクエストを送信したときに発生し、しばらくすると関数の send() 部分でエラーが発生し、接続が拒否され続けます。1 秒あたりにより多くのリクエストを入力すると、エラーが早く発生します。1 秒あたり 2 つのリクエストのみを送信すると、エラーはまったく発生しません。リソースが不足していると思われますが、どれが見つかりません。

ここにコードの基本構造があります

//declarations

socketfd = socket(servinfo->ai_family,servinfo->ai_socktype,servinfo->ai_protocol);

arg = fcntl(socketfd, F_GETFL, NULL)) < 0);
arg |= O_NONBLOCK;
fcntl(socketfd, F_SETFL, arg)

if((conn = connect(socketfd, servinfo->ai_addr, servinfo->ai_addrlen)) < 0)
{
    if(errno == EINPROGRESS)
    {
        do
        {
            tv.tv_sec = CONNECT_TIMEOUT;
            tv.tv_usec = 0;
            FD_ZERO(&myset);
            FD_SET(socketfd, &myset);
            if((res = select(socketfd+1, NULL, &myset, NULL, &tv) > 0)) 
            {
                if( (arg = fcntl(socketfd, F_GETFL, NULL)) < 0) { 
                    perror("fcntl get 2");
                } 
                arg &= (~O_NONBLOCK); 
                if( fcntl(socketfd, F_SETFL, arg) < 0) {
                    perror("fcntl set 2");
                }
                char szBuf[4096];

                std::string htmlreq = "HEAD / HTTP/1.1\r\nHost:";
                htmlreq += info->hostName;
                htmlreq += "\r\n\r\n";

                if((conn = send(socketfd,htmlreq.c_str(),htmlreq.size(),0)) == -1 && errno != EINTR)
                {
                    perror("send");
                    close(socketfd);
                    return;
                }

                if((conn = recv(socketfd,szBuf,sizeof(szBuf)+1,0)) < 0 && errno != EINTR)
                {
                    perror("recv");
                    close(socketfd);
                    return ;
                }

                close(socketfd);

                // do stuff with data
                break;
            }
            else
            {
                //timeout
                break;
            }
        }while(1);
    }
    else
    {
        perror("connect");
        close(socketfd);
        return; 
    }
}

最初からいくつかのエラーチェックを削除しました。出力として得られるのは、しばらくすると「送信:接続が拒否されました」です。どの部分が問題を引き起こしている可能性があるかについてのいくつかの指針をいただければ幸いです。プラットフォームはubuntu Linuxです。必要に応じて、コードの他の部分も投稿できれば幸いです。事前にTnx。

4

2 に答える 2

1

おそらく不足しているリソースは、接続しているサーバーにあります。次のいずれかの理由により、接続先のコンピューターによって接続が拒否されています。

  1. 1 秒あたりの接続数を制限するように構成する (いくつかの基準に基づく)
  2. または、接続先のサーバーに何らかの理由で負荷がかかりすぎて、それ以上接続できません。

3 回目の接続で常にエラーが発生するため、接続しているサーバーが IP ベースで接続数を制限している可能性があります。

編集1

ノンブロッキング接続をしようとしていますか? よく見ると、選択に問題があるように思えます。select では、ソケットが実際に接続される前に読み取り可能であることを返し、send を呼び出しています。ノンブロッキング接続で注意すべきことの 1 つは、ソケットがエラー時に読み取り可能および書き込み可能になることです。つまり、選択が返された後に両方をチェックする必要があることを意味します。そうしないと、実際のエラーが何であれ、代わりに送信エラーが表示される可能性があります。

これは、スティーブンス UNP からのものです。

FD_ZERO(&rset);
FD_SET(sockfd, &rset);
wset = rset;
tval.tv_sec = nsec;
tval.tv_usec = 0;

if ( (n = Select(sockfd+1, &rset, &wset, NULL,
                 nsec ? &tval : NULL)) == 0) {
    close(sockfd);      /* timeout */
    errno = ETIMEDOUT;
    return(-1);
}

if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
    len = sizeof(error);
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
        return(-1);         /* Solaris pending error */
} else
    err_quit("select error: sockfd not set");

done:
Fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */

if (error) {
    close(sockfd);      /* just in case */
    errno = error;
    return(-1);
}
return(0);
于 2009-05-11T10:37:20.550 に答える
0

あなたのコードにはかなりの問題があります。

まず、ソケットをノンブロッキングに設定します。なぜあなたがこれをするのか理解できません。接続機能には内部タイムアウトがあるため、ブロックされません。

コードのもう 1 つの問題は、接続がすぐに成功した場合、最初の if ステートメントが命令ブロックをスキップすることです。これは起こるかもしれません。

どうやら最初に HEAD メッセージを送信したいようです。リモートサーバーまたはネットワークが非常に遅いことが予想され、タイムアウトが必要でない限り、これを非ブロックにする必要はありません。この場合、ブロックしないソケットを使用した選択が意味をなします。

HEAD メッセージを送信すると、recv 関数を使用して収集した応答データが期待されます。この関数呼び出しは、送信されたデータ全体が受信される前に戻る可能性があることに注意してください。送信されたすべてのデータが受信されたことを確認するには、独立した方法が必要です。サーバーは接続を閉じますか? これは、0 を返す recv 関数によって検出されます。

したがって、recv は、受信したデータをバッファまたはファイルに追加し、recv が 0 を返したときに終了するループにラップする必要があります。実際にブロックする可能性があるこの recv 操作にタイムアウトを追加する場合は、非ブロック ソケットを使用します。

ただし、最初にタイムアウトなしで試して、現在のバージョンのようにブロックすることなくフルスピードで動作することを確認してください.

名前と IP アドレスの解決のために最初の接続が遅くなり、データがキャッシュされるため、後続の呼び出しで速くなると思われます。

于 2009-05-11T14:57:18.110 に答える