2

新しいクライアントとクライアントからのデータを受け入れるサーバー コードを作成しました。しかし問題は、クライアントからのデータがないにもかかわらず、select がタイムアウトまで待機しないことです。5 秒間待ってから、使用可能なクライアントにハートビートを送信します。ただし、最初の反復で 5 秒間待機し、次の反復で急速にハートビートを送信します。この問題を解決する方法。前もって感謝します。

void * Communicate(void * id)
{
int *iSockID = (int *) id;
int listener =  *iSockID;

fd_set master;    // master file descriptor list
fd_set read_fds;  // temp file descriptor list for select() read
fd_set write_fds; // temp file descriptor list for select() read
int fdmax;        // maximum file descriptor number

int i, j, rv;

FD_ZERO(&master);    // clear the master and temp sets
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
// add the listener to the master set
FD_SET(listener, &master);
printf("Listener is %d \n" , listener);

// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
//accept 3 clients


// main loop
for(;;) {
    read_fds = master; // copy it
    write_fds = master;
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv) ;
    if (iResult == -1) 
    {
        perror("select");
        exit(4);
    }

    for(i = 0; i <= fdmax; i++) 
    {           
        //send work for clients
        SendHeartBeats(write_fds , fdmax , listener , i );

    }

    // run through the existing connections looking for data to read
    // ADD NEW CONNECTIONS READ FROM CONNECTIONS    
    for(i = 0; i <= fdmax; i++)
    {   


        if (FD_ISSET(i, &read_fds)) 
        { // we got one!!
            // handle new connections               
            if (i == listener) 
            {                                    
                AcceptNewClients(master , fdmax , listener );   
            } else 
            {
                AccepeDataFromClients(i , master);
            } // END handle data from client
        } // END got new incoming connection
    } // END looping through file descriptors
    sleep(3);
} // END for(;;)
return 0;
}
4

3 に答える 3

1

read_fdwrite_fd変数が呼び出されるたびに再初期化するようにコードを変更する必要がありますselect()。これは、終了時にそれらを変更するため、毎回それらをリセットする必要があるためです。他の人が言ったように、=演算子を使用してmaster変数をコピーすることは、fd_set構造体をコピーする正しい方法ではありません。

これを試して:

void * Communicate(void * id)
{
    int *iSockID = (int *) id;
    int listener =  *iSockID;

    fd_set master;    // master file descriptor list
    fd_set read_fds;  // temp file descriptor list for select() read
    fd_set write_fds; // temp file descriptor list for select() read
    struct timeval tv;
    int fdmax;        // maximum file descriptor number
    int i, j, rv;

    printf("Listener is %d \n", listener);

    // add the listener to the master set
    FD_ZERO(&master);
    FD_SET(listener, &master);

    // keep track of the biggest file descriptor
    fdmax = listener; // so far, it's this one
    //accept 3 clients

    // main loop
    clock_t c1 = clock();
    while (1)
    {
        FD_ZERO(&read_fds);
        FD_ZERO(&write_fds);

        #ifdef MSWINDOWS
        // Windows does not have FD_COPY()
        for (u_int i = 0; i < master.fd_count; ++i)
        {
            FD_SET(master.fd_array[i], &read_fd);
            FD_SET(master.fd_array[i], &write_fd);
        }
        #else
        FD_COPY(&master, &read_fd);
        FD_COPY(&master, &write_fd);
        #endif

        tv.tv_sec = 1;
        tv.tv_usec = 0;

        int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv);
        if (iResult == -1) 
        {
            perror("select");
            exit(4);
        }

        clock_t c2 = clock();
        if (((c2-c1) / CLOCKS_PER_SEC) >= 5)
        {
            c1 = c2;
            for(i = 0; i <= fdmax; i++) 
            {           
                if ((i != listener) && FD_ISSET(i, &write_fds))
                { 
                    //send work for client
                    SendHeartBeat(i);
                }
            }
        }

        // run through the existing connections looking for data to read
        // ADD NEW CONNECTIONS READ FROM CONNECTIONS    
        for(i = 0; i <= fdmax; i++)
        {   
            if (FD_ISSET(i, &read_fds)) 
            {
                // we got one!!
                if (i == listener) 
                {                                    
                    AcceptNewClient(master, fdmax, listener);   
                }
                else 
                {
                    AcceptDataFromClient(i);
                }
            }
        }
    }

    return 0;
}
于 2013-01-24T10:11:33.863 に答える
1

等号で変数を fd_set することはできません。FD_COPY を使用する必要があります。そうでない場合は、既に完了としてマークされている実際のデータにハンドルをコピーするだけです。

于 2013-01-24T07:46:35.173 に答える
0

を使用しているコメントで述べましたLinux 2.6.27。Linux では、anfd_setは整数型の配列を含む構造体なので、代入を使用してコピーmasterしてread_fdsも問題ありません。

呼び出しがブロックされない理由は、とfromselectの両方を初期化しているためです。アイドル状態のクライアント ソケットは常に書き込み可能になるため、すぐに目を覚ますことができます。余談ですが、ソケットで書き込み可能を選択しても意味がありません。read_fdswrite_fdsmasterlistener

write_fds書き込みを試みてEAGAIN/EWOULDBLOCKエラーが発生した場合にのみ、クライアント ソケットを設定する必要があります。

于 2013-03-13T18:49:56.153 に答える