-1

私は2つのスレッドを持っています:

スレッド A: select()ループです。新しい接続の受け入れ、データの受信など、読み取り操作のソケット処理を実行します。

while (1) {

    FD_ZERO(&fdReadSet);

    numActiveSockets = 0;

    for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++)
    {
        numActiveSockets++;
        FD_SET(it->first, &fdReadSet);
    }

    int ret;
    bool hasListen = false;

    if (( ret = select(numActiveSockets, &fdReadSet, NULL, NULL, NULL)) == SOCKET_ERROR) {
        printf("Select Failed, Error code = %d\n", WSAGetLastError());
        return -1;
    }

    for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++)
    {
        if (FD_ISSET(it->first, &fdReadSet))
        {
            if (it->first == TcpSocket::m_listen_sock)
            {
                if (!hasListen)
                {
                    sockaddr_in sock_addr;
                    int sockLength = sizeof(sock_addr);

                    SOCKET sock = accept(it->first, (sockaddr *) &sock_addr, &sockLength);
                    TcpSocket * socket = new TcpSocket();
                    socket->m_sock = sock;
                    m_sock_table[sock] = socket;

                    it = m_sock_table.begin();
                    hasListen = true;
                }
            }
            else
            {
                char * buffer = it->second->GetWriteBuffer();
                int numRead = recv(it->first, buffer, SOCKET_BUFFER_SIZE, 0);

                if (numRead == SOCKET_ERROR)
                {
                    int err = WSAGetLastError();
                    if (err == WSAECONNRESET)
                    {
                        printf("Connection [%i]: RESET Received. Closing Socket\n", it->first);
                        closesocket(it->first);
                        it = socketVector.erase(it->first);  // iterator invalidated after erase
                    }
                    else
                    {
                        printf("Recv Failed. Error code = %d\n", err);
                        return -1;
                    }
                }
                else if (numRead == 0)//connection close
                { 
                    printf("Connection [%i]: Graceful exit. Closing Socket\n", it->first);
                    closesocket(it->first);
                    it = socketVector.erase(it->first);  // iterator invalidated after erase

                }
                else {
                    /* Process received data */

                }

            }
        }
    }
}

スレッド B: アプリケーションがconnect()を実行して新しい接続を確立できるようにします。connect()が成功すると、返されたソケットが m_sock_table に追加されます

すべてのソケットを保持するm_sock_tableというソケット テーブルがあります。このm_sock_tableを使用して、 select()で使用されるfdReadSetを初期化します。

- - - - - -問題 - - - - - - - - -

スレッド A がselect()によってブロックされ、同時にスレッド B がconnect()を介して新しい接続を確立した場合、 fdReadsetが新しい接続で更新されていないため、アプリケーションは新しい接続からデータを受信できません。接続されたソケット

この問題を解決する良い方法は何でしょうか? それとも、最初からデザインが間違っているだけですか?

4

1 に答える 1

1

システムコールを中断する以外に何もしないシグナルを使用できます。

#include <signal.h>
void do_nothing() { }

struct sigaction sa;
sa.sa_handler = do_nothing;
sigemptyset(sa.sa_mask);
#ifdef SA_INTERRUPT
sa.sa_flags = SA_INTERRUPT;
#else
sa.sa_flags = 0;
#endif
sigaction(SIGUSR1, &sa, 0);

次に、スレッド B で新しい接続を開始した後、スレッド A がそれを処理することを確認した後、シグナルを送信します。

/* need only be done once, but needed in every thread other than A */
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSR1)
pthread_sigmask(SIG_BLOCK, &sigs, 0);

/* each time we create a new connection */
kill(getpid, SIGUSR1);

上記の場合、select は EINTR エラーを返すため、それを確認してループします (新しい接続をセットに追加します)。

于 2013-10-06T20:12:22.770 に答える