1 /* Echo server (TCP blocking)
2 * Copyright (C) 2000-2003 David Helder
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
18 */
19
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
25 #include <gnet.h>
26
27 #include <signal.h>
28
29
30 typedef struct
31 {
32 GTcpSocket* socket;
33 gchar* name;
34 guint watch_flags;
35 gint port;
36 guint watch;
37 gchar buffer[1024];
38 guint n;
39
40 } ClientState;
41
42
43 static void clientstate_delete (ClientState* state);
44
45 static void async_accept (GTcpSocket* server_socket,
46 GTcpSocket* client_socket,
47 gpointer data);
48 static gboolean async_client_iofunc (GIOChannel* client,
49 GIOCondition condition, gpointer data);
50
51 static void async_sig_int (int signum);
52
53 static GTcpSocket* async_server = NULL;
54
55
56 int
main(int argc,char ** argv)57 main(int argc, char** argv)
58 {
59 GTcpSocket* server;
60 GInetAddr* addr;
61 gchar* name;
62 gint port;
63 GMainLoop* main_loop;
64
65 gnet_init ();
66
67 if (argc != 2)
68 {
69 fprintf (stderr, "usage: echoserver <port> \n");
70 exit(EXIT_FAILURE);
71 }
72
73 port = atoi(argv[argc - 1]);
74
75 /* Create the server */
76 server = gnet_tcp_socket_server_new_with_port (port);
77 if (server == NULL)
78 {
79 fprintf (stderr, "Could not create server on port %d\n", port);
80 exit (EXIT_FAILURE);
81 }
82
83 async_server = server;
84 signal (SIGINT, async_sig_int);
85
86 /* Print the address */
87 addr = gnet_tcp_socket_get_local_inetaddr(server);
88 g_assert (addr);
89 name = gnet_inetaddr_get_canonical_name (addr);
90 g_assert (name);
91 port = gnet_inetaddr_get_port (addr);
92 g_print ("Async echoserver running on %s:%d\n", name, port);
93 gnet_inetaddr_delete (addr);
94 g_free (name);
95
96 /* Create the main loop */
97 main_loop = g_main_new (FALSE);
98
99 /* Wait asyncy for incoming clients */
100 gnet_tcp_socket_server_accept_async (server, async_accept, NULL);
101
102 /* Start the main loop */
103 g_main_run (main_loop);
104
105 exit (EXIT_SUCCESS);
106 return 0;
107 }
108
109
110
111 static void
clientstate_delete(ClientState * state)112 clientstate_delete (ClientState* state)
113 {
114 g_source_remove (state->watch);
115 gnet_tcp_socket_delete (state->socket);
116 g_free (state->name);
117 g_free (state);
118 }
119
120 static void
async_accept(GTcpSocket * server,GTcpSocket * client,gpointer data)121 async_accept (GTcpSocket* server, GTcpSocket* client, gpointer data)
122 {
123 if (client)
124 {
125 GInetAddr* addr;
126 gchar* name;
127 gint port;
128 GIOChannel* client_iochannel;
129 ClientState* client_state;
130
131 /* Print the address */
132 addr = gnet_tcp_socket_get_local_inetaddr(client);
133 g_assert (addr);
134 name = gnet_inetaddr_get_canonical_name (addr);
135 g_assert (name);
136 port = gnet_inetaddr_get_port (addr);
137 gnet_inetaddr_delete (addr);
138
139 client_iochannel = gnet_tcp_socket_get_io_channel (client);
140 g_assert (client_iochannel != NULL);
141
142 client_state = g_new0(ClientState, 1);
143 client_state->socket = client;
144 client_state->name = name;
145 client_state->port = port;
146 client_state->watch_flags = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
147 client_state->watch =
148 g_io_add_watch (client_iochannel, client_state->watch_flags,
149 async_client_iofunc, client_state);
150
151 g_print ("Accepted connection from %s:%d\n", name, port);
152 }
153 else
154 {
155 fprintf (stderr, "Server error\n");
156 exit (EXIT_FAILURE);
157 }
158 }
159
160
161 /*
162
163 Client IO callback. Called for errors, input, or output. When
164 there is input, we reset the watch with the output flag (if we
165 haven't already). When there is no more data, the watch is reset
166 without the output flag.
167
168 */
169 gboolean
async_client_iofunc(GIOChannel * iochannel,GIOCondition condition,gpointer data)170 async_client_iofunc (GIOChannel* iochannel, GIOCondition condition,
171 gpointer data)
172 {
173 ClientState* client_state = (ClientState*) data;
174 gboolean rv = TRUE;
175
176 g_assert (client_state != NULL);
177
178
179 /* Check for socket error */
180 if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
181 {
182 fprintf (stderr, "Client socket error (%s:%d)\n",
183 client_state->name, client_state->port);
184 goto error;
185 }
186
187 if (condition & G_IO_OUT)
188 {
189 GIOError error;
190 gsize bytes_written;
191
192 /* Write the data out */
193 error = g_io_channel_write(iochannel, client_state->buffer,
194 client_state->n, &bytes_written);
195
196 if (error != G_IO_ERROR_NONE)
197 {
198 fprintf (stderr, "Client write error (%s:%d): %d\n",
199 client_state->name, client_state->port, error);
200 goto error;
201 }
202
203 else if (bytes_written > 0)
204 {
205 guint old_watch_flags;
206
207 /* Move the memory down some (you wouldn't want to do this
208 in a performance server because it's slow!) */
209 client_state->n -= bytes_written;
210 g_memmove(client_state->buffer,
211 &client_state->buffer[bytes_written],
212 client_state->n);
213
214 old_watch_flags = client_state->watch_flags;
215
216 /* Remove OUT watch if done */
217 if (client_state->n == 0)
218 client_state->watch_flags &= ~G_IO_OUT;
219
220 /* Add IN watch */
221 client_state->watch_flags |= G_IO_IN;
222
223 /* Update watch flags if they changed */
224 if (old_watch_flags != client_state->watch_flags)
225 {
226 g_source_remove (client_state->watch);
227 client_state->watch =
228 g_io_add_watch(iochannel, client_state->watch_flags,
229 async_client_iofunc, client_state);
230 rv = FALSE;
231 }
232 }
233 }
234
235 /* Check for data to be read (or if the socket was closed) */
236 if (condition & G_IO_IN)
237 {
238 GIOError error;
239 gsize bytes_read;
240
241 /* Read the data into our buffer */
242 error =
243 g_io_channel_read (iochannel,
244 &client_state->buffer[client_state->n],
245 sizeof(client_state->buffer) - client_state->n,
246 &bytes_read);
247
248
249 /* Check for socket error */
250 if (error != G_IO_ERROR_NONE)
251 {
252 fprintf (stderr, "Client read error (%s:%d): %d\n",
253 client_state->name, client_state->port, error);
254 goto error;
255 }
256
257 /* Check if we read 0 bytes, which means the connection is
258 closed */
259 else if (bytes_read == 0)
260 {
261 g_print ("Connection from %s:%d closed\n",
262 client_state->name, client_state->port);
263 goto error;
264 }
265
266 /* Otherwise, we read something */
267 else
268 {
269 guint old_watch_flags;
270
271 g_assert (bytes_read > 0);
272
273 old_watch_flags = client_state->watch_flags;
274 client_state->n += bytes_read;
275
276 /* Add an add watch */
277 client_state->watch_flags |= G_IO_OUT;
278
279 /* Remove the IN watch if the buffer is full */
280 if (client_state->n == sizeof(client_state->buffer))
281 client_state->watch_flags &= ~G_IO_IN;
282
283 /* Update watch flags if they changed */
284 if (old_watch_flags != client_state->watch_flags)
285 {
286 g_source_remove (client_state->watch);
287 client_state->watch =
288 g_io_add_watch(iochannel, client_state->watch_flags,
289 async_client_iofunc, client_state);
290 rv = FALSE;
291 }
292 }
293
294 }
295
296 return rv;
297
298 error:
299 clientstate_delete (client_state);
300 return FALSE;
301 }
302
303
304 static void
async_sig_int(int signum)305 async_sig_int (int signum)
306 {
307 gnet_tcp_socket_delete (async_server);
308 exit (EXIT_FAILURE);
309 }
310