C++ 即使没有新数据,FD_ISSET是否始终为真?

C++ 即使没有新数据,FD_ISSET是否始终为真?,c++,recv,C++,Recv,我试图检查客户是否发送了一些新数据。这实际上告诉我,我总是有新的数据: bool ClientHandle::hasData() { fd_set temp; FD_ZERO(&temp); FD_SET(m_sock, &temp); //setup the timeout to 1000ms struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; //temp.

我试图检查客户是否发送了一些新数据。这实际上告诉我,我总是有新的数据:

bool ClientHandle::hasData()
{
    fd_set temp;
    FD_ZERO(&temp);
    FD_SET(m_sock, &temp);

    //setup the timeout to 1000ms
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 1000;
    //temp.fd_count possible?
    if (select(m_sock+1, &temp, nullptr, nullptr, &tv) == -1)
    {
        return false;
    }

    if (FD_ISSET(m_sock, &temp)) 
        return true;

    return false;
}
我正在与java客户机连接,并发送一条“连接”消息,我在ctor中阅读了该消息:

ClientHandle::ClientHandle(SOCKET s) : m_sock(s)
{
    while (!hasData())
    {
    }
    char buffer[5];
    recv(m_sock, buffer, 4, NULL);
    auto i = atoi(buffer);
    LOG_INFO << "Byte to receive: " << i;
    auto dataBuffer = new char[i + 1]{'\0'};
    recv(m_sock, dataBuffer, i, NULL);
    LOG_INFO << dataBuffer;
    //clean up
    delete[] dataBuffer;
}

那么hasdatafd_ISSET方法有什么问题呢

摘自Steven的书UNIX网络编程:

如果以下四个条件中的任何一个为真,则插座可用于读取:

  • 套接字接收缓冲区中的数据字节数大于或等于套接字接收缓冲区低水位线的当前大小。套接字上的读取操作不会阻塞,并将返回大于0的值(即准备读取的数据)。我们可以使用SO_RCVLOWAT插座选项设置此低水位线。TCP和UDP套接字的默认值为1

  • 连接的读取部分已关闭(即,已接收FIN的TCP连接)。套接字上的读取操作将不会阻塞,并返回0(即EOF)

  • 套接字是侦听套接字,已完成的连接数不为零。侦听套接字上的accept通常不会阻塞,尽管我们将在第16.6节中描述accept可以阻塞的计时条件

  • 套接字错误挂起。套接字上的读取操作将不会阻塞,并将返回错误(–1),并将errno设置为特定错误条件。还可以通过调用getsockopt并指定SO_ERROR socket选项来获取和清除这些挂起的错误

在上述所有情况下,ISSET都将返回true。Java客户机关闭连接后,套接字就可以在服务器中读取了

在ClientHandle::ClientHandle中,您没有检查recv的返回值以及是否返回了任何数据


它是否在第二次调用recv时阻塞?

您没有检查
recv
的返回值,也没有处理接收到的字节数少于您要求的字节数。那么,当连接关闭时,您预计会发生什么情况

那么hasdatafd_ISSET方法有什么问题呢

实际上没有。您使用的
recv()
有问题

如果客户端断开连接,
recv()
将返回
0
,并将返回该值,直到您
关闭
套接字(服务器端)。你可以找到这些信息。 即使
recv()
返回
0
,它也会“触发”
select()

知道了这一点,就很容易发现问题:您从不检查
recv()
的返回值,因此无法确定客户端是否仍然连接。但是,您仍然使用
FD\u SET
添加它

#include <sys/types.h> // for ssize_t
#include <stdio.h> // for perror()
ClientHandle::ClientHandle(SOCKET s) : m_sock(s)
{
    while (!hasData())
    {
    }
    char buffer[5];
    ssize_t ret = recv(m_sock, buffer, 4, NULL);
    if (ret == -1) // error
    {
        perror("recv");
        return ;
    }
    else if (ret == 0) // m_sock disconnects
    {
       close(m_sock);
       // DO NOT FD_SET m_sock since the socket is now closed 
    }
    else
    {
        auto i = atoi(buffer);
        LOG_INFO << "Byte to receive: " << i;
        auto dataBuffer = new char[i + 1]{'\0'};
        recv(m_sock, dataBuffer, i, NULL);
        LOG_INFO << dataBuffer;
        //clean up
        delete[] dataBuffer;
    }
}
#包括//用于ssize\t
#包括//for perror()
ClientHandle::ClientHandle(插座s):m_袜子(s)
{
而(!hasData())
{
}
字符缓冲区[5];
ssize_t ret=recv(m_sock,buffer,4,NULL);
if(ret==-1)//错误
{
perror(“recv”);
返回;
}
如果(ret==0)//m_sock断开连接,则为else
{
关闭(m_sock);
//不要设置m_sock,因为该套接字现在已关闭
}
其他的
{
自动i=atoi(缓冲区);

所以,has数据很好,但正如我从其他答案中看到的,它在不同的条件下也会返回true(不仅仅是它包含我认为的新数据),所以hasdata方法不正确。所以我需要以某种方式添加它。@BennX
hasdata()
几乎正确:您不应该在断开连接的插座上使用
FD\u SET
recv()
如果获得数据,则返回正数;
0
如果客户端断开连接,则返回正数;
-1
如果发生错误。我更新了答案以添加更多解释。因此,为了检查我是否获得了数据,我只需recv并处理它,如果有新数据?我想在尝试获取数据之前,我可以使用select检查它。@BennX
recv()
只需接收数据。
select()
允许您
recv()
在适当的时候。这是一个有用的资源,您应该看看。我已经知道该指南,但我想我需要再次深入阅读。我只是在接收之前尝试检查是否有来自客户端的数据,但我发现它没有如我预期的那样工作。您是否可以添加一个“正确”选项hasData如果这有可能的话?我想我需要把它组合成“getData”之类的东西它会自动检查并返回0(如果不是这样的话)。我明白了,所以hasData实际上是不正确的。您是否可以发布一种检查客户端是否有新数据的正确方法?它应该是阻塞的,但由于我在这些调用之间刷新,它不会阻塞并正常工作。@BennX,只需检查第一个recv的返回值是否有错误或0(零)数据。如果返回值为0且读取的数据为0,则连接已关闭。如果返回值为!=0,请检查错误。仅当第一个recv成功读取>0字节时,才调用第二个recv
#include <sys/types.h> // for ssize_t
#include <stdio.h> // for perror()
ClientHandle::ClientHandle(SOCKET s) : m_sock(s)
{
    while (!hasData())
    {
    }
    char buffer[5];
    ssize_t ret = recv(m_sock, buffer, 4, NULL);
    if (ret == -1) // error
    {
        perror("recv");
        return ;
    }
    else if (ret == 0) // m_sock disconnects
    {
       close(m_sock);
       // DO NOT FD_SET m_sock since the socket is now closed 
    }
    else
    {
        auto i = atoi(buffer);
        LOG_INFO << "Byte to receive: " << i;
        auto dataBuffer = new char[i + 1]{'\0'};
        recv(m_sock, dataBuffer, i, NULL);
        LOG_INFO << dataBuffer;
        //clean up
        delete[] dataBuffer;
    }
}