即使没有新数据,FD_ISSET也始终为真?

时间:2015-09-08 17:16:49

标签: 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.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;
}

这似乎工作正常。之后,我一直在检查是否有新数据始终为真,即使java客户端没有发送任何新数据。

这是java客户端。不要判断我只是检查连接。它不会像这样发送大小信息为char []。

public static void main(String[] args) throws UnknownHostException,
        IOException {
    Socket soc = null;

    soc = new Socket("localhost", 6060);
    PrintWriter out = new PrintWriter(soc.getOutputStream(), true);
    BufferedReader in = new BufferedReader(new InputStreamReader(
            soc.getInputStream()));

    if (soc != null)
        System.out.println("Connected");
    out.write("10\0");
    out.flush();
    out.write("newCon\0");
    out.flush();    
    out.close();
    in.close();
    soc.close();
}

那么hasData FD_ISSET方法有什么问题?

3 个答案:

答案 0 :(得分:1)

来自Steven的书籍UNIX Networking Programming:

如果满足以下四个条件之一,则套接字已准备好读取:

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

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

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

  • 套接字错误正在等待处理。套接字上的读操作不会阻塞,并且会将errno设置为特定错误条件时返回错误(-1)。通过调用getsockopt并指定SO_ERROR套接字选项,也可以获取和清除这些挂起的错误。

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

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

第二次调用recv会阻塞吗?

答案 1 :(得分:1)

您不会检查recv的返回值,并且您不会处理比您要求的更少的字节数。那么当连接关闭时你会发生什么呢?

答案 2 :(得分:1)

  

那么hasData FD_ISSET方法有什么问题?

其实没有。使用recv()时存在问题。

如果客户端断开连接,

recv()将返回0并将返回此信息直到您close套接字(服务器端)。您可以找到此信息in the manual。 即使recv()返回0,它也会触发&#34; select()

知道这一点,很容易发现问题:您永远不会检查recv()的返回值,因此您无法说明客户端是否仍然连接。但是,您仍然使用FD_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;
    }
}