Need help implementing simple socket server using GIOService (GLib, Glib-GIO) - c

I'm learning the basics of writing a simple, efficient socket server using GLib. I'm experimenting with GSocketService. So far I can only seem to accept connections but then they are immediately closed. From the docs I can't figure out what step I am missing. I'm hoping someone can shed some light on this for me.
When running the following:
# 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.
Output from the server:
# ./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
Current code:
/*
* 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);
}

The GSocketConnection has to be ref'ed in the incoming callback, this will keep the connection alive. You can pass it to a data structure, a class, or as user_data to the watch callback.
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;
}
You are not returning in the watch callback network_read(), you must end it with "return true". From the documentation: "the function should return FALSE if the event source should be removed".
The 100% CPU is caused by the fact that at the time the connection is closed the channel is still alive. Make sure to properly remove the event source when no longer needed.
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;
}

It's not documented in the GSocketService docs (I had to go through the GLib sources to find it), but the routine that calls the callback (new_connection in this case) *does a g_object_unref() on the connection object* after it returns. This effectively closes the connection immediately new_connection() returns to it.
I have no idea why it does this, but the solution is to add a g_object_ref() on entering the callback:
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));
Without that addition, polling the file descriptor in the main loop just returned POLLNVAL because the connection had been closed. In the absence of a handler for that result, it did that continuously -- and that's what caused the 100% CPU load.

From the GIO docs :
The GIOStream object owns the input and the output streams, not the other way around, so keeping the substreams alive will not keep the GIOStream object alive. If the GIOStream object is freed it will be closed, thus closing the substream, so even if the substreams stay alive they will always just return a G_IO_ERROR_CLOSED for all operations.

Related

Get IP address from sender using GIOChannel with GTK+

I am writing a C program using GTK+ on a raspberry pi.
There is a device coupled to it via the Ethernet port (all UDP communication).
This device sends a heartbeat to the pi , which I'm able to receive.
Now I need to send something back to the device. Therefore I need to know its IP address.
I want my program to autodetect the IP address from the message, but I have no clue how to get it.
Here is the code to setup the socket:
gd->gXctlServer = G_SOCKET_ADDRESS(g_inet_socket_address_new(g_inet_address_new_any(G_SOCKET_FAMILY_IPV4),XCTLPORT));
gd->gXctlSocket = g_socket_new(G_SOCKET_FAMILY_IPV4,G_SOCKET_TYPE_DATAGRAM,G_SOCKET_PROTOCOL_UDP,&err);
if (gd->gXctlSocket == NULL)
{
g_print("Error creating Xctl socket: %s\n",err->message);
g_assert(err == NULL);
}
if (g_socket_bind(gd->gXctlSocket,gd->gXctlServer,TRUE,&err) == FALSE)
{
g_print("Error binding Xctl socket:%s\n",err->message);
g_assert(err == NULL);
}
// add receiver watch for Xctl messages:
gd->xctlChannel = g_io_channel_unix_new(g_socket_get_fd(gd->gXctlSocket));
// set channel encoding to NULL for binary data:
g_io_channel_set_encoding(gd->xctlChannel,NULL,&err);
if (err != NULL) g_print("error setting encoding: %s\n",err->message);
gd->xctlRrcvEvent = g_io_add_watch(gd->xctlChannel,G_IO_IN,(GIOFunc) xctlreceiver, NULL);
g_io_channel_unref(gd->xctlChannel);
gd is a pointer to some global data, among which the server, socket and channel
Here is the code of my listener:
// this function listens to all Xctl messages
static gboolean xctlreceiver(GIOChannel *channel, GIOCondition condition, gpointer data)
{
char buf[1024];
gsize read;
GError *err = NULL;
g_print("xctlreceiver\n");
if (condition & G_IO_HUP) return FALSE;
g_io_channel_read_chars(channel,buf,sizeof(buf),&read,&err);
if (err != NULL) g_print("error receiving xctl: %s\n",err->message);
g_print("received %d bytes\n",read);
if (isXtouchHeartbeat(buf,read))
{
g_print("received heartbeat from X-touch\n");
}
return TRUE;
}
The receiver is working and indicates that the heartbeat is received.
But the question is: How do I get the IP address of the device that sent me the heartbeat.
Can someone please help me out here?
Thanks,
Bart.
EDIT: The solution may be simpler than I thought.
Maybe I do not need to know the IP address of the sender. If I use the function g_io_channel_write_chars and I use the same channel as in the g_io_channel_read_chars function, it might work.
I will try this and will come back to it later.
I found a solution to my problem
My receiver function now looks like this:
static gboolean xctlreceiver(GIOChannel *channel, GIOCondition condition, gpointer data)
{
char buf[1024];
gsize read;
GError *err = NULL;
g_print("xctlreceiver\n");
GSocketAddress *addr;
if (condition & G_IO_HUP) return FALSE;
if (gd->connectedToController == -1)
{
read = g_socket_receive_from(gd->gXctlSocket,&gd->gXctlController,buf,sizeof(buf),NULL,&err);
g_print("received %d bytes from XCTL socket on address %s\n",read,g_inet_address_to_string(g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(gd->gXctlController))));
for (int i=0; i<read; i++)
{
g_print("0x%02X ",buf[i]);
}
g_print("\n");
if (isXtouchHeartbeat(buf,read))
{
g_print("received heartbeat from X-touch\n");
gd->connectedToController = 1;
sendXtouchHeartbeat(NULL);
g_timeout_add_seconds(2,sendXtouchHeartbeat,NULL);
}
}
else
{
g_io_channel_read_chars(channel,buf,sizeof(buf),&read,&err);
if (err != NULL) g_print("error receiving xctl: %s\n",err->message);
g_print("received %d bytes from XCTL channel\n",read);
if (gd->connectedToController == -1)
{
//g_print("X-touch found on ip address: %s\n",inet_ntoa(si_recv.sin_addr));
gd->connectedToController = 1;
}
}
return TRUE;
}
The function g_socket_receive_from gives me the address of the sender in the second argument.
I put it into my gd struct, for later use.
Hope this helps anyone with the same problem.
Kr,
Bart.

QDBus Works with my Server as Client but not GDbus

I wrote a simple DBus server in Linux using Qt's QDBus. It is a very small amount of code and the core of it is here:
InterfaceDescription::InterfaceDescription()
{
new ifadapter(this); // Cleans itself up
qDebug() << "Creating";
QDBusConnection dbus = QDBusConnection::sessionBus(); // Use session bus
dbus.registerObject("/mygatt",this); // Register object on the bus
dbus.registerService("com.my.gatt.interface"); // Expose interface to others
qDebug() << "Done creating";
}
QByteArray InterfaceDescription::read() {
qDebug() << "CALLING READ";
return QByteArray("HELLO");
}
I then wrote a small DBus client in Linux also using Qt's QDBus. It works great and I can successfully communicate from this client to my server. Client code:
#include <QCoreApplication>
#include "clientIf.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
clientIf* client = new clientIf("com.my.gatt.interface", "/mygatt", QDBusConnection::sessionBus(), 0);
qDebug() << "Sending Read() command over Dbus to server...";
client->read();
qDebug() << "Done sending read command...";
return a.exec();
}
No I am trying to use GDBus to implement the client. So far I have this:
#include <stdbool.h>
#include <stdio.h>
#include <glib/gprintf.h>
#include <gio/gio.h>
void test_Echo(GDBusProxy *proxy)
{
GVariant *result;
GError *error = NULL;
const gchar *str;
g_printf("Calling read...\n");
result = g_dbus_proxy_call_sync(proxy,
"read",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error(error);
g_variant_get(result, "(&s)", &str);
g_printf("The server answered: '%s'\n", str);
g_variant_unref(result);
}
void test_Quit(GDBusProxy *proxy)
{
GVariant *result;
GError *error = NULL;
g_printf("Calling method Quit()...\n");
result = g_dbus_proxy_call_sync(proxy,
"Quit",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_assert_no_error(error);
g_variant_unref(result);
}
int main(void)
{
GDBusProxy *proxy;
GDBusConnection *conn;
GError *error = NULL;
const char *version;
GVariant *variant;
conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error(error);
proxy = g_dbus_proxy_new_sync(conn,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* GDBusInterfaceInfo */
"com.my.gatt.interface", /* name */
"/mygatt", /* object path */
"com.my.gatt.interface", /* interface */
NULL, /* GCancellable */
&error);
g_assert_no_error(error);
/* Test all server methods */
test_Echo(proxy);
test_Quit(proxy);
g_object_unref(proxy);
g_object_unref(conn);
return 0;
}
When I run this code, it does not work like the QDBus does, it errors with the following error:
ERROR:../dbustester/main.cpp:29:void test_Echo(GDBusProxy*): assertion failed (error == NULL): GDBus.Error:org.freedesktop.DBus.Error.UnknownInterface: No such interface 'com.my.gatt.interface' at object path '/mygatt' (g-dbus-error-quark, 42)
Calling read...
Aborted
So QDBus works with the server, but GDBus does not. What am I doing wrong?
Figured it out, QDBus generates the interface with a strange name, so my Interface Name was specified wrong. I used the gdbus tool to figure this out.

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:
Each incoming connection will be handled by an individual new thread
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.
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?
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.

