Search code examples
c++recv

FD_ISSET always true even if there is no new data?


I am trying to check if a client has send some new data. This actually tells me that i always have new data:

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

I am connecting with a java client and send a "connection" message which i read inside of the 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;
}

This seems to work right. After that i keep checking if there is new data which always is true even if the java client does not send any new data.

Here is the java client. Don't judge me it's just for checking the connections. It wont stay like this to send the size information as 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();
}

So what is wrong with the hasData FD_ISSET method?


Solution

  • So what is wrong with the hasData FD_ISSET method?

    Actually no. There is a problem with your use of recv().

    recv() will return 0 if the client is disconnected and will return this until you close the socket (server-side). You can find this information in the manual. Even if recv() returns 0, it will "trigger" select().

    Knowing that, it's easy to find out the problem: you never check the return value of recv() and so you're unable to say if the client is still connected or not. However, you still add it with 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;
        }
    }