Gtk_Main() and Unix Sockets

gtk_main() and unix sockets

You are facing the problem that you have several event systems at once but only one thread. Gtk+ comes with its own event handler, that eventually boils down to a select() which will wake up on any user input or other gtk event. You yourself want to handle networking with your own event handling, which typically consists of a select() on your socket(s) or using the sockets in blocking mode.

One solution is to integrate your events into the event loop of Gtk+.

You can make Gtk+ watch/select() your sockets and call a specific function when their state changes (data readable).
See the section "Creating new source types" on http://developer.gnome.org/glib/2.30/glib-The-Main-Event-Loop.html

Another solution would be to use Gtk+ networking functionality.

Typically you don't want to do something so special with the sockets that it is not easily wrapable with Glib IO Channels. See http://developer.gnome.org/glib/2.30/glib-IO-Channels.html

A third solution is to start a second thread that handles your networking, e.g. with posix threads or Gtk+ threading functionality.

Separating GUI from the worker part of your application is in general a good idea. However for a chat application, it probably does not give any benefit over the other solutions. See http://developer.gnome.org/glib/2.30/glib-Threads.html

Safety of using pthreads in Gtk+2.0 application

According to the documentation, the main even loop can accept sources from different threads:

A GMainContext can only be running in a single thread, but sources can
be added to it and removed from it from other threads.

So you can inject the code in the UI thread from your working thread by:

  1. creating a GSource (e.g., by using g_idle_source_new);
  2. adding the code you want to be executed with g-source-set-callback;
  3. attaching it to the UI thread context with g_source_attach().

Here is a sample program for GTK+2 and GLib >= 2.32:

#include <gtk/gtk.h>

#define N_THREADS 100
#define N_ITERATIONS 100

GtkWidget *bar;
GMainContext *context;

static gboolean
update_progress_bar(gpointer user_data)
{
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(bar),
g_random_double_range(0, 1));
return G_SOURCE_REMOVE;
}

static gpointer
thread_func(gpointer user_data)
{
int n_thread = GPOINTER_TO_INT(user_data);
int n;
GSource *source;

g_print("Starting thread %d\n", n_thread);

for (n = 0; n < N_ITERATIONS; ++n) {
/* If you want to see anything you should add a delay
* to let the main loop update the UI, e.g.:
* g_usleep(g_random_int_range(1234, 567890));
*/
source = g_idle_source_new();
g_source_set_callback(source, update_progress_bar, NULL, NULL);
g_source_attach(source, context);
g_source_unref(source);
}

g_print("Ending thread %d\n", n_thread);
return NULL;
}

gint
main(gint argc, gchar *argv[])
{
GtkWidget *window;
GThread *thread[N_THREADS];
int n;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

bar = gtk_progress_bar_new();
gtk_container_add(GTK_CONTAINER(window), bar);

context = g_main_context_default();

for (n = 0; n < N_THREADS; ++n)
thread[n] = g_thread_new(NULL, thread_func, GINT_TO_POINTER(n));

gtk_widget_show_all(window);
gtk_main();

for (n = 0; n < N_THREADS; ++n)
g_thread_join(thread[n]);

return 0;
}

g_io_channel + socket = client, and GIO not work properly

well last night I brainstormed myself with my own heart, and finally could make these thing work,

here the proper code

#include <stdio.h>
#include <gio/gio.h> // g_timeout_add
#include <gtk/gtk.h> // gtk
#include <netinet/in.h> //sockaddr_in
#include <sys/socket.h> // socket();
#include <arpa/inet.h> // inet_addr();
#include <string.h> // memset();
#include <fcntl.h>
#include <stdlib.h>

struct dada
{
gint id_sock;
guint id_gio_connect;
guint id_gio_watch;
};

gboolean readdata(GIOChannel *chan,GIOCondition condition, struct dada *didi)
{

gchar dada[20] = {0};
int dadaz =0;

if( condition != G_IO_IN )
return FALSE;

if(dadaz = recv(g_io_channel_unix_get_fd(chan),dada,19,0)<=0)
{
perror("recv");
close(didi->id_sock);
g_source_remove(didi->id_gio_connect);
g_source_remove(didi->id_gio_watch);
exit(0);
return FALSE;
}

printf("data in : %s\n",dada);

return TRUE;
}

gboolean incoming(GIOChannel *chan, GIOCondition condition, struct dada *didi )
{

if( condition & G_IO_ERR || condition & G_IO_HUP )
return FALSE;

didi->id_gio_watch = g_io_add_watch(chan,G_IO_IN | G_IO_ERR | G_IO_HUP,(GIOFunc)readdata,didi);

return FALSE;
}

// gtk area

void hello(GtkWidget *widget, gpointer data)
{
g_print("Haii world %s\n", (char *)data);

}

gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
g_print("a delete event has been occured properly :D\n");

return(0);
}

void destroy(GtkWidget * widget, gpointer data)
{
gtk_main_quit();
}
// end of gtk area

int main(int argc, char **argv)
{

//gtk bussines from here
GtkWidget *window;
GtkWidget *button;

gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL);
gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy), NULL);
gtk_container_set_border_width(GTK_CONTAINER(window),10);

button = gtk_button_new_with_label("ohayo");

gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(hello), (gpointer)"hha" );
gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));

gtk_container_add(GTK_CONTAINER(window),button);

gtk_widget_show(button);
gtk_widget_show(window);

//gtk bussiness done here...

// network code //
struct dada didi;
memset(&didi,0,sizeof(didi));

struct sockaddr_in your; // set my network device info
gint rootsock; // handle the root socket

//socket
rootsock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

memset(&your,0,sizeof(your));

printf("sock : %d\n",rootsock);

your.sin_family = AF_INET;
your.sin_addr.s_addr = inet_addr("127.0.0.1");
your.sin_port = htons(1111);

connect(rootsock,(struct sockaddr*)&your,sizeof(your));

didi.id_sock = rootsock;
didi.id_gio_connect = g_io_add_watch(g_io_channel_unix_new(didi.id_sock),G_IO_IN | G_IO_OUT | G_IO_ERR | G_IO_HUP,(GIOFunc)incoming,&didi);

// network code //

gtk_main();

return 0;

}

and the only different had made is the "worked" code , need :

- gio for connecting, and gio for watching

instead of be "non-worked" code ( only gio for connecting ), but again i just wondering "why", why on connect() need two gio (recursively) in order make these "gio things" work,

these is really odd, if I see back then on g_io_channel + socket = server , still just get one client ? in C language

which on accept() just need only one gio and only for watching.

if someone could explain these, how great it'll be :)

How g_main_loop works in gtk programming?

I recommend reading the documentation, starting with the linked-to function g_main_loop_new().

Basically, letting glib "own" your application's main loop makes it easier to support things like "pluggable" event sources; where your application listens to both (for example) events coming from GTK+ widgets, and a network socket or Unix pipe. These are things that need to be hooked together at a fairly low level in an application's main loop, and letting glib own those parts makes it easier.

There are glib data structures (like IO channels) that are compatible with the main loop and allow you to add things to the set of inputs the loop manages. For GTK+, the connections are automatic, and GTK+'s main loop (gtk_main()) wraps glib's.

Linux best way in two-way IPC in C

There are a number of different ways to implement IPC. For a good comparison, see Stevens' books. The classic is 'Advanced Programming in the UNIX environment', but there is also 'UNIX Network Programming, Volume 2, Second Edition: Interprocess Communications'. I know it is sometimes not considered good form to point to references elsewhere, but whether this is an academic or commercial problem, most UNIX programmers would recognise Stevens as an invaluable resource.

That said, here are your main options for IPC:

  1. Use a pipe() between the processes. The format will always be stream-based; if you are sending datastructures, this can be a pain as you need to worry not only about serialization, but also about buffering and translating the 'packets' back into messages. Pipes are unidirectional, so you will need two for bidirectional communication.

  2. Use a named pipe or fifo. This allows many-to-one communication and for the fifo to persist after one end has quit. Otherwise as per (1).

  3. Use a socketpair between the processes - specifically a unix domain socket. Sockets are bidirectional. You can either use streaming sockets (SOL_STREAM), datagrams (unreliable, ordering not guaranteed - SOCK_DGRAM) or perhaps better sequenced reliable bidirectional packet communication (SOCK_SEQPACKET). Packet based communication means that you can (e.g.) put one datastructure in each packet.

  4. Use signals. Effectively you get to send one integer at a time. Signals do not mix well with threading, handling interrupted system calls is hard, and various race conditions make them unreliable unless you know what you are doing and are not too worried about portability. Best avoided in most cases.

  5. Use system V semaphores (semget etc.) or POSIX semaphores (sem_open etc.). Useful for sending signals between processes to achieve synchronization but not much more.

  6. Use shared memory (shmget etc.) - the same page(s) are made visible to multiple processes. You will need to combine with some method of synchronisation.

  7. System V message queues (msgget etc.) - maintain a queue of packets (messages) between two processes.

  8. Some combination of the above.

I've omitted some things only in forks of the kernel (e.g. Binder) or under development (e.g. KDBus).

Examples of and tutorials for nearly all the above can be found here.

Now, most of these could be used for the application you mention. It looks like you want to send variable size messages, so if you use a stream based protocol, the normal hack is to send the length of the packet as the first 1, 2 or 4 bytes (depending on the maximum length). Packet based protocols are easier here (obviously) but each have their own maximum packet size. Do you care about reliability? Do you care about portability? Do you care about speed? All of these are valid concerns when choosing between them.

Finally, an advantage to the FD-based methods (e.g. pipes, socketpairs) is that you can add them to a normal select() loop; if you have other things going on in your program, this may be helpful.

You asked in comments for some examples of socketpair code. I reiterate the comment at the top re getting hold of Stephens. In the absence of that:

  • Socketpair() in C/Unix shows a good example of setting up a socketpair for IPC using fork().
  • The tutorial mentioned above has a good section on socketpair() here.

gtk_window_set_icon_from_file() not working properly

The documentation for gtk_window_set_icon() and friends says,

This icon is used when the window is minimized (also known as iconified). Some window managers or desktop environments may also place it in the window frame, or display it in other contexts.

So it not required that the icon is displayed in the title bar; that is at the discretion of the window manager in your desktop environment. The default window managers in both Gnome and Unity currently do not do so.



Related Topics



Leave a reply



Submit