Need help implementing simple socket server using GIOService (GLib, Glib-GIO)

前端 未结 3 916
半阙折子戏
半阙折子戏 2020-12-30 18:22

I\'m learning the basics of writing a simple, efficient socket server using GLib. I\'m experimenting with GSocketService. So far I can only seem to accept connections but th

相关标签:
3条回答
  • 2020-12-30 18:42

    It's not documented in the GSocketService docs (I had to go through the GLib sources to find it), but the routine that calls the callback (new_connection in this case) *does a g_object_unref() on the connection object* after it returns. This effectively closes the connection immediately new_connection() returns to it.

    I have no idea why it does this, but the solution is to add a g_object_ref() on entering the callback:

    gboolean
    new_connection(GSocketService *service,
                  GSocketConnection *connection,
                  GObject *source_object,
                  gpointer user_data)
    {
    
      g_object_ref(connection);    /* Tell glib not to disconnect */
    
      GSocketAddress *sockaddr = g_socket_connection_get_remote_address(connection, NULL);
      GInetAddress *addr =
          g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(sockaddr));
      guint16 port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(sockaddr));
    

    Without that addition, polling the file descriptor in the main loop just returned POLLNVAL because the connection had been closed. In the absence of a handler for that result, it did that continuously -- and that's what caused the 100% CPU load.

    0 讨论(0)
  • 2020-12-30 19:00

    The GSocketConnection has to be ref'ed in the incoming callback, this will keep the connection alive. You can pass it to a data structure, a class, or as user_data to the watch callback.

    gboolean
    new_connection(...)
    {
      ...
    
      g_object_ref (connection);
      GSocket *socket = g_socket_connection_get_socket(connection);
    
      gint fd = g_socket_get_fd(socket);
      GIOChannel *channel = g_io_channel_unix_new(fd);
      // Pass connection as user_data to the watch callback
      g_io_add_watch(channel, G_IO_IN, (GIOFunc) network_read, connection);
      return TRUE;
    }
    

    You are not returning in the watch callback network_read(), you must end it with "return true". From the documentation: "the function should return FALSE if the event source should be removed".

    The 100% CPU is caused by the fact that at the time the connection is closed the channel is still alive. Make sure to properly remove the event source when no longer needed.

    gboolean
    network_read(GIOChannel *source,
                 GIOCondition cond,
                 gpointer data)
    {
      GString *s = g_string_new(NULL);
      GError *error = NULL;
      GIOStatus ret = g_io_channel_read_line_string(source, s, NULL, &error);
    
      if (ret == G_IO_STATUS_ERROR) {
        //g_error ("Error reading: %s\n", error->message);
        g_warning ("Error reading: %s\n", error->message);
        // Drop last reference on connection
        g_object_unref (data);
        // Remove the event source
        return FALSE;
      }
      else
        g_print("Got: %s\n", s->str);
    
      if (ret == G_IO_STATUS_EOF) {
        return FALSE;
      }
    
    0 讨论(0)
  • 2020-12-30 19:09

    From the GIO docs :

    The GIOStream object owns the input and the output streams, not the other way around, so keeping the substreams alive will not keep the GIOStream object alive. If the GIOStream object is freed it will be closed, thus closing the substream, so even if the substreams stay alive they will always just return a G_IO_ERROR_CLOSED for all operations.

    0 讨论(0)
提交回复
热议问题