Why do I always get 0 bytes reading from a GInputStream?

I’m writing a client application that connects to a server using a TCP socket. And I only want to use async APIs.

I’m creating a socket with g_socket_client_new() and I connect to the server with g_socket_client_connect_to_uri_async(). This works fine. The callback function specified with g_socket_client_connect_to_uri_async() gets called and a connection to the server gets established. The problem is with reading from the incoming stream.

All PDUs sent over the TCP connection start with an 8-byte header. The header contains, among other things, the total length of the PDU. The idea is to read the 8-byte header, retrieve to total length of the PDU from the header, and use that length to determine how many more bytes need to be read.

I start the ball rolling by invoking g_input_stream_read_all_async() and I specify a count of 8. I also specify a callback function, which should only be called when the 8 bytes have been read or if an error occurs. In the callback I use g_input_stream_read_all_finish() to retrieve the status and number of bytes that were actually read. g_input_stream_read_all_finish() always returns TRUE (meaning success), but the number of bytes read is always 0. According to the documentation, this indicates end-of-file (i.e. TCP socket is disconnected). However, I checked with g_socket_connection_is_connected() and the socket is still connected. What am I missing?

Hi, welcome to Discourse :slight_smile:

It’s pretty hard to answer this question without a minimal reproducible example, since the problem could be anywhere in your client code or in the server code. Can you provide one please?

Hi Philip. Thanks for your response. I’m including the code below. I included all the relevant pieces. Note that the connection with the server is successful and I’m able to send data to the server over the GOutputStream. It’s the data from the server over the GInputStream that is not being received.

class dc_c /* Discover Controller */
{
public:
    dc_c(nofussd_context_c & ctx_r, const std::string & uri_r);
    ~dc_c();

    const char * uri() const { return uri_m.c_str(); }

private:
    std::string          uri_m      = "tcp://localhost:8009";
    GCancellable       * cancel_pm  = nullptr;
    GSocketClient      * socket_pm  = nullptr;
    GSocketConnection  * conn_pm    = nullptr;

    static void on_tcp_connected(GObject * source_object_p, GAsyncResult * result_p, gpointer user_data_p)
    {
        static_cast<dc_c *>(user_data_p)->tcp_connected_finish(source_object_p, result_p);
    }

    void dc_c::tcp_connected_finish(GObject * source_object_p, GAsyncResult * result_p);

    void dc_c::istream_look_for_CH(GInputStream  * istream_p);


    static void on_istream_recv_CH(GObject * source_object_p, GAsyncResult * res_p, gpointer user_data_p)
    {
        GInputStream  * istream_p = G_INPUT_STREAM(source_object_p);
        static_cast<dc_c *>(user_data_p)->istream_recv_CH(istream_p, res_p);
    }
    void istream_recv_CH(GInputStream  * istream_p, GAsyncResult * res_p);

[...]


dc_c::dc_c(const std::string & uri_r) : uri_m(uri_r)
{
    socket_pm = g_socket_client_new();
    cancel_pm = g_cancellable_new();

    g_socket_client_connect_to_uri_async(socket_pm, uri(), NVME_TCP_DISC_PORT,
                                         cancel_pm, dc_c::on_tcp_connected, this);

[...]
}


void dc_c::tcp_connected_finish(GObject * source_object_p, GAsyncResult * result_p)
{
    GError * err_p  = nullptr;
    conn_pm = g_socket_client_connect_to_uri_finish(G_SOCKET_CLIENT(source_object_p), result_p, &err_p);

    if (conn_pm == nullptr)
    {
        syslog(LOG_ERR, "dc_c::tcp_connected_finish() - %s: Failed to connect. %s", uri(), err_p->message);
        g_error_free(err_p);
        err_p = nullptr;
        restart_connection();
        return;
    }

    g_tcp_connection_set_graceful_disconnect(G_TCP_CONNECTION(conn_pm), TRUE);

    // Start looking for PDUs (Wait for Common Header).
    // All received PDUs will start with an 8-byte Common Header (CH).
    istream_look_for_CH(g_io_stream_get_input_stream(G_IO_STREAM(conn_pm)));

[...]

}

#define CH_SZ  8
void dc_c::istream_look_for_CH(GInputStream  * istream_p)
{
    memset(&recv_buffer_m[0], 0, sizeof(recv_buffer_m));
    g_input_stream_read_all_async(istream_p, &recv_buffer_m[0], CH_SZ, G_PRIORITY_DEFAULT,
                                  cancel_pm, dc_c::on_istream_recv_CH, this);
}

void dc_c::istream_recv_CH(GInputStream  * istream_p, GAsyncResult  * res_p)
{
    GError   * err_p   = nullptr;
    gsize      bytes_n = 0;
    gboolean   success = g_input_stream_read_all_finish(istream_p, res_p, &bytes_n, &err_p);

    syslog(LOG_INFO, "dc_c::istream_recv_CH() - %s: success=%s, bytes_n=%lu, expected=8, err_p=%p, connected=%s, iostream:[closed=%s, has-pending=%s], istream:[closed=%s, has-pending=%s]",
           uri(), true_false(success), bytes_n, err_p,
           true_false(g_socket_connection_is_connected(conn_pm)),
           true_false(g_io_stream_is_closed(G_IO_STREAM(conn_pm))),
           true_false(g_io_stream_has_pending(G_IO_STREAM(conn_pm))),
           true_false(g_input_stream_is_closed(istream_p)),
           true_false(g_input_stream_has_pending(istream_p)));

[...]

And here’s the syslog I see in the journal’

dc_c::istream_recv_CH() - tcp://localhost:8009: success=true, bytes_n=0, expected=8, err_p=(nil), connected=true, iostream:[closed=false, has-pending=false], istream:[closed=false, has-pending=false]

Here we see that success=TRUE and bytes_n=0 although I asked for 8 bytes.

My bad. There is a problem on the server and it is closing the connection. That’s why my client gets 0 bytes. Problem solved.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.