Glib/Gio Asynchronous or Threaded UDP Server

I have currently a synchronous UDP application receiving messages.
The code :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include <gio/gio.h>
int main(argc,argv)
int argc;
char ** argv;{
char buf[256], *ptr, sep[] = "| ";
GError * error = NULL;
GSocket * socket;
GSocketAddress *gsockAddr, *gfromAddr;
guint16 udp_port = 1500;
//Creates socket udp ipv4
socket = g_socket_new(G_SOCKET_FAMILY_IPV4,
G_SOCKET_TYPE_DATAGRAM,
G_SOCKET_PROTOCOL_UDP,
&error);
g_assert(error == NULL);
if (socket == NULL) {
g_print("ERROR");
exit(1);
}
//sockaddr struct like
gsockAddr = G_SOCKET_ADDRESS(g_inet_socket_address_new(g_inet_address_new_any(G_SOCKET_FAMILY_IPV4), udp_port));
if(gsockAddr == NULL){
g_error("Error socket\n");
exit(1);
}
//
if (g_socket_bind (socket, gsockAddr, TRUE, NULL) == FALSE){
g_print("Error bind\n");
exit(1);
}
int bytes = g_socket_receive_from (socket,
&gfromAddr,
buf,
255,
NULL,
&error);
if (bytes == -1) {
g_warning ("Failed to receive from socket: %s", error->message);
g_error_free (error);
return TRUE;
}
g_message("Server receive: %s", buf);
guint16 port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(gfromAddr));
g_print("...from %s(%d)\n",g_inet_address_to_string(g_inet_socket_address_get_address(G_INET_SO CKET_ADDRESS(gfromAddr))), (int) port);
exit(0);
}
So, I want to make the receive operation, non-blocking instead of blocking. I want to make it either ansynchronous, or/and threaded so that, meanwhile, I could do other operations related to the application I want to develop.
But I did not suceed to make it like I want. I tried to use GLib IO Channels, but I can not make it works. The processus is waiting, but only because of the Main Loop (I can not telnet the application).
The code :
#include <gio/gio.h>
#include <glib.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BLOCK_SIZE 1024
static gboolean
gio_read_socket (GIOChannel *channel,
GIOCondition condition,
gpointer data)
{
char buf[1024];
gsize bytes_read;
GError *error = NULL;
if (condition & G_IO_HUP) return FALSE; /* this channel is done */
g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read,
&error);
g_assert (error == NULL);
buf[bytes_read] = '\0';
g_print ("%s", buf);
return TRUE;
}
int
main (int argc, char **argv)
{
GSocket * s_udp;
GError *err = NULL;
guint16 udp_port = 5556;
s_udp = g_socket_new(G_SOCKET_FAMILY_IPV4,
G_SOCKET_TYPE_DATAGRAM,
G_SOCKET_PROTOCOL_UDP,
&err);
g_assert(err == NULL);
if (s_udp == NULL) {
g_print("ERROR");
exit(1);
}
g_socket_bind(s_udp,
G_SOCKET_ADDRESS(g_inet_socket_address_new(g_inet_address_new_any(G_SOCKET_FAMILY_IPV4), udp_port)),
TRUE,
&err);
g_assert(err == NULL);
int fd = g_socket_get_fd(s_udp);
GIOChannel* channel = g_io_channel_unix_new(fd);
guint source = g_io_add_watch(channel, G_IO_IN,
(GIOFunc) gio_read_socket, NULL);
g_io_channel_unref(channel);
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
g_main_loop_unref(loop);
}
I am quite a beginner with GLib/Gio, and I think I am doing wrong with the IO Channels. I would like to add it to the main loop as an event, so that I could use my callback function. Maybe there is a simpler way to do that.
Besides, I have a TCP asynchronous and threaded server that is working, but I did not find how to do the same with UDP (using a GThreadedSocketService and creating a socket listener, then adding the service to the main loop. Easy as pie with TCP).
Do you have any idea how to proceed ? If you know how to do but only with the basic API socket, I still take it ! Thanks.
I figure it out.
I am indeed quite a beginner. Because, when I wanted to test my udp application (the second code block), I used telnet to connect to it and try to send messages. However, we can not telnet udp applications of course...
So I tried with a simple udp sender (I used Glib/Gio for it by the way) instead of telnet and it worked, perfectly non-blocking and reusable. I did make some changes but basically, it is the same. I put an idle function to show you how non-blocking it is, whether this can help someone one day.
My simple Glib/Gio UDP app, non blocking :
#include <gio/gio.h>
#include <glib.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BLOCK_SIZE 1024
static gboolean
gio_read_socket (GIOChannel *channel,
GIOCondition condition,
gpointer data)
{
char buf[1024];
gsize bytes_read;
GError *error = NULL;
if (condition & G_IO_HUP) return FALSE; /* this channel is done */
g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read,
&error);
g_assert (error == NULL);
buf[bytes_read] = '\0';
g_print ("%s", buf);
int *a = data;
*a = *a + 1;
return TRUE;
}
gboolean
idleCpt (gpointer user_data){
int *a = user_data;
g_print("%d\n", *a);
sleep(1);
return TRUE;
}
int
main (int argc, char **argv)
{
GSocket * s_udp;
GError *err = NULL;
int idIdle = -1, dataI = 0;
guint16 udp_port = 1505;
GSocketAddress * gsockAddr = G_SOCKET_ADDRESS(g_inet_socket_address_new(g_inet_address_new_any(G_SOCKET_FAMILY_IPV4), udp_port));
s_udp = g_socket_new(G_SOCKET_FAMILY_IPV4,
G_SOCKET_TYPE_DATAGRAM,
G_SOCKET_PROTOCOL_UDP,
&err);
g_assert(err == NULL);
if (s_udp == NULL) {
g_print("ERREUR");
exit(1);
}
if (g_socket_bind (s_udp, gsockAddr, TRUE, NULL) == FALSE){
g_print("Erreur bind\n");
exit(1);
}
g_assert(err == NULL);
int fd = g_socket_get_fd(s_udp);
GIOChannel* channel = g_io_channel_unix_new(fd);
guint source = g_io_add_watch(channel, G_IO_IN,
(GIOFunc) gio_read_socket, &dataI);
g_io_channel_unref(channel);
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
idIdle = g_idle_add(idleCpt, &dataI);
g_main_loop_run(loop);
}
The code is not perfect, there is a lot of optimisations to make, but we can do nice things from that I think. If you want to see my udp sender, just ask.

