2010-03-10 11 views
3

J'apprends les bases de l'écriture d'un serveur socket simple et efficace en utilisant GLib. J'expérimente avec GSocketService. Jusqu'à présent, je ne peux que sembler accepter les connexions, mais elles sont immédiatement fermées. De la docs je ne peux pas comprendre quelle étape je manque. J'espère que quelqu'un pourra éclaircir cela pour moi.Besoin d'aide pour implémenter un serveur socket simple en utilisant GIOService (GLib, Glib-GIO)

Lors de l'exécution de ce qui suit:

# 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. 

Sortie du serveur:

# ./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 

Code actuel:

/* 
* 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); 
} 
+1

Oups. Aussi juste remarqué ce code entraîne 100% d'utilisation du processeur après la première connexion. Semble poll() est répété appelé à partir de g_main_loop_run(). Hrm. –

Répondre

1

De the GIO docs:

Le GIOStream obj ect possède les flux d'entrée et de sortie, et non l'inverse, donc garder les sous-flux en vie ne gardera pas l'objet GIOStream en vie. Si l'objet GIOStream est libéré, il sera fermé, fermant ainsi le sous-flux, donc même si les sous-flux restent actifs, ils renverront toujours un G_IO_ERROR_CLOSED pour toutes les opérations.

5

Le GSocketConnection doit être refait dans le rappel entrant, cela maintiendra la connexion en vie. Vous pouvez le transmettre à une structure de données, à une classe ou à user_data au rappel de la montre.

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

Vous, vous devez mettre fin ne sont pas dans le network_read retournerez de rappel de la montre() avec "return true". De la documentation: "la fonction doit retourner FALSE si la source d'événement doit être supprimée".

Le processeur 100% est dû au fait qu'au moment où la connexion est fermée, le canal est toujours actif. Assurez-vous de supprimer correctement la source d'événement lorsque vous n'en avez plus besoin.

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

Ça sonne bien! Je vais accepter après quelques votes de plus parce que je ne suis pas en mesure de vérifier maintenant. –

2

Il est pas documenté dans la documentation GSocketService (je devais passer par les sources GLib pour le trouver), mais la routine qui appelle le rappel (new_connection dans ce cas) * fait un g_object_unref() sur la connexion objet * après son retour. Ceci ferme effectivement la connexion immédiatement new_connection() y retourne.

Je ne sais pas pourquoi il fait cela, mais la solution est d'ajouter un g_object_ref() en entrant dans le rappel:

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

Sans cet ajout, l'interrogation du descripteur de fichier dans la boucle principale vient de rentrer POLLNVAL parce que la connexion avait été fermée. En l'absence d'un gestionnaire pour ce résultat, il l'a fait continuellement - et c'est ce qui a causé la charge de 100% du processeur.

+0

Excellent point, Ron! Je me suis cogné la tête pendant deux jours maintenant. Merci! – jcoppens

+0

"Ce n'est pas documenté dans les documents GSocketService" --- C'est: dans la documentation de GSocketService :: incoming': "la connexion ne sera pas réactivée une fois le gestionnaire de signal retourné, donc vous devez refaire vous-même si vous prévoyez de utilise le" –

Questions connexes