Нужна помощь в реализации простого сокет-сервера с использованием GIOService (GLib, Glib-GIO) - PullRequest
3 голосов
/ 10 марта 2010

Я изучаю основы написания простого и эффективного сервера сокетов с использованием GLib. Я экспериментирую с GSocketService. Пока что я могу только принимать соединения, но они сразу же закрываются. Из документов я не могу понять, какой шаг мне не хватает. Я надеюсь, что кто-то может пролить свет на это для меня.

При выполнении следующего:

# telnet localhost 4000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
# telnet localhost 4000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
# telnet localhost 4000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.

Вывод с сервера:

# ./server
New Connection from 127.0.0.1:36962
New Connection from 127.0.0.1:36963
New Connection from 127.0.0.1:36965

Текущий код:

/*
 * server.c
 *
 *  Created on: Mar 10, 2010
 *      Author: mark
 */
#include <glib.h>
#include <gio/gio.h>

gchar *buffer;

gboolean
network_read(GIOChannel *source,
            GIOCondition cond,
            gpointer data)
{
  GString *s = g_string_new(NULL);
  GError *error;
  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);
  else
    g_print("Got: %s\n", s->str);

}

gboolean
new_connection(GSocketService *service,
              GSocketConnection *connection,
              GObject *source_object,
              gpointer user_data)
{
  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));

  g_print("New Connection from %s:%d\n", g_inet_address_to_string(addr), port);

  GSocket *socket = g_socket_connection_get_socket(connection);

  gint fd = g_socket_get_fd(socket);
  GIOChannel *channel = g_io_channel_unix_new(fd);
  g_io_add_watch(channel, G_IO_IN, (GIOFunc) network_read, NULL);
  return TRUE;
}

int main(int argc, char **argv) {
  g_type_init();
  GSocketService *service = g_socket_service_new();
  GInetAddress *address = g_inet_address_new_from_string("127.0.0.1");
  GSocketAddress *socket_address = g_inet_socket_address_new(address, 4000);
  g_socket_listener_add_address(G_SOCKET_LISTENER(service), socket_address, G_SOCKET_TYPE_STREAM,
          G_SOCKET_PROTOCOL_TCP, NULL, NULL, NULL);

  g_object_unref(socket_address);
  g_object_unref(address);
  g_socket_service_start(service);

  g_signal_connect(service, "incoming", G_CALLBACK(new_connection), NULL);

  GMainLoop *loop = g_main_loop_new(NULL, FALSE);
  g_main_loop_run(loop);
}

Ответы [ 3 ]

5 голосов
/ 18 июля 2010

GSocketConnection необходимо перефразировать во входящем обратном вызове, это будет поддерживать соединение живым. Вы можете передать его в структуру данных, класс или как user_data для обратного вызова watch.

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

Вы не возвращаетесь в обратном вызове network_read (), вы должны завершить его с помощью «return true». Из документации: « функция должна возвращать FALSE, если источник события должен быть удален ».

Процессор 100% вызван тем, что на момент закрытия соединения канал еще жив. Убедитесь, что правильно удалили источник события, когда он больше не нужен.

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;
  }
2 голосов
/ 27 октября 2011

Это не задокументировано в документации GSocketService (мне пришлось пройтись по источникам GLib, чтобы найти его), но подпрограмма, которая вызывает callback (в данном случае new_connection) * выполняет g_object_unref () для объекта соединения * после него возвращается. Это эффективно закрывает соединение, сразу же возвращается new_connection ().

Я понятия не имею, почему это происходит, но решение состоит в том, чтобы добавить g_object_ref () при вводе обратного вызова:

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

Без этого добавления опрос файлового дескриптора в основном цикле только что возвратил POLLNVAL, потому что соединение было закрыто. В отсутствие обработчика для этого результата, он делал это непрерывно - и это вызвало 100% загрузку ЦП.

1 голос
/ 22 мая 2010

С Документация GIO :

Объект GIOStream владеет входным и выходным потоками, а не наоборот, поэтому поддержание подпотоков живым не будет поддерживать объект GIOStream живым. Если объект GIOStream освобожден, он будет закрыт, таким образом закрывая подпоток, поэтому даже если подпотоки останутся живыми, они всегда будут просто возвращать G_IO_ERROR_CLOSED для всех операций.

...