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