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.
Related
My goal: Is to monitor the state of my network interface (mainly wireless) from my firmware (in C) by monitoring the wpa_supplicant through the D-Bus interfaces. I would like to stick with C and low-level API of D-bus.
What I have so far
I've written a small program in C, copied most of the code as is from this SO user.
I've gone through all possible tutorials on D-Bus and wpa_supplicant
My program compiles and works properly. However it does not produce the expected output.
Here's my code:
#include <stdio.h>
#include <dbus/dbus.h>
#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant"
#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicantAAA"
#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces"
#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interfaces"
#define WPAS_DBUS_NETWORKS_PART "Networks"
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
#define WPAS_DBUS_BSSIDS_PART "BSSIDs"
#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID"
int ret;
char signalDesc[1024]; // Signal description as string
// Signal handling
signal(SIGKILL, stopLoop);
signal(SIGTERM, stopLoop);
void loop(DBusConnection* conn)
{
DBusMessage* msg;
DBusMessageIter args;
DBusMessageIter subArgs;
int argType;
int i;
int buffSize = 1024;
char strValue[buffSize];
const char* member = 0;
while (1)
{
// non blocking read of the next available message
dbus_connection_read_write(conn, 0);
msg = dbus_connection_pop_message(conn);
// loop again if we haven't read a message
if (!msg)
{
printf("No message received, waiting a little ...\n");
sleep(1);
continue;
}
else printf("Got a message, will analyze it ...\n");
// Print the message member
printf("Got message for interface %s\n",
dbus_message_get_interface(msg));
member = dbus_message_get_member(msg);
if(member) printf("Got message member %s\n", member);
// Check has argument
if (!dbus_message_iter_init(msg, &args))
{
printf("Message has no argument\n");
continue;
}
else
{
// Go through arguments
while(1)
{
argType = dbus_message_iter_get_arg_type(&args);
if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
char* str = NULL;
dbus_message_iter_get_basic(&args, &str);
printf("Received string: \n %s \n",str);
}
else
printf("Arg type not implemented yet !\n");
if(dbus_message_iter_has_next(&args))
dbus_message_iter_next(&args);
else break;
}
printf("No more arguments!\n");
}
// free the message
dbus_message_unref(msg);
}
}
int main()
{
DBusConnection *connection;
DBusError error;
char *name = "org.share.linux";
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if ( dbus_error_is_set(&error) )
{
printf("Error connecting to the daemon bus: %s",error.message);
dbus_error_free(&error);
return 1;
}
// request a name on the bus
ret = dbus_bus_request_name(connection, WPAS_DBUS_SERVICE, 0, &error);
if (dbus_error_is_set(&error))
{
printf(stderr, "Name Error (%s)\n", error.message);
dbus_error_free(&error);
}
/* Connect to signal */
// Interface signal ..
printf(signalDesc, "type='signal',interface='%s'",WPAS_DBUS_IFACE_INTERFACE);
dbus_bus_add_match(connection, signalDesc, &error);
dbus_connection_flush(connection);
if (dbus_error_is_set(&error))
{
fprintf(stderr, "Match Error (%s)\n", error.message);
return 1;
}
// Do main loop
loop(connection);
dbus_connection_close(connection);
return 0;
}
List of D-bus services on my BBB
Output
Some pointers
I would like to catch the signals as shown in the D-Bus API of wpa_supplicant.
Some things I would like to do -- see when a wireless interface say wlan0 is enabled, connects to access point etc. Also capability to set AP and stuff.
Its catching signal from other interfaces for which no match has been added.
I run this program and change the state of the networking interfaces but I dont get any signals. Also, I dont know if requesting name on the bus is necessary as I'm just listening.
What's the possible issue here? Any pointers will be really helpful.
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.
I am trying to set up a mailslot that i can write to and read from.
But when calling ReadFile to i get error 87. I have tried different approaces from MSDN and i still get error 87.
I have removed a lot of error handling in my code in order to shorten it a bit.
This are calls i do from main.
hMailslot= mailslotCreate("\\\\.\\mailslot\\myslot"); //works
hMailslot=mailslotConnect("\\\\.\\mailslot\\myslot"); //works
mailslotWrite(hMailslot,w, lstrlen(w)+1)*sizeof(CHAR); //works
mailslotRead(hMailslot); //Error 87 invalid parameter
mailslotClose(hMailslot); //?
Here is a shortened version of my code.
#define TIME_OUT MAILSLOT_WAIT_FOREVER
HANDLE mailslotCreate (char *name) {
HANDLE H = (HANDLE)CreateMailslot(name,0,TIME_OUT,(LPSECURITY_ATTRIBUTES) NULL);
return H;
}
HANDLE mailslotConnect (char * name) {
HANDLE H = CreateFile(name,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
return H;
}
int mailslotWrite(HANDLE mailSlot,void *msg,int msgSize) {
DWORD cbWritten;
WriteFile(mailSlot, msg, msgSize, &cbWritten, (LPOVERLAPPED) NULL);
return cbWritten;
}
int mailslotRead (HANDLE mailbox) {
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
LPTSTR Message;
fResult =GetMailslotInfo(mailbox,(LPDWORD) NULL,&cbMessage,&cMessage,(LPDWORD)NULL);
if (!fResult) //Works
{
printf("GetMailslotInfo failed with %d.\n", GetLastError());
}
Message = (LPTSTR) calloc(cbMessage,sizeof(char));
Message[0] = '\0';
fResult = ReadFile((HANDLE)mailbox,(LPVOID)Message,(DWORD)cbMessage,LPDWORD)&cbRead,(LPOVERLAPPED) NULL);
if (!fResult) //Error 87
{
printf("ReadFile failed with %d.\n", GetLastError());
free(Message);
return 0;
}
return cbRead;
}
int mailslotClose(HANDLE mailSlot){
return CloseHandle(mailSlot);
}
As per Microsoft, Error 87 is ERROR_INVALID_PARAMETER from ReadFile() or WriteFile()
So something is wrong with those parameters.
Also from MS: CreateMailSlot(), CreateFile() and ReadFile() definition
I've been looking at Using Mailslots and trying to compare it to what you have. About the only difference I can see is that even though they create the file with FILE_ATTRIBUTE_NORMAL, and write to it with (LPOVERLAPPED) NULL, they still supply an OVERLAPPED ov when reading the file - even though their documentation says that it is not required.
However I don't know enough to know if that is the actual issue
The problem I am having is specifically printing out the response of a dbus method call in C using the low level API. I am new to C's libdbus, but have done some work in python-dbus.
I know how to write dbus methods and method calls in python as well as the CLI
I can find code on the internet to invoke dbus methods, but they don't return or print out the response
I have been looking at the libdbus doxygen api, but cannot determine how to pull out the response.
The way I have my code set up, a python dbus daemon runs with methods I want to call. Some of them return a string. I want a C program to connect to the session bus, call the method, print out the reply and exit.
This is what I have currently:
#include <stdio.h>
#include <dbus/dbus.h>
static void send_dbus_message (DBusConnection *connection, const char *msg)
{
DBusMessage *message;
//initialize the message
message = dbus_message_new_signal ("/org/example/foo/bar",
"org.example.foo.bar",
msg);
//send the message
dbus_connection_send (connection, message, NULL);
//deallocate the message
dbus_message_unref (message);
}
int main (int argc, char **argv)
{
DBusConnection *connection;
DBusError error;
//init error message
dbus_error_init (&error);
connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
if (!connection)
{
printf ("Connection to D-BUS daemon failed: %s", error.message);
//deallocate error message
dbus_error_free (&error);
return 1;
}
send_dbus_message (connection, "HelloWorld");
return 0;
}
Can be synchronous or asynchronous.
You can use the method mentioned in http://www.matthew.ath.cx/misc/dbus to get a method reply message.
Once you have a dbus message you can use following method to extract the data.
To parse a dbus message, you need a argument iterator. Initalize it to read contents of the incoming message.
DBusMessageIter MsgIter;
dbus_message_iter_init(msg, &MsgIter);//msg is pointer to dbus message received
You have to validate the signature of the incoming message before reading it. Or you can also go for argument by argument verification. For example, if the argument type is string
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&MsgIter)){
char* str = NULL;
dbus_message_iter_get_basic(&MsgIter, &str);//this function is used to read basic dbus types like int, string etc.
}
For complex types, like structures, arrays, variants and dict entries, you have to create corresponding child iterators to parse contents of each complex element. Say, for a dbus signature ofs(i{ii}i)u, the extraction is done as below
//Parsing a signature s(i{ii}i)u
DBusMessageIter rootIter;
dbus_message_iter_init(msg, &rootIter);
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&rootIter))
{
char* str = NULL;
dbus_message_iter_get_basic(&rootIter, &str);//this function is used to read basic dbus types like int, string etc.
dbus_message_iter_next(&rootIter);//Go to next argument of root iter
//Block to enter and read structure
if (DBUS_TYPE_STRUCT == dbus_message_iter_get_arg_type(&rootIter))
{
DBusMessageIter structIter;
dbus_message_iter_recurse(&rootIter, &structIter);//Initialize iterator for struct
//Argument 1 is int32
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&structIter))
{
int a;
dbus_message_iter_get_basic(&structIter, &a);//Read integer
dbus_message_iter_next(&structIter);//Go to next argument of structiter
if (DDBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&structIter))
{
DBusMessageIter dictIter;
dbus_message_iter_recurse(&structIter, &dictIter);//Initialize iterator for dictentry
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&dictIter))
{
dbus_message_iter_get_basic(&dictIter, &a);//Read integer
dbus_message_iter_next(&dictIter);//Go to next argument of dictentry
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&dictIter))
{
dbus_message_iter_get_basic(&dictIter, &a);//Read integer
}
}
}
dbus_message_iter_next(&structIter);//Go to next argument of structiter
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&structIter))
{
dbus_message_iter_get_basic(&structIter, &a);//Read integer
}
}
}
dbus_message_iter_next(&rootIter);//Go to next argument of root iterator
if (DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type(&rootIter))
{
uint32_t b;
dbus_message_iter_get_basic(&rootIter, &b);//Read integer
}
}
In above code, I used argument by argument signature check. Instead you can do a one time verfication using dbus_message_iter_get_signature. Refer to libdbus api for more info.
#
From your reply I understand that you have problems with connection setup ,
here is a full example, where a method call is invoked on a server and the result is printed
if the first argument is a string.
#
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <assert.h>
DBusConnection* conn = NULL;
//Helper function to setup connection
void vsetupconnection();
//Send method call, Returns NULL on failure, else pointer to reply
DBusMessage* sendMethodCall(const char* objectpath, \
const char* busname, \
const char* interfacename, \
const char* methodname);
#define TEST_BUS_NAME "org.freedesktop.DBus"
#define TEST_OBJ_PATH "/org/freedesktop/DBus"
#define TEST_INTERFACE_NAME "org.freedesktop.DBus.Introspectable"
#define TEST_METHOD_NAME "Introspect"
int main (int argc, char **argv)
{
vsetupconnection();
DBusMessage* reply = sendMethodCall(TEST_OBJ_PATH, TEST_BUS_NAME, TEST_INTERFACE_NAME, TEST_METHOD_NAME);
if(reply != NULL) {
DBusMessageIter MsgIter;
dbus_message_iter_init(reply, &MsgIter);//msg is pointer to dbus message received
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&MsgIter)){
char* str = NULL;
dbus_message_iter_get_basic(&MsgIter, &str);
printf("Received string: \n %s \n",str);
}
dbus_message_unref(reply);//unref reply
}
dbus_connection_close(conn);
return 0;
}
void vsetupconnection()
{
DBusError err;
// initialise the errors
dbus_error_init(&err);
// connect to session bus
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
printf("Connection Error (%s)\n", err.message);
dbus_error_free(&err);
}
if (NULL == conn) {
exit(1);
}
else {
printf("Connected to session bus\n");
}
}
DBusMessage* sendMethodCall(const char* objectpath, const char* busname, const char* interfacename, const char* methodname)
{
assert(objectpath != NULL); assert(busname != NULL); assert(interfacename != NULL);
assert(methodname != NULL); assert(conn != NULL);
DBusMessage* methodcall = dbus_message_new_method_call(busname,objectpath, interfacename, methodname);
if (methodcall == NULL) {
printf("Cannot allocate DBus message!\n");
}
//Now do a sync call
DBusPendingCall* pending;
DBusMessage* reply;
if (!dbus_connection_send_with_reply(conn, methodcall, &pending, -1))//Send and expect reply using pending call object
{
printf("failed to send message!\n");
}
dbus_connection_flush(conn);
dbus_message_unref(methodcall);
methodcall = NULL;
dbus_pending_call_block(pending);//Now block on the pending call
reply = dbus_pending_call_steal_reply(pending);//Get the reply message from the queue
dbus_pending_call_unref(pending);//Free pending call handle
assert(reply != NULL);
if(dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
printf("Error : %s",dbus_message_get_error_name(reply));
dbus_message_unref(reply);
reply = NULL;
}
return reply;
}
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.