选择 - 几个问题

时间:2013-07-08 18:08:15

标签: c++ winsock

我有一些关于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;
                }
            }
        }
    }
}

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

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

如果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];
}

3 个答案:

答案 0 :(得分:1)

  

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

Read the documentation。 Windows上的select()函数会忽略第一个参数,因此传递给它的内容无关紧要。

  

接下来的问题是,为什么选择需要超时?

NEED 超时,但如果需要,您可以 OPTIONALLY 提供超时。这样,如果在超时之前未达到请求的套接字状态,select()仍然可以退出,并且不会无限期地使调用线程死锁,从而允许它执行其他操作。

  

当我没有给这个arg选择标记时,所有套接字都是可以读取的套接字,但是当套接字无法读取任何数据时选择这样做。

如果未提供超时,select()将无限期等待,直到请求的套接字状态实际发生。如果套接字有数据要读取,则可以将其标记为可读,但如果另一方已gracefully断开连接,则也可以将其标记为可读。随后对recv()的调用将告诉您哪种情况(recv()错误时返回-1,断开时返回0,数据上返回0> 0)。再次,read the documentation

  

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

答案 1 :(得分:0)

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

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

其他:您的代码中没有FD_SET。通常,套接字在循环中设置(通过FD_SET so),找到最高的套接字。 编辑:我的错误我没有在您的代码中看到fd_set temp_list = m_RecvList;。我们需要更多代码才能使用select分析您的问题。

答案 2 :(得分: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会保存数据吗? Meybe应该看起来:

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