Problème fork et pipe en C
Fermé
dragnyon
-
Modifié le 20 déc. 2021 à 12:53
mamiemando Messages postés 33372 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 22 novembre 2024 - 20 déc. 2021 à 13:24
mamiemando Messages postés 33372 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 22 novembre 2024 - 20 déc. 2021 à 13:24
A voir également:
- Problème fork et pipe en C
- Pipe macos - Guide
- Pip n'est pas reconnu en tant que commande interne ✓ - Forum Python
- Problème PIP - Forum Python
- Relais brisé (pipe) - Forum Programmation
- 'Pip' n’est pas reconnu en tant que commande interne ✓ - Forum Programmation
1 réponse
mamiemando
Messages postés
33372
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
22 novembre 2024
7 802
20 déc. 2021 à 13:24
20 déc. 2021 à 13:24
Bonjour,
Ce que je te propose, c'est d'utiliser
Pré-requis
Dans ce qui suit, je présuppose que tu as installé
Pour la phase de compilation je suppose que tu as installé un compilateur (e.g.
Sous Linux (plus précisément sous debian/ubuntu) on installe ces dépendances comme suit :
Pour windows, voir ce lien.
client.c
Dans cet exemple, le client envoie un couple (id, d) avec d = id + 1000.
server.c
Dans cet exemple, le serveur renvoie au client adéquat le couple (id, d-1000) :
Makefile
Compilation
Quelques recommandations
Puisque tu dis débuter, je me permets quelques conseils pour améliorer la qualité de ton code :
Bonne chance
Ce que je te propose, c'est d'utiliser
glibet
gio, car cela permet de faire plus simplement et proprement un client/serveur ultra performante. Pas besoin de s'embêter avec des threads, les problèmes de timeouts, tout est géré pour toi par gio. La seule difficulté, c'est d'installer glib et gio (sous Linux c'est facile, sous Windows, je ne sais pas mais normalement c'est faisable).
Pré-requis
Dans ce qui suit, je présuppose que tu as installé
glib-2.0et
gio-2.0. Ces deux librairies existent sous Windows et Linux.
Pour la phase de compilation je suppose que tu as installé un compilateur (e.g.
gcc) et dans mon cas je lance la compilation via une
Makefile, ce qui nécessite d'installer
make. Tu peux utiliser ce que tu as l'habitude d'utiliser.
Sous Linux (plus précisément sous debian/ubuntu) on installe ces dépendances comme suit :
sudo apt update
sudo apt install libglib2.0-dev glib-networking make
Pour windows, voir ce lien.
client.c
Dans cet exemple, le client envoie un couple (id, d) avec d = id + 1000.
// Based on https://stackoverflow.com/questions/24476188/asynchronous-gio-server-client #include <glib.h> #include <gio/gio.h> #include <stdio.h> #include <string.h> #include "common.h" #define TIMEOUT_MS 10000 // 10 seconds #define BUFFER_SIZE 1024 struct client_conn_t { GSocketClient * client; // Needed to get GSocketClient from main() GSocketConnection * connection; // Needed to return GSocketConnection to main() GIOChannel * channel; }; static void print_connection(GSocketConnection * connection) { 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("Connection to %s:%d\n", g_inet_address_to_string(addr), port); } static gboolean callback_read( GIOChannel * channel, GIOCondition condition, gpointer user_data ) { TRACE_FUNCTION; gsize len; GIOStatus ret; GSocketConnection * connection = G_SOCKET_CONNECTION(user_data); GError * error = NULL; if (condition & G_IO_HUP) { g_error("The server has closed the connection!\n"); return FALSE; } gchar buffer[BUFFER_SIZE]; // Larger than sizeof(reply_t) gsize bytes_read; ret = g_io_channel_read_chars(channel, buffer, BUFFER_SIZE, &len, &error); switch (ret) { case G_IO_STATUS_ERROR: g_print("Error reading: %s\n", error->message); g_object_unref(connection); return FALSE; case G_IO_STATUS_EOF: g_print("The server has closed the connection\n"); g_object_unref(connection); return FALSE; } if (len) { g_print("Read %u bytes (expected: %d)\n", len, sizeof(reply_t)); print_reply((reply_t *) buffer); } return TRUE; } gboolean callback_send(gpointer data) { TRACE_FUNCTION; struct client_conn_t *c = data; if (g_socket_connection_is_connected(c->connection)) { static unsigned query_id = 0; query_id++; query_t q = { .query_id = query_id, .data = query_id + 1000 }; print_query(&q); // The server should reply (query_id, data) == (query_id, query_id) // Sync version //return send_bytes(c->connection, c->channel, &q, sizeof(q)); // Async version. Pass a callback instead of NULL to use your own GAsyncReadyCallback. send_bytes_async(c->connection, &q, sizeof(q), NULL); } else { g_print("callback_send : not connected\n"); } return TRUE; // Keep this GSource, the server may start soon... } void callback_connect( GObject * source_object, GAsyncResult * res, gpointer user_data ) { TRACE_FUNCTION; GError * error = NULL; // Set GSocketConnection struct client_conn_t * c = (struct client_conn_t *) user_data; GSocketConnection * connection = g_socket_client_connect_to_host_finish(c->client, res, &error); c->connection = connection; // Check whether the GSocketConnection is established. if (error) { g_error(error->message); return; } // Print connection print_connection(connection); // Install watch g_object_ref(connection); // ADDED GSocket * socket = g_socket_connection_get_socket(connection); if (!socket) { g_error("Cannot get socket\n"); return; } // From here, the code is the same in the client and the server. gint fd = g_socket_get_fd(socket); GIOChannel * channel = g_io_channel_unix_new(fd); c->channel = channel; // We'll need it for callback_send g_print("channel = %p\n", channel); if (!channel) { g_error("Cannot create channel\n"); return; } // Exchange binary data with the server g_io_channel_set_encoding(channel, NULL, &error); if (error) { g_error("Cannot set encoding: %s", error->message); return; } // G_IO_IN: There is data to read. // G_IO_OUT: Data can be written (without blocking). // G_IO_PRI: There is urgent data to read. // G_IO_ERR: Error condition. // G_IO_HUP: Hung up (the connection has been broken, usually for pipes and sockets). // G_IO_NVAL: Invalid request. The file descriptor is not open. // Triggered whenever the client can read data from the socket if (!g_io_add_watch(channel, G_IO_IN | G_IO_HUP, callback_read, connection)) { g_error("Cannot watch\n"); return; } /* // Triggered whenever the client can write into the socket if (!g_io_add_watch(channel, G_IO_OUT | G_IO_HUP, callback_write, connection)) { g_error("Cannot watch\n"); return; } */ g_timeout_add(500, callback_send, c); g_print("OK!\n"); } gboolean timeout_callback(gpointer data) { TRACE_FUNCTION; g_print("timeout_callback: LEAVING\n"); g_main_loop_quit((GMainLoop*) data); return FALSE; } int main(int argc, char **argv) { #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init (); #endif struct client_conn_t *c = g_malloc0(sizeof *c); c->client = g_socket_client_new(); // NO context must be used! // Indeed g_io_add_watch installs watch in the DEFAULT context. GMainLoop * loop = g_main_loop_new(NULL, FALSE); g_timeout_add(TIMEOUT_MS, timeout_callback, loop); g_socket_client_connect_to_host_async(c->client, "localhost", PORT, NULL, callback_connect, c); g_main_loop_run(loop); // Free, close etc... g_print("Free\n"); g_io_stream_close(G_IO_STREAM(c->connection), NULL, NULL); g_object_unref(c->client); g_object_unref(c->connection); g_main_loop_unref(loop); g_free(c); return 0; }
server.c
Dans cet exemple, le serveur renvoie au client adéquat le couple (id, d-1000) :
// Based on: // https://stackoverflow.com/questions/9513327/gio-socket-server-client-example // http://www.linuxjournal.com/node/8545/print // // Compilation: // gcc `pkg-config --cflags --libs glib-2.0 gio-2.0` server.c -o server #include <glib.h> #include <gio/gio.h> #include <string.h> #include "common.h" #define BUFFER_SIZE 1024 static gboolean callback_read( GIOChannel * channel, GIOCondition condition, gpointer user_data ) { TRACE_FUNCTION; // gsize len; gssize len; GIOStatus ret; GSocketConnection * connection = G_SOCKET_CONNECTION(user_data); GError * error = NULL; if (condition & G_IO_HUP) { g_print("The client has disconnected! I feel alone so I stop to listen.\n"); return FALSE; // The client has disconnected abruptly, remove this GSource } gchar buffer[BUFFER_SIZE]; // Larger than sizeof(reply_t) gsize bytes_read; /* INCORRECT // ret = g_io_channel_read_chars(channel, buffer, BUFFER_SIZE, &len, &error); */ GInputStream * istream = g_io_stream_get_input_stream(G_IO_STREAM(connection)); g_print("istream = %x\n", istream); len = g_input_stream_read (istream, buffer, BUFFER_SIZE, NULL, &error); g_print("len = %d\n", len); switch (len) { case -1: g_error("Error reading: %s\n", error->message); g_object_unref(connection); return FALSE; case 0: g_print("Client disconnected\n"); return FALSE; // The client has closed the connection gracefully, remove this GSource default: break; } query_t * q = (query_t *) buffer; print_query(q); // TODO Run the query in a thread, then send the reply back to the client. // TODO If data >= 0 : send every 100ms either data+1,data+2,data+3 with a dedicated thread. // TODO If data < 0 : send every 150ms either data-1,data-2,data-3 with a dedicated thread. static int reply_id = 0; reply_t r = { .query_id = q->query_id, .data = q->data - 1000 }; print_reply(&r); // Sync version //return send_bytes(connection, channel, &r, sizeof(r)); // Async version. Pass a callback instead of NULL to use your own GAsyncReadyCallback. send_bytes_async(connection, &r, sizeof(r), NULL); return TRUE; } // This function will get called everytime a client attempts to connect gboolean callback_connect( GSocketService * service, GSocketConnection * connection, GObject * source_object, gpointer user_data ) { TRACE_FUNCTION; GError * error = NULL; // Print connection 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); // Install watch g_object_ref(connection); GSocket * socket = g_socket_connection_get_socket(connection); // From here, the code is the same in the client and the server. gint fd = g_socket_get_fd(socket); GIOChannel * channel = g_io_channel_unix_new(fd); if (!channel) { g_error("Cannot create channel\n"); return TRUE; } // Exchange binary data with the client g_io_channel_set_encoding(channel, NULL, &error); if (error) { g_error("Cannot set encoding: %s", error->message); return TRUE; } // G_IO_IN: There is data to read. // G_IO_OUT: Data can be written (without blocking). // G_IO_PRI: There is urgent data to read. // G_IO_ERR: Error condition. // G_IO_HUP: Hung up (the connection has been broken, usually for pipes and sockets). // G_IO_NVAL: Invalid request. The file descriptor is not open. // Triggered whenever the server can read data from the socket if (!g_io_add_watch(channel, G_IO_IN | G_IO_HUP, callback_read, connection)) { g_error("Cannot watch\n"); return TRUE; } return FALSE; } int main(int argc, char **argv) { // For old glib #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init (); #endif // socket() GError * error = NULL; GSocketService * service = g_socket_service_new(); g_socket_listener_add_inet_port( (GSocketListener *) service, PORT, NULL, &error ); if (error) { g_error(error->message); return 1; } // connect() // Listen to the 'incoming' signal g_signal_connect( service, "incoming", G_CALLBACK(callback_connect), NULL ); // Start the socket service g_socket_service_start(service); // Run the main loop g_print("Listening on localhost:%d\n", PORT); GMainLoop *loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); // This cannot be reached will we do not configure Cancelable g_print("Free\n"); g_main_loop_unref(loop); g_socket_service_stop(service); g_free(service); return 0; }
Makefile
CC = gcc CFLAGS = `pkg-config --cflags --libs glib-2.0 gio-2.0` -g -W all: $(CC) $(CFLAGS) -c common.c -o common.o $(CC) $(CFLAGS) client_async.c -o client_async common.o $(CC) $(CFLAGS) server_async.c -o server_async common.o
Compilation
make
Quelques recommandations
Puisque tu dis débuter, je me permets quelques conseils pour améliorer la qualité de ton code :
- évite les variables globales (surtout quand tu utilises des threads !) ;
- soigne l'indentation (espace autour des opérateurs, indentation = 4 espaces...; espace derrière les virgules/points virgules) ;
- soigne le nommage (je recommande d'adopter le style suivant :
ma_variable
,ma_fonction
,typedef struct ma_structure_t { ... } ma_structure_t;
,#define MA_CONSTANTE
).
Bonne chance