Search code examples
cserveriostreamgio

Simple threaded server implemented by gio library


I am trying to learn gio library, especially the giostream and gthreadedsocketservice. I want to write a simple server that:

  1. Each incoming connection will be handled by an individual new thread
  2. At the client side, user types a string and it will be sent to the server; at the server side upon receiving the string, immediately displays it to stdout.
  3. Unless server or client is terminated, the connection is not closed. Namely multiple messages can be sent from client to server without the need to connect multiple times.

The code I tried is: Client-side:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <gio/gio.h>

int main(int argc, char* argv[]){
 GError* error = NULL;
 GSocketConnection* connection = NULL;
 GOutputStream* ostream = NULL;
 GSocketClient* client = g_socket_client_new();
 gchar message[1024];

 connection = g_socket_client_connect_to_host(client, (gchar*)"localhost", 1500, NULL, &error);

 if (error) {
  g_error(error->message);
  g_error_free(error);
  return 1;
 }
 else g_print("Message: connected.\n");

 while(TRUE){
  scanf("%s", message);
  ostream = g_io_stream_get_output_stream(G_IO_STREAM(connection));
  g_output_stream_write(ostream, message, strlen(message), NULL, &error);
  if (error) {
   g_error(error->message);
   g_error_free(error);
   return 1;
  }
 }

 g_print("Message: client terminated.\n");
 return 0;
}

Server-side:

#include <glib.h>
#include <gio/gio.h>

gboolean run_callback(GThreadedSocketService*, GSocketConnection*, GObject*, gpointer);

int main(int argc, char **argv){
 int port = 1500;
 GError* error = NULL;
 GMainLoop* loop = NULL;
 GThreadedSocketService* service = NULL;

 service = (GThreadedSocketService*)g_threaded_socket_service_new(-1);
 g_socket_listener_add_inet_port((GSocketListener*)service, port, NULL, &error);
 if (error != NULL) {g_error(error->message);}
 g_signal_connect(service, "run", G_CALLBACK(run_callback), NULL);
 g_socket_service_start((GSocketService*)service);
 g_print("Message: server launched...\n");
 loop = g_main_loop_new(NULL, FALSE);
 g_main_loop_run(loop);

 return 0;
}

gboolean run_callback(GThreadedSocketService* service, GSocketConnection* connection, GObject* source_object, gpointer user_data){
 GInputStream* instream = NULL;
 gchar message[1024];
 GError* error = NULL;

 instream = g_io_stream_get_input_stream(G_IO_STREAM(connection));
 g_input_stream_read_all(instream, message, 1024, NULL, NULL, &error);
 if (error != NULL) {
  g_error(error->message);
  g_error_free(error);
  return FALSE;
 }
 g_print("Received: %s\n", message);

 g_print("Message: connection terminated.\n");
 if (error) g_error_free(error);
 return FALSE;
}

The problem is when I tested it out, on client side I typed three lines:

aaa
bbb
ccc

But nothing is shown on the server side. Only when I exit the client, on the server screen it shows:

aaabbbccc

But what I wanted was when I type "aaa" and entered, it immediately shows up on the server screen.

Any idea on where it goes wrong?


Solution

  • The problem is that you use g_input_stream_read_all. Notice the suffix all in the name? It means that it will attempt to read the size you pass to it, only returning when it has received all those bytes or there is an error or disconnection.

    Instead use e.g. g_input_stream_read in a loop.