1 // GnetSocketDriver.cc
2 //
3 // Copyright (C) 2009, 2010, 2011 Rob Caelers <robc@krandor.nl>
4 // All rights reserved.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #if defined(HAVE_GIO_NET) && defined(HAVE_DISTRIBUTION)
25
26 #include "debug.hh"
27 #include "GIOSocketDriver.hh"
28
29 using namespace std;
30
31
32 //! Creates a new listen socket.
GIOSocketServer()33 GIOSocketServer::GIOSocketServer() :
34 service(NULL)
35 {
36 }
37
38
39 //! Destructs the listen socket.
~GIOSocketServer()40 GIOSocketServer::~GIOSocketServer()
41 {
42 if (service != NULL)
43 {
44 g_socket_service_stop(service);
45 g_object_unref(service);
46 service = NULL;
47 }
48 }
49
50
51 //! Listen at the specified port.
52 void
listen(int port)53 GIOSocketServer::listen(int port)
54 {
55 GError *error = NULL;
56
57 service = g_socket_service_new();
58 if (service == NULL)
59 {
60 throw SocketException("Failed to create server");
61 }
62
63 gboolean rc = g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service), port, NULL, &error);
64 if (!rc)
65 {
66 g_object_unref(service);
67 service = NULL;
68
69 throw SocketException(string("Failed to listen: ") + error->message);
70 }
71
72 g_signal_connect(service, "incoming", G_CALLBACK(static_socket_incoming), this);
73 g_socket_service_start(service);
74
75 }
76
77 gboolean
static_socket_incoming(GSocketService * service,GSocketConnection * connection,GObject * src_object,gpointer user_data)78 GIOSocketServer::static_socket_incoming(GSocketService *service,
79 GSocketConnection *connection,
80 GObject *src_object,
81 gpointer user_data)
82 {
83 TRACE_ENTER("GIOSocketServer::static_socket_incoming");
84 (void) service;
85 (void) src_object;
86
87 try
88 {
89 GIOSocketServer *ss = (GIOSocketServer *) user_data;
90 GIOSocket *socket = new GIOSocket(connection);
91 ss->listener->socket_accepted(ss, socket);
92 }
93 catch(...)
94 {
95 // Make sure that no exception reach the glib mainloop.
96 }
97
98 TRACE_EXIT();
99 return FALSE;
100 }
101
102
103
104 void
static_connected_callback(GObject * source_object,GAsyncResult * result,gpointer user_data)105 GIOSocket::static_connected_callback(GObject *source_object,
106 GAsyncResult *result,
107 gpointer user_data)
108 {
109 TRACE_ENTER("GIOSocketServer::static_connected_callback");
110
111 GIOSocket *socket = (GIOSocket *)user_data;
112 GError *error = NULL;
113
114 GSocketConnection *socket_connection =
115 g_socket_client_connect_finish(G_SOCKET_CLIENT(source_object), result, &error);
116
117 if (error != NULL)
118 {
119 TRACE_MSG("failed to connect");
120 g_error_free(error);
121 }
122 else if (socket_connection != NULL)
123 {
124 socket->connection = socket_connection;
125 socket->socket = g_socket_connection_get_socket(socket->connection);
126 g_socket_set_blocking(socket->socket, FALSE);
127 g_socket_set_keepalive(socket->socket, TRUE);
128
129 socket->source = g_socket_create_source(socket->socket,
130 (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP),
131 NULL);
132 g_source_set_callback(socket->source, reinterpret_cast<GSourceFunc>(static_data_callback), (void*)socket, NULL);
133 g_source_attach(socket->source, NULL);
134 // g_source_unref(source);
135
136 if (socket->listener != NULL)
137 {
138 socket->listener->socket_connected(socket, socket->user_data);
139 }
140 }
141 TRACE_EXIT();
142 }
143
144
145 gboolean
static_data_callback(GSocket * socket,GIOCondition condition,gpointer user_data)146 GIOSocket::static_data_callback(GSocket *socket,
147 GIOCondition condition,
148 gpointer user_data)
149 {
150 TRACE_ENTER_MSG("GIOSocket::static_data_callback", (int)condition);
151
152 GIOSocket *giosocket = (GIOSocket *)user_data;
153 gboolean ret = TRUE;
154
155 (void) socket;
156
157 try
158 {
159 // check for socket error
160 if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
161 {
162 if (giosocket->listener != NULL)
163 {
164 TRACE_MSG("Closing socket");
165 // giosocket->close();
166 giosocket->listener->socket_closed(giosocket, giosocket->user_data);
167 }
168 ret = FALSE;
169 }
170
171 // process input
172 if (ret && (condition & G_IO_IN))
173 {
174 if (giosocket->listener != NULL)
175 {
176 giosocket->listener->socket_io(giosocket, giosocket->user_data);
177 }
178 }
179 }
180 catch(...)
181 {
182 // Make sure that no exception reach the glib mainloop.
183 TRACE_MSG("Exception. Closing socket");
184 giosocket->close();
185 ret = FALSE;
186 }
187 TRACE_EXIT();
188 return ret;
189 }
190
191 //! Creates a new connection.
GIOSocket(GSocketConnection * connection)192 GIOSocket::GIOSocket(GSocketConnection *connection) :
193 connection(connection),
194 resolver(NULL)
195 {
196 TRACE_ENTER("GIOSocket::GIOSocket(con)");
197 socket = g_socket_connection_get_socket(connection);
198 g_object_ref(connection);
199
200 g_socket_set_blocking(socket, FALSE);
201 g_socket_set_keepalive(socket, TRUE);
202
203 source = g_socket_create_source(socket, (GIOCondition)G_IO_IN, NULL);
204 g_source_set_callback(source, reinterpret_cast<GSourceFunc>(static_data_callback), (void*)this, NULL);
205 g_source_attach(source, NULL);
206 g_source_unref(source);
207 TRACE_EXIT();
208 }
209
210
211 //! Creates a new connection.
GIOSocket()212 GIOSocket::GIOSocket() :
213 connection(NULL),
214 socket(NULL),
215 resolver(NULL),
216 source(NULL),
217 port(0)
218 {
219 TRACE_ENTER("GIOSocket::GIOSocket()");
220 TRACE_EXIT();
221 }
222
223
224 //! Destructs the connection.
~GIOSocket()225 GIOSocket::~GIOSocket()
226 {
227 TRACE_ENTER("GIOSocket::~GIOSocket");
228 if (connection != NULL)
229 {
230 g_object_unref(connection);
231 }
232 if (resolver != NULL)
233 {
234 g_object_unref(resolver);
235 }
236 if (source != NULL)
237 {
238 g_source_destroy(source);
239 }
240 TRACE_EXIT();
241 }
242
243
244 //! Connects to the specified host.
245 void
connect(const string & host,int port)246 GIOSocket::connect(const string &host, int port)
247 {
248 TRACE_ENTER_MSG("GIOSocket::connect", host << " " << port);
249 this->port = port;
250
251 GInetAddress *inet_addr = g_inet_address_new_from_string(host.c_str());
252 if (inet_addr != NULL)
253 {
254 connect(inet_addr, port);
255 g_object_unref(inet_addr);
256 }
257 else
258 {
259 resolver = g_resolver_get_default();
260 g_resolver_lookup_by_name_async(resolver,
261 host.c_str(),
262 NULL,
263 static_connect_after_resolve,
264 this);
265
266 }
267 TRACE_EXIT();
268 }
269
270 void
connect(GInetAddress * inet_addr,int port)271 GIOSocket::connect(GInetAddress *inet_addr, int port)
272 {
273 TRACE_ENTER_MSG("GIOSocket::connect", port);
274 GSocketAddress *socket_address = g_inet_socket_address_new(inet_addr, port);
275 GSocketClient *socket_client = g_socket_client_new();
276
277 g_socket_client_connect_async(socket_client,
278 G_SOCKET_CONNECTABLE(socket_address),
279 NULL,
280 static_connected_callback,
281 this);
282 TRACE_EXIT();
283 }
284
285 void
static_connect_after_resolve(GObject * source_object,GAsyncResult * res,gpointer user_data)286 GIOSocket::static_connect_after_resolve(GObject *source_object, GAsyncResult *res, gpointer user_data)
287 {
288 TRACE_ENTER("GIOSocket::static_connect_after_resolve");
289 GError *error = NULL;
290 GList *addresses = g_resolver_lookup_by_name_finish((GResolver *)source_object, res, &error);
291
292 if (error != NULL)
293 {
294 TRACE_MSG("failed");
295 g_error_free(error);
296 }
297
298 if (addresses != NULL)
299 {
300 // Take first result
301 if (addresses->data != NULL)
302 {
303 GInetAddress *a = (GInetAddress *) addresses->data;
304 GIOSocket *socket = (GIOSocket *) user_data;
305 socket->connect(a, socket->port);
306 }
307 g_resolver_free_addresses(addresses);
308 }
309 TRACE_EXIT();
310 }
311
312
313 //! Read from the connection.
314 void
read(void * buf,int count,int & bytes_read)315 GIOSocket::read(void *buf, int count, int &bytes_read)
316 {
317 TRACE_ENTER_MSG("GIOSocket::read", count);
318
319 GError *error = NULL;
320 gsize num_read = 0;
321
322 if (socket != NULL)
323 {
324 num_read = g_socket_receive(socket, (char *)buf, count, NULL, &error);
325
326 if (error != NULL)
327 {
328 throw SocketException(string("socket read error: ") + error->message);
329 }
330 }
331
332 bytes_read = (int)num_read;
333 TRACE_RETURN(bytes_read);
334 }
335
336
337 //! Write to the connection.
338 void
write(void * buf,int count,int & bytes_written)339 GIOSocket::write(void *buf, int count, int &bytes_written)
340 {
341 GError *error = NULL;
342 gsize num_written = 0;
343 if (socket != NULL)
344 {
345 num_written = g_socket_send(socket, (char *)buf, count, NULL, &error);
346 if (error != NULL)
347 {
348 throw SocketException(string("socket write error: ") + error->message);
349 }
350 }
351 bytes_written = (int) num_written;
352 }
353
354
355 //! Close the connection.
356 void
close()357 GIOSocket::close()
358 {
359 TRACE_ENTER("GIOSocket::close");
360 GError *error = NULL;
361 if (socket != NULL)
362 {
363 g_socket_shutdown(socket, TRUE, TRUE, &error);
364 g_socket_close(socket, &error);
365 socket = NULL;
366 }
367 TRACE_EXIT();
368 }
369
370 //! Create a new socket
371 ISocket *
create_socket()372 GIOSocketDriver::create_socket()
373 {
374 return new GIOSocket();
375 }
376
377
378 //! Create a new listen socket
379 ISocketServer *
create_server()380 GIOSocketDriver::create_server()
381 {
382 return new GIOSocketServer();
383 }
384
385 #endif
386