0

我有几个关于选择功能的问题,我写了这段代码:

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

我可以给第一个参数来选择函数,但我不能这样做,但是为什么呢?为什么 a 应该给第一个 arg 来选择?m_fdmax 是最大数量的套接字,但此代码在没有此参数的情况下工作。

下一个问题是,为什么选择需要超时?当我不提供此参数时,选择将所有套接字标记为可读的套接字,但在套接字没有任何数据可读取时选择执行此操作。当我给这个参数时,我没有这个问题。但为什么 ?

如果 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

我可以给第一个参数来选择函数,但我不能这样做,但是为什么呢?为什么 a 应该给第一个 arg 来选择?m_fdmax 是最大数量的套接字,但此代码在没有此参数的情况下工作。

阅读文档。Windows 上的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

关于超时: select可以用一个struct timeval来超时。如果你传递一个NULL指针,select将等到一个事件到来。如果您将地址传递给 a struct timevalselect即使没有事件也会返回(在您的代码中,select将每秒返回)。

关于 fdmax:是的,您必须找到最高的套接字,并且您的代码段是正确的。

其他:您的代码中没有任何FD_SET内容。通常,套接字(通过FD_SETso)设置在找到最高套接字的循环中。 编辑: 我的错我没有fd_set temp_list = m_RecvList;在你的代码中看到。我们将需要更多代码来分析您的问题select

于 2013-07-08T18:17:47.003 回答
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,你的 recv 函数看起来是:

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

但是 recv 在 bufor 存在时保存数据?Meybe 这应该是这样的:

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