0

select 関数についていくつか質問があります。次のコードを書きました。

void TCPSerwer::sel()
{
    struct timeval tv = {1, 0};
    fd_set temp_list = m_RecvList;
    //if(select(m_fdmax + 1, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR)
    if(select(0, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR)
    {
        perror( "select" );
        exit( 1 );
    }

    for(int i = 0; i <= m_fdmax; i++ )
    {
        if(FD_ISSET(i, &temp_list))
        {
            // New connection
            if(i == m_ListenSocket)
            {
                acceptClient();
            }

            // Data from client
            else
            {
                PacketHeader header;
                int nbytes = recv(i, (char*)(&header), sizeof(PacketHeader),

                // Error
                if(nbytes < 0)
                {
                    disconnectClient(i);
                }
                // success
                else
                {
                    std::cout << "type: " << header.type << "   len: " << header.length << std::endl;
                }
            }
        }
    }
}

関数を選択するために最初の引数を与えることができますが、それはできませんが、なぜですか? select に最初の引数を与える必要があるのはなぜですか? m_fdmax はソケットの最大数ですが、このコードはこの引数なしで機能します。

次の質問は、なぜタイムアウトが必要なのかを選択することです。この引数を指定しない場合、select はすべてのソケットを読み取り可能なソケットとしてマークしますが、ソケットに読み取るデータがない場合はこれを選択します。この引数を与えると、この問題はありません。しかし、なぜ ?

m_fdmax がソケットの最大数の場合、接続を閉じるときに次に高いソケット数を見つける必要がありますよね? そして、私はこれを行う必要があります:

int size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
   if(m_ClientVector[i] > m_fdmax)
           m_fdmax = m_ClientVector[i];
}
4

3 に答える 3

1

関数を選択するために最初の引数を与えることができますが、それはできませんが、なぜですか? select に最初の引数を与える必要があるのはなぜですか? m_fdmax はソケットの最大数ですが、このコードはこの引数なしで機能します。

ドキュメントを読んでください。Windowsのselect()関数は最初のパラメーターを無視するため、何を渡しても問題ありません。

次の質問は、なぜタイムアウトが必要なのかを選択することです。

タイムアウトは必要ありませんが、必要に応じてオプションでタイムアウトを指定できます。このようにして、タイムアウトが経過する前に要求されたソケット状態に達しない場合select()でも、呼び出し元のスレッドを無期限に終了してデッドロックせずに、他のことを実行できるようにすることができます。

この引数を指定しない場合、select はすべてのソケットを読み取り可能なソケットとしてマークしますが、ソケットに読み取るデータがない場合はこれを選択します。

タイムアウトを指定しない場合select()、要求されたソケット状態が実際に発生するまで無期限に待機します。ソケットは、読み取るデータがある場合は読み取り可能としてマークできますがgracefully、相手側によって切断されている場合も読み取り可能としてマークできます。後続の への呼び出しrecv()は、どちらが該当するかを示します (エラーの場合recv()は -1、切断の場合は 0、データの場合は >0 を返します)。もう一度、ドキュメントを読んでください。

m_fdmax がソケットの最大数の場合、接続を閉じるときに次に高いソケット数を見つける必要がありますよね?

最大ソケット番号を計算したい場合 (Windows は気にしませんが、他のプラットフォームは気にします)、 を呼び出すselect()たびに、または少なくともfd_set構造を再準備するたびに、最大ソケット番号を再計算する必要があります。 (select()とにかく呼び出すたびにこれを行う必要があります)。

そして、私はこれをやるべきです

Windows では、いいえ。他のプラットフォームでは、はい。

そうは言っても、代わりに Windows で次のコードを試してください。

void TCPSerwer::sel()
{
    struct timeval tv = {1, 0};
    fd_set temp_list = m_RecvList;

    int ret = select(0, &temp_list, NULL, NULL, &tv);
    if (ret == SOCKET_ERROR)
    {
        perror( "select" );
        exit( 1 );
    }

    if (ret == 0) // timeout
        return;

    for(u_int i = 0; i < temp_list.fd_count; ++i)
    {
        SOCKET s = temp_list.fd_array[i];

        // New connection
        if (s == m_ListenSocket)
        {
            acceptClient();
            continue;
        }

        // Data from client

        PacketHeader header;

        char *pheader = (char*) &header;
        int nbytes = 0;

        do
        {
            ret = recv(s, pheader, sizeof(PacketHeader)-nbytes, 0);   

            // success
            if (ret > 0)
                nbytes += ret;
        }
        while ((ret > 0) && (nbytes < sizeof(PacktHeader)));

        // Error or disconnect
        if (nbytes < sizeof(PacktHeader))
        {
            disconnectClient(i);
            continue;
        }

        // success
        std::cout << "type: " << header.type << "   len: " << header.length << std::endl;
    }
}
于 2013-07-09T00:44:55.400 に答える
0

助けてくれてありがとう、私はこのコードを Windows と Linux で使用したいのですが、今それをやっています: 新しい接続があるとき:

bool TCPSerwer::acceptClient()
{
SOCKET new_client = accept(m_ListenSocket, 0, 0);

if(new_client == INVALID_SOCKET)
        return false;

m_ClientVector.push_back(new_client);

    // Add to FD
    FD_SET(new_client, &m_RecvList);

if(new_client > m_fdmax)
    m_fdmax = new_client;

return true;
}

接続を閉じたいとき:

void TCPSerwer::disconnectClient(const SOCKET& client)
{
int size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
    if(m_ClientVector[i] == client)
    {
        closesocket(m_ClientVector[i]);

        // Delete from FD
        FD_CLR(m_ClientVector[i], &m_RecvList);

        m_ClientVector.erase(m_ClientVector.begin() + i);

        break;
    }
}

// re-calculateing the highest socket number
size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
    if(m_ClientVector[i] > m_fdmax)
        m_fdmax = m_ClientVector[i];
}

}

Remy Lebeauに1 つの質問があります。recv関数は次のように見えます。

recv(s, pheader, sizeof(PacketHeader)-nbytes, 0); 

しかし、buforのときにrecvセーブデータは?おそらくこれは次のようになります。

recv(s, pheader + nbytes, sizeof(PacketHeader)-nbytes, 0); 
于 2013-07-10T16:11:51.043 に答える