Receiving garbage from GSocket

I am using the below functions to create and open a GSocket and then listen for UDP messages to display in a GTK_ENTRY field. The open_listen_socket function is called, and then after that I have a function on a timeout calling the get_incoming_messages function every second or so.
The problem is that when I launch the program, I only get a bunch of garbage characters showing up in the GTK_ENTRY field, and my console is repeating an error message "Pango-WARNING: Invalid UTF-8 string passed to pango_layout_set_text()."
I've been able to send UDP messages no problem, but am going crazy trying to figure out receiving them, even though it feels like it shouldn't be so difficult!
Thank you for any help, here are my functions:
static void open_listen_socket()
{
GInetAddress *localAddress;
GSocketAddress *localSocketAddress;
localAddress = g_inet_address_new_from_string("127.0.0.1");
guint16 listenPort = atoi(gtk_entry_get_text (GTK_ENTRY (listenPortField)));
localSocketAddress = g_inet_socket_address_new(localAddress, listenPort);
listenSocket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, 17, NULL);
g_socket_bind (listenSocket, localSocketAddress, FALSE, NULL);
g_socket_set_blocking(listenSocket, FALSE);
g_socket_listen (listenSocket, NULL);
}
static int get_incoming_message()
{
gchar buffer[1024];
int input_length;
input_length = g_socket_receive(listenSocket, (gchar *)buffer, 1024, NULL, NULL);
gtk_entry_set_text (GTK_ENTRY (current_status_message_box), (const gchar *)buffer);
return 1;
}
Also, if it helps at all, I had to add the "return 1;" at the end of get_incoming_message, because without it that process seemed to get stuck inside that function even though the rest of the application continued working ok.
/EDIT AND UPDATE BELOW*/
Ok, using the advice below I have things working, but am hitting a new problem. It appears that my call to g_socket_receive returns true every single iteration, printing an empty line if no message has been sent. That means that I do see my sent messages come through, but they disappear from the GTK_ENTRY a split second later as they are replaced by an empty line on the next iteration.
static gboolean get_incoming_message()
{
gchar buffer[10] = {0};
GError *err = NULL;
if (g_socket_receive(listenSocket, (gchar *)buffer, 10, NULL, &err) > 0 );
{
printf("\n%s", buffer);
gtk_entry_set_text (GTK_ENTRY (current_status_message_box), (const gchar *)buffer);
return TRUE;
}
return FALSE;
}
I don't know what checks to use to tell the difference between an actual message and these empty lines! It's almost as if this socket is receiving an endless stream of empty data anytime a legitimate message is not being sent. Does that make sense?
/Figured it out!/
I had to add an ELSE statement to give it something else to do when there was no data, to prevent it from trying to write the empty line. Not sure I understand that, so I'd love an explanation if anyone has one, but I'm back in business! Thanks again to everyone!
There are few pointers for your consideration:
Whenever function provides facility to use GError please use it. It is very helpful to find out error message
Check return value of the functions to make sure they have done what they were intended to (Already pointed out by Joachim)
Use enum when available. In this case GSocketProtocol. Don't use 17 use G_SOCKET_PROTOCOL_UDP instead, as your application will break if the enum value changes
UDP sockets don't really "listen" so g_socket_listen call is redundant
Please find below sample code based on what you have posted at your disposal:
/* gcc -Wall -Wextra `pkg-config --cflags --libs glib-2.0 gio-2.0` gsock.c -o gsock */
#include <glib.h>
#include <gio/gio.h>
#include <stdio.h>
#define MAX_RECV 10
#define TIMEOUT_INTERVAL 1
static GSocket *listenSocket;
static GMainLoop *loop;
static gboolean
recv_msg(gpointer data)
{
(void)data;
static unsigned int count = 0;
gchar buffer[1024] ={0};
GError *err = NULL;
if( g_socket_receive(listenSocket, (gchar *)buffer, 1024, NULL, &err) > 0 )
{
printf("buff = %s\n", buffer);
}
else
{
printf(" Nothing posted in last %d sec (Error: %s)\n", TIMEOUT_INTERVAL, err->message);
g_error_free(err);
}
if(count++ < MAX_RECV)
return TRUE;
/* This is fugly!! :\ */
g_main_loop_quit(loop);
return FALSE;
}
static int
open_listen_socket(void)
{
GInetAddress *localAddress;
GSocketAddress *localSocketAddress;
GError *err = NULL; /* This is mandatory */
localAddress = g_inet_address_new_from_string("127.0.0.1");
guint16 listenPort = 31337; /* Can you recongnize this port? xD*/
localSocketAddress = g_inet_socket_address_new(localAddress, listenPort);
listenSocket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err);
if( NULL == listenSocket)
{
printf("\n Failed to create socket! Error: %s\n", err->message);
g_error_free(err);
return -1;
}
if( FALSE == g_socket_bind (listenSocket, localSocketAddress, FALSE, &err))
{
printf("\n Failed to bind! Error: %s\n", err->message);
g_error_free(err);
return -1;
}
g_socket_set_blocking(listenSocket, FALSE);
/* UDP socket don't "listen". Uncomment below to see the error message*/
/*
if(FALSE == g_socket_listen (listenSocket, &err))
{
printf("\n Failed to listen! Error: %s\n", err->message);
g_error_free(err);
}
*/
return 0;
}
int main(void)
{
g_type_init();
if(open_listen_socket() < 0)
{
printf("\n Socket creation went wrong!!\n");
}
else
{
loop = g_main_loop_new (NULL, TRUE);
g_timeout_add_seconds(TIMEOUT_INTERVAL, recv_msg, NULL);
g_main_loop_run(loop);
}
return 0;
}
Sample run:
Terminal #1:
$ ./gsock
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
buff = hello
buff = world
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Nothing posted in last 1 sec (Error: Error receiving data: Resource temporarily unavailable)
Terminal #2:
$ nc 127.0.0.1 31337 -u
hello
world
^C
Hope this helps!
Check the line where you receive data again... Which socket do you read from? The listening socket! That socket is only used to accept new connections (via the g_socket_accept) function.
g_socket_accept returns a new socket (that you also have to make non-blocking), that can be used for reading and sending.
Also, you should really check the return value from g_socket_receive! In this case I can almost bet it's returning -1 for an error, but you "print" the buffer anyway which can contain anything.

Resources