Using GIOChannel

GIOChannel provides a portable method for using file descriptors, sockets and pipes and integrating them into the GLib main loop.

The g_io_channel_unix_new() API takes a file descriptor as an input parameter and is used to create a new GIOChannel. Alternatively, the g_io_channel_new_file() API can be used to create a channel for a file. Once the channel is created, it can be used in a generic manner.

To read a channel, APIs such as the following can be used:

  • g_io_channel_read_chars()

  • g_io_channel_read_line()

  • g_io_channel_read_to_end()

To write to a channel an API like g_io_channel_write_chars() is used.

To set the position in the current GIOChannel, g_io_channel_seek_position() can be used.

Finally the channel can be closed using g_io_channel_shutdown().

The following is an example code where a file descriptor to a plain file is used to create a GIOChannel, and then the contents of the file are read in chunks of 100 bytes and printed using g_print. Finally the channel is closed.

#include <glib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	GIOChannel *channel;
	gchar buf[100];
	gsize bytes_read;
	FILE *fp;
	
	if(argc != 2)
	{
		g_print("usage:cat <file_name>\n");
		g_print("Press any key to exit\n");
		getchar();
		return 1;
	}
	
	fp = fopen(argv[1],"r");
	
	if(!fp)
	{
		g_print("Unable to open the file %s\n",argv[1]);
		return 1;
	}
		
	channel = g_io_channel_unix_new(fileno(fp));
	
 	do
 	{
		g_io_channel_read_chars(channel,buf,100,&bytes_read,NULL);
		g_print("%s",buf);
		
	}
	while(bytes_read > 0);
	
	g_io_channel_shutdown(channel,TRUE,NULL);
	
	fclose(fp);
 
	return 0;
}
 

In the example below, a sample code to copy a file is written. In the code, channels are opened using g_io_channel_new_file(). The contents of the file are read using g_io_channel_read_chars(), and the contents are written to a new file using g_io_channel_write_chars().

#include <glib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	GIOChannel *in_channel,*out_channel;
	gchar buf[100];
	gsize bytes_read,bytes_written;
	GError *error = NULL;
	
	if(argc != 3)
	{
		g_print("usage:<cp SOURCE> <DESTINATION>\n");
		g_print("Press any key to exit\n");
		getchar();
		return 1;
	}
		
	in_channel = g_io_channel_new_file(argv[1],"r",&error);
	
	if(!in_channel)
	{
		g_print("Unable to open the file %s to read\n",argv[1]);
		g_print("Press any key to exit\n");
		getchar();
		return 1;
	}
	
	out_channel = g_io_channel_new_file(argv[2],"w",&error);
	
	if(!out_channel)
	{
		g_print("Unable to open the file %s to write\n",argv[2]);
		g_print("Press any key to exit\n");
		getchar();
		return 1;
	}
	
	do
	{
		g_io_channel_read_chars(in_channel,buf,100,&bytes_read,&error);
		if(error)
		{
			g_print("Error while reading file %s\n",argv[1]);
			g_print("Press any key to exit\n");
			getchar();
			return 1;
		}
		
		g_io_channel_write_chars(out_channel,buf,bytes_read,&bytes_written,&error);
		if(error)
		{
			g_print("Error while writing file %s\n",argv[2]);
			g_print("Press any key to exit\n");
			getchar();
			return 1;
		}
	}
	while(bytes_read > 0);
	
       g_io_channel_shutdown(in_channel,TRUE,&error);
	if(error)
	{
		g_print("Error has occured\n");
		g_print("Press any key to exit\n");
		getchar();
		return 1;
	}
	 
	g_io_channel_shutdown(out_channel,TRUE,&error);
 	if(error)
	{
		g_print("Error has occured\n");
		g_print("Press any key to exit\n");
		getchar();
		return 1;
	}
	
	g_print("File copied successfully...\n");
	getchar();
	
	return 0;
}

Using GIOChannel in GLib main loops

GIOChannel provides a way to integrate file descriptors to main loops. The following code demonstrates the usage. In the code, a pipe is created. A thread is created which writes to the write end of the pipe. The main loop checks if the other end of the pipe is ready to read. If it is, then the callback function is called. The callback calls g_main_loop_quit(), and the main loop is terminated and g_main_loop_run() returns.

#include <glib.h>
#include <stdio.h>

int fd[2];

void *thread_function(void *data)
{
	// call sleep so that the main loop source is not ready immediately
	sleep(10);
	
	write(fd[1],"GIOChannel main loop example",29);
	return NULL;
}

gboolean my_callback(GIOChannel *source,GIOCondition condition,gpointer data)
{
	char buf[100];	
	
	read(fd[0],buf,sizeof(buf));
	g_print("%s",buf);
	
	getchar();
	
	g_main_loop_quit((GMainLoop *)data);
	
	return FALSE;
}

int main()
{
	GMainLoop *loop;
	GIOChannel *channel;
	
	pipe(fd);
	
	channel = g_io_channel_unix_new(fd[0]);
	
	g_thread_init(NULL);
	g_thread_create(thread_function,NULL,TRUE,NULL);
	
	loop = g_main_loop_new(NULL,FALSE);
	g_io_add_watch(channel,G_IO_IN | G_IO_HUP | G_IO_ERR,(GIOFunc)my_callback,loop);
	g_main_loop_run(loop);
	
	g_io_channel_shutdown(channel,TRUE,NULL);

	return 0;
}