Control Remoto

Vicente González Ruiz

December 14, 2014

Contents

1 Introducción
2 El robot
3 La matriz
4 Objetivo de los robots
5 El protocolo de comunicación
6 Código fuente del servidor

1 Introducción

En un entorno industrial es probable que tengamos que controlar algún dispositivo. En muchas de esas ocasiones es posible que exista alguna aplicación que nos permita realizar dicho control. Sin embargo, también podría ocurrir lo contrario y consecuentemente, tengamos que diseñar una aplicación específica. En esta práctica aprenderemos a diseñar una aplicación que permita controlar un dispositivo remoto a través de la red.

2 El robot


PIC


Figure 1: Direcciones posibles del movimiento del robot.

El dispositivo a controlar es un robot muy simple que puede moverse en 4 direcciones posibles (véase la Figura 1), paso a paso, es decir, que mientras no le demos la order de moverse, estará detenido y cuando se la demos, sólo se moverá un paso, hasta que volvamos a decirle que se mueva.

3 La matriz


PIC


Figure 2: Una matriz ejemplo (aparecen además 4 robots).

El robot se moverá por una matriz de posisiciones discretas (véase la Figura 2). En dicha matriz habrá posiciones libres y ocupadas. Cuando estén libres, el robot podrá moverse a ellas y ocuparlas. Cuando estén ocupadas, el robot no podrá moverse sobre ellas. El robot sólo puede ocupar una posición de la matriz en un instante dado.

Las posiciones podrán estár ocupadas temporalmente o de forma indefinida. Una posición está ocupada de forma indefinida por algún elemento bloqueante (imaginemos que se trata de una pared que no podemos atravesar). Sin embargo, una determinada posición podría estar temporalmente ocupada porque en ella hay otro robot en ese momento y éste podría moverse en el futuro.

4 Objetivo de los robots

En la matriz aparece una abertura, una salida. El objetivo de los robots será encontrar la salida en un tiempo mínimo.

5 El protocolo de comunicación


Robot:
NSEW (Puedo moverme en todas las direcciones)
Controlador:
S (Muévete hacia el sur)
Robot:
S (Me he movido hacia el sur)
Robot:
NSW (Norte, sur, y oeste)
Controlador:
E (Muévete hacia el este)
Robot:
# (Estás tonto o qué?)
Robot:
NSW (Norte, sur, y oeste!)
Controlador:
N (OK, muévete hacia el norte)
Robot:
N (!Ahora sí!)

Figure 3: Un trozo de interacción (ejemplo) entre un robot y un controlador.

En todo sistema de comunicación es necesario un protocolo de comunicación que permita interaccionar a los elementos remotos. En el caso de nuestro robot, este es el protocolo asociado:

  1. Por defecto, en cada interacción el robot devolverá sobre qué direcciones puede moverse a continuación. Si devuelve, por ejemplo, una E significa que sólo puede moverse hacia el este, etc.
  2. El robot acepta además alguno de los mensajes N, S, E o W. Cuando reciba uno (y sólo 1, no más) de estos caracteres, el robot tratará1 de moverse a dicha posición. Si lo consigue, devolverá de vuelta el correspondiente caracter. Si no lo consigue, devolverá #. En la Figura 3 se presenta un ejemplo de una interacción robot-controlador (servidor-cliente). Si el robot recibe cualquier otro símbolo diferente de N, S, E o W, se autodestruirá.
  3. Todas las comunicaciones con los robots se realizarán a través de un proceso servidor que, aparte de contolar que las reglas definidas en la matriz se cumplen en todo momento, indicará al proceso cliente que controla un robot que éste ha encontrado la salida. Dicho evento se comunicará con el carácter !.
  4. El cliente, en cualquier momento puede finalizar la comunicación con el servidor enviándole el carácter ..

6 Código fuente del servidor

El codigo mostrado a continuación puede conseguirse en http://www.ace.ual.es/\~vruiz/redes_industriales/matrix.c.

 
1/ 
2 
3  This is a server program that uses the Internet sockets. Basically, 
4  a server is a process that waits for clients requests, perform some 
5  task related to there requests and return one o more responses to 
6  the clients. To compile this program (matrix.c) under Linux you can 
7  write: 
8 
9  gcc g matrix.c o matrix L/usr/X11R6/lib lX11 lpthread 
10 
11  This server simulates a virtual world (The Matrix) where there are 
12  a treasure and robots that try to find it. Robots are controlled by 
13  clients (one robot/client) using TCP connections. 
14 
15  When a controller (client) has been connected to the server process 
16  (referenced by a IP address and a port in the host that possess this 
17  adders), the server creates a robot and places it in the 
18  matrix. Automatically the server tells the controller the movement 
19  alternatives that in that moment the robot has. Example 
20  (controllers console): 
21 
22  telnet localhost 6789 
23  Trying 127.0.0.1... 
24  Connected to localhost. 
25  Escape character is ’ˆ]’. 
26  WENS                   < Movement possibilities (servers output) 
27  W                      < Request (clients output) 
28  W                      < Response (servers output) 
29  WENS                   < Movement possibilities (servers output) 
30  .                      < Endofconnection request (clients output) 
31  Connection closed by foreign host. 
32 
33  In this example, the robot says that it can move in any direction 
34  and the controller move the robot to the West (W). Notice that only 
35  four directions are available and that a request only moves a robot 
36  in one step (one pixel in Matrix). 
37 
38/ 
39 
40/ 
41  Header files. 
42/ 
43 
44/ General staff. / 
45#include <stdio.h>      // printf() 
46#include <errno.h>      // perror() 
47#include <stdlib.h>     // exit(), random(), EXIT_SUCCESS, EXIT_FAILURE 
48#include <string.h>     // memset() 
49#include <unistd.h>     // read(), write(), getpot() 
50#include <time.h>       // time() 
51 
52/ Threads. / 
53#include <pthread.h>    // pthread_t, pthread_create() 
54 
55/ Sockets. More information in: 
56 
57   http://beej.us/guide/bgnet/ 
58   http://www.linuxhowtos.org/C_C++/socket.htm 
59   http://www.cs.utah.eduswalton/listings/sockets/programs/ 
60   http://gnosis.cx/publish/programming/sockets.html 
61   http://www.fortunecity.com/skyscraper/arpanet/6/cc.htm 
62   http://www.csce.uark.eduaapon/courses/os/examples/concurrentserver.c 
63   http://www.prasannatech.net/2008/07/socketprogrammingtutorial.html 
64 
65/ 
66#include <sys/types.h> 
67#include <sys/socket.h> // socket(), listen() 
68#include <netinet/in.h> 
69#include <netdb.h> 
70#include <arpa/inet.h> // inet_ntop() 
71 
72/ Signals. / 
73#include <signal.h>    // signal(), SIGINT 
74 
75/ Exceptions control. / 
76#include <assert.h> 
77 
78/ X11. / 
79#include <X11/Xlib.h> 
80#include <X11/Xutil.h> 
81#include <X11/Xos.h> 
82#include <X11/Xatom.h> 
83 
84/ 
85  Definitions. 
86/ 
87#define TRUE 1 
88#define FALSE 0 
89#define WIN_WIDTH 500 
90#define WIN_HEIGHT 500 
91#define PORT 6789 
92#define FREE 0 
93#define OCCUPIED 1 
94#define WALL 2 
95#define TREASURE 3 
96#define STONE 4 
97#define STONES 1000 
98#define END 1 
99#define NDEBUG // If defined, no assert() efects! 
100#define FATAL_ERROR(function) { perror(function); exit(EXIT_FAILURE); } 
101 
102/ 
103  Global data. 
104/ 
105 
106/ The virtual world. / 
107char ∗∗ matrix; 
108 
109/ Semaphore used for access to the shared data between the threads in 
110   mutual exclusion. / 
111pthread_mutex_t mut; 
112 
113/ The X11 display. / 
114Display  display; 
115 
116/ The graphics context. / 
117GC gc; 
118 
119/ The window. / 
120Window win; 
121 
122/ Colors. / 
123unsigned long black, white, red, green, blue; 
124 
125/ Treasure location. / 
126unsigned int treasure_y, treasure_x; 
127 
128/ 
129  Functions. 
130/ 
131int main(int argc, char  argv[]) { 
132 
133  int win_width = WIN_WIDTH; 
134  int win_height = WIN_HEIGHT; 
135  int port = PORT; 
136 
137  / Command line arguments handling. / { 
138    int c; 
139    while ((c = getopt(argc, argv, p:w:h:H)) != 1) { 
140      switch (c) { 
141      case p: 
142        port = atoi(optarg); 
143        break; 
144      case w: 
145        win_width = atoi(optarg); 
146        break; 
147      case h: 
148        win_height = atoi(optarg); 
149        break; 
150      case H: 
151        printf(Usage:_matrix_[OPTION...]n); 
152        printf(n); 
153        printf(__p_listening_port_(%d)n, port); 
154        printf(__w_matrix_width_(%d)n, win_width); 
155        printf(__h_matrix_height_(%d)n, win_height); 
156        printf(n); 
157        printf(A_virtual_world_where_you_can_find_a_treasure.n); 
158        return; 
159      case ?: 
160        if (isprint(optopt)) 
161          printf(Unknown_option_%c’.n, optopt); 
162        else 
163          printf(Unknown_option_character_∖∖x%x’.n, optopt); 
164        return 1; 
165      default: 
166        abort(); 
167      } 
168    } 
169  } 
170 
171  printf(Matrix_size:_y=%d,_x=%dn, win_width, win_height); 
172 
173  / Randomize the random number generator. / 
174  srandom(time(NULL)); 
175 
176  / Establish a connection to the X11 server on this machine. / 
177  display = XOpenDisplay(NULL); 
178 
179  / Color initialization. / { 
180 
181    int screenNumber = DefaultScreen(display); 
182    white = WhitePixel(display,screenNumber); 
183    black = BlackPixel(display,screenNumber); 
184 
185    / Return a color number given its name. / 
186    unsigned long GetColor(Display  display, char  color_name) { 
187      Colormap cmap; 
188      XColor near_color, true_color; 
189      cmap = DefaultColormap(display, 0); 
190      XAllocNamedColor(display, cmap, color_name, &near_color, &true_color); 
191      return near_color.pixel; 
192    } 
193 
194    red = GetColor(display, red); 
195    green = GetColor(display, green); 
196    blue = GetColor(display, blue); 
197  } 
198 
199  / Create the window that will paint The Matrix. / 
200  win = XCreateSimpleWindow 
201    (display, // Display 
202     DefaultRootWindow(display), // Parent window 
203     100, 100, // Origin (upperlefthand corner with respect the parent window) 
204     win_width, win_height, 5, white, // Width and color of the border 
205     black); // Color of the background 
206 
207  / Maps the window on the specified display. / 
208  XMapWindow(display, win); 
209 
210  / Events (such as mouse clicks, mouse movements, key strokes, etc.) 
211     we are interested in. In this case, the client will be aware of 
212     structural changes to the window. / { 
213 
214    long eventMask = StructureNotifyMask; 
215    XSelectInput(display, win, eventMask); 
216 
217    / Sleep the X11 client until the server does not notify the 
218       mapping operation. /{ 
219      XEvent evt; 
220      do { 
221        XNextEvent(display, &evt); // calls XFlush 
222      } while (evt.type != MapNotify); 
223    } 
224 
225  } / End of window creation and event handling. / 
226 
227  / Create a Graphics Context (necessary for painting in the 
228     window). / 
229  gc = XCreateGC(display, win, 0, NULL); 
230 
231  / Initialization of Matrix. / { 
232 
233    / Memory allocation: / { 
234      matrix = (char ∗∗) malloc((win_height+2)  sizeof(char )); 
235      assert(matrix); 
236      { 
237        int y; 
238        for (y = 0; y < win_height+2; y++) { 
239          matrix[y] = (char ) calloc(win_width+2, sizeof(char)); 
240          assert(matrix[y]); 
241        } 
242      } 
243    } 
244 
245    / Offset displacement. / { 
246      int y; 
247      matrix += 1; 
248      for (y = 0; y < win_height; y++) { 
249        matrix[y] += 1; 
250      } 
251    } 
252 
253    / Paint the walls in the window. / 
254    XSetForeground(display, gc, green); 
255    XDrawRectangle(display, win, gc, 0, 0, win_width  1, win_height  1); 
256 
257    / Left and right walls in The Matrix. / { 
258      int y; 
259      for (y = 0; y < win_height; y++) { 
260        matrix[y][0] = matrix[y][win_width  1] = WALL; 
261      } 
262    } 
263 
264    / Top and bottom walls. / { 
265      int x; 
266      for (x = 0; x < win_width; x++) { 
267        matrix[0][x] = matrix[0][win_height  1] = WALL; 
268      } 
269    } 
270 
271    / Treasure location. / 
272    treasure_y = (random() % (win_height  2)) + 1; // Remember the walls 
273    treasure_x = (random() % (win_width  2)) + 1; 
274    matrix[treasure_y][treasure_x] = TREASURE; 
275    printf(Treasure_in_y=%d,_x=%dn, treasure_y, treasure_x); 
276 
277    / Paint the treasure. / 
278    XSetForeground(display, gc, red); 
279    XFillArc(display, win, gc, treasure_x  5, treasure_y  5, 10, 10, 0, 360 
280              64); 
281 
282    / Some stones in the matrix, / { 
283      int i; 
284      for(i=0; i<STONES; i++) { 
285        int y = (random() % (win_height  2)) + 1; 
286        int x = (random() % (win_width  2)) + 1; 
287        matrix[y][x] = STONE; 
288        XSetForeground(display, gc, blue); 
289        XDrawPoint(display, win, gc, x, y); 
290      } 
291    } 
292 
293    / Paint everything in the window (sends the graphics operation to 
294       the server and waits for its completeness). / 
295    XFlush(display); 
296  } 
297 
298  / Semaphore creation. / 
299  pthread_mutex_init(&mut, NULL); 
300 
301  / Listening socket request (to the OS). / 
302  int listen_sd; { 
303    / Get the protocol number for the TCP. / 
304    struct protoent  ptrp; // Pointer to a protocol table entry. 
305    ptrp = getprotobyname(tcp); 
306    if (ptrp == NULL) 
307      FATAL_ERROR(getprotobyname); 
308 
309    listen_sd = socket(PF_INET, SOCK_STREAM, ptrp>p_proto); 
310    if (listen_sd < 0) 
311      FATAL_ERROR(socket); 
312  } 
313 
314  / Listen socket mapping. / { 
315    struct sockaddr_in listen_sa; // Listen socket address 
316    memset((char ) &listen_sa, 0, sizeof(listen_sa)); 
317    listen_sa.sin_family = AF_INET; // This is an Internet socket 
318    listen_sa.sin_addr.s_addr = INADDR_ANY; // Listen to any of the NICs 
319    listen_sa.sin_port = htons((u_short) port); // Listen to the port port 
320 
321    / Reuse port, even if the SOs timeout has not been expired. / { 
322      int yes = 1; 
323      if (setsockopt(listen_sd, 
324                     SOL_SOCKET, 
325                     SO_REUSEADDR, 
326                     &yes, 
327                     sizeof(int)) < 0) 
328        FATAL_ERROR(setsockopt); 
329    } 
330 
331    / Request to the OS the port (it could be occupied). / 
332    if (bind(listen_sd, 
333             (struct sockaddr ) &listen_sa, 
334             sizeof(listen_sa)) < 0) 
335      FATAL_ERROR(bind); 
336  } 
337 
338  / Listen the port. / { 
339    if (listen(listen_sd, 1) < 0) 
340      FATAL_ERROR(listen); 
341    printf(Listening_on_port_%dn, port); 
342  } 
343 
344  / Install an handler for a graceful CTRL+c exit. / { 
345    void end() { 
346      / Free the allocated resources. / 
347      printf(nCTRL+c_detected!_Exiting_...n); 
348      XDestroyWindow(display, win); 
349      XCloseDisplay(display); 
350      close(listen_sd); 
351      exit(EXIT_SUCCESS); 
352    } 
353    signal(SIGINT, end); 
354  } 
355 
356  / This is the working cycle of the server. Forever, it is listening 
357     to the listening socket and when a client establish a new 
358     connection, the server creates: (1) a new socket (the service 
359     socket”) connected to the same port than the listening socket 
360     and (2) a thread to serve the client. / 
361  while (TRUE) { 
362 
363    / Creation of the service socket (this is a blocking 
364       operation). Notice that it will be only a service socket per 
365       client, so, this data have to be transfered to a thread. / 
366    int serve_sd; // Serve socket descriptor 
367    struct sockaddr_storage serve_sa; // Serve socket address 
368    socklen_t serve_sa_len; // Length of serve_sa 
369    { 
370 
371      / Accept a new connection. / { 
372        serve_sa_len = sizeof(serve_sa); 
373        serve_sd = accept(listen_sd, 
374                          (struct sockaddr ) &serve_sa, 
375                          &serve_sa_len); 
376        if (serve_sd < 0) 
377          FATAL_ERROR(accept); 
378 
379        / Where is the client? (see: netstat tcp)/ { 
380          char host[NI_MAXHOST], port[NI_MAXSERV]; 
381          int s = getnameinfo((struct sockaddr ) &serve_sa, 
382                              serve_sa_len, host, NI_MAXHOST, 
383                              port, NI_MAXSERV, NI_NUMERICSERV); 
384          if (!s) { 
385            printf(New_connection_from_%s:%sn, host, port); 
386          } 
387        } 
388      } 
389    } / serve_sd / 
390 
391    struct thread_args { 
392      int sd; // Socket descriptor 
393      struct sockaddr_storage sa; // Socket address 
394      socklen_t sa_len; // Length of sa 
395    }; 
396    struct thread_args  ta; // Pointer to the structure of a threads args 
397 
398    / This is the thread. / 
399    void  robot(void  args) { 
400 
401      struct thread_args  ta = (struct thread_args )args; // Pointer copy 
402      int sd = ta>sd; // Serve socket desciptor 
403      struct sockaddr_storage sa = ta>sa; // Serve socket address 
404      socklen_t sa_len = ta>sa_len; // Length of sa 
405 
406      / Install an handler for unpolite clients. This code is 
407         executed only if the server sends data to a client that died 
408         unexpectedly and did not send to the mandatory FIN 
409         segment. Under this situation, the clients thread is 
410         destroyed, the memory resources deallocated and the socket 
411         closed. / { 
412        void closed_connection() { 
413          / Who didnt send the FIN segment? / { 
414            char host[NI_MAXHOST], port[NI_MAXSERV]; 
415            int s = getnameinfo((struct sockaddr ) &sa, 
416                                sa_len, host, NI_MAXHOST, 
417                                port, NI_MAXSERV, NI_NUMERICSERV); 
418            if (!s) { 
419              printf(Lost_connection_with_client_%s:%sn, host, port); 
420            } 
421          } 
422          free(ta); // Free the buffer with the threads args 
423          close(sd); // Close the socket 
424          pthread_exit(0); // Kill the thread 
425        } 
426        signal(SIGPIPE, closed_connection); // Install the hander 
427      } 
428 
429      time_t t0 = time(NULL), t1; 
430 
431      int y, x; / Coordinates of the robot. / { 
432        y = (random() % (win_height  2)) + 1; // Remember the walls 
433        x = (random() % (win_width  2)) + 1; // Remember the walls 
434        printf(New_robot_placed_at_y=%d,_x=%dn, y, x); 
435      } 
436 
437      / Draw the robot. / { 
438        XSetForeground(display, gc, white); 
439        XDrawPoint(display, win, gc, x, y); 
440        XFlush(display); 
441      } 
442 
443      / Send the position of the robot in the matrix. / { 
444        char message[6]; 
445        memset(message, 0, 6); 
446        sprintf(message, %dn, win_height); 
447        send(sd, message, strlen(message), 0); 
448        memset(message, 0, 5); 
449        sprintf(message, %dn, win_width); 
450        send(sd, message, strlen(message), 0); 
451        memset(message, 0, 5); 
452        sprintf(message, %dn, y); 
453        send(sd, message, strlen(message), 0); 
454        memset(message, 0, 5); 
455        sprintf(message, %dn, x); 
456        send(sd, message, strlen(message), 0); 
457      } 
458 
459      / This is the robots life in the matrix. / { 
460        for (;;) { 
461 
462          / Inform to the controller (client) the available movements 
463             for the robot. / 
464          void send_robot_options(int sd, int y, int x) { 
465            char message[6]; 
466            int len = 0; 
467            if (matrix[y][x  1] == FREE) 
468              message[len++] = W; 
469            if (matrix[y][x + 1] == FREE) 
470              message[len++] = E; 
471            if (matrix[y  1][x] == FREE) 
472              message[len++] = N; 
473            if (matrix[y + 1][x] == FREE) 
474              message[len++] = S; 
475            message[len++] = n; 
476            message[len++] = 0; // Neccesary to find the length! 
477            send(sd, message, strlen(message), 0); 
478          } 
479 
480          send_robot_options((int) sd, y, x); 
481 
482          / Receive from the robot controller (client) the movement 
483             command and inform to it whether the movement has been 
484             possible or not. / 
485          int receive_robot_movement(int sd, int  y, int  x) { 
486            char message[80]; 
487            memset(message, 0, 80); 
488 
489            void read_a_text_line(int sd, char  str, int length) { 
490              int n, c = 0; 
491              do { 
492                n = recv(sd, str, 1, 0); 
493                if (c++ > length) 
494                  break; 
495              } while (n > 0 && str++ != n); 
496            } 
497 
498            read_a_text_line(sd, message, 2); 
499 
500            if (message[0] == .) 
501              return END; 
502 
503            if (message[0] == W) { 
504              if (matrix[y][(x)  1] == FREE) { 
505                pthread_mutex_lock(&mut); // Semaphore in red 
506                matrix[y][x] = FREE;  // The robot leaves a matrix cell ... 
507                XSetForeground(display, gc, black); 
508                XDrawPoint(display, win, gc, x, y); 
509                if ( (x) > 1 ) (x); 
510                XSetForeground(display, gc, white); 
511                XDrawPoint(display, win, gc, x, y); 
512                XFlush(display); 
513                matrix[y][x] = OCCUPIED; // ... and occupies other. 
514                pthread_mutex_unlock(&mut); // Semaphore in green 
515              } else if (matrix[y][(x)  1] == TREASURE) { 
516                message[0] = !; 
517              } else { 
518                message[0] = #; 
519              } 
520            } else if (message[0] == E) { 
521              if (matrix[y][(x) + 1] == FREE) { 
522                pthread_mutex_lock(&mut); 
523                matrix[y][x] = FREE; 
524                XSetForeground(display, gc, black); 
525                XDrawPoint(display, win, gc, x, y); 
526                if ( (x) < (win_width2) ) (x)++; 
527                XSetForeground(display, gc, white); 
528                XDrawPoint(display, win, gc, x, y); 
529                XFlush(display); 
530                matrix[y][x] = OCCUPIED; 
531                pthread_mutex_unlock(&mut); 
532              } else if (matrix[y][(x) + 1] == TREASURE) { 
533                message[0] = !; 
534              } else { 
535                message[0] = #; 
536              } 
537            } else if (message[0] == N) { 
538              if (matrix[(y)  1][x] == FREE) { 
539                pthread_mutex_lock(&mut); 
540                matrix[y][x] = FREE; 
541                XSetForeground(display, gc, black); 
542                XDrawPoint(display, win, gc, x, y); 
543                if ( (y) > 1 ) (y); 
544                XSetForeground(display, gc, white); 
545                XDrawPoint(display, win, gc, x, y); 
546                XFlush(display); 
547                matrix[y][x] = OCCUPIED; 
548                pthread_mutex_unlock(&mut); 
549              } else if (matrix[(y)  1][x] == TREASURE) { 
550                message[0] = !; 
551              } else { 
552                message[0] = #; 
553              } 
554            } else if (message[0] == S) { 
555              if (matrix[(y) + 1][x] == FREE) { 
556                pthread_mutex_lock(&mut); 
557                matrix[y][x] = FREE; 
558                XSetForeground(display, gc, black); 
559                XDrawPoint(display, win, gc, x, y); 
560                if( (y) < (win_height2) ) (y)++; 
561                XSetForeground(display, gc, white); 
562                XDrawPoint(display, win, gc, x, y); 
563                XFlush(display); 
564                matrix[y][x] = OCCUPIED; 
565                pthread_mutex_unlock(&mut); 
566              } else if (matrix[(y) + 1][x] == TREASURE) { 
567                message[0] = !; 
568              } else { 
569                message[0] = #; 
570              } 
571            } 
572            //message[1] = n’; 
573            if(message[0] == !) { 
574              t1 = time(NULL)  t0; 
575              printf(Treasure_found_in_%ld_seconds_by_, t1); { 
576                char host[NI_MAXHOST], port[NI_MAXSERV]; 
577                int s = getnameinfo((struct sockaddr ) &sa, 
578                                    sa_len, host, NI_MAXHOST, 
579                                    port, NI_MAXSERV, NI_NUMERICSERV); 
580                if (!s) { 
581                  printf(%s:%sn, host, port); 
582                } 
583              } 
584              matrix[y][x] = TREASURE; 
585              sprintf(message+1, _Treasure_found_in_%ld_secondsn, t1); 
586            } 
587#if !defined NDEBUG 
588            printf(%s,message); 
589#endif 
590            send(sd, message, strlen(message), 0); 
591          } 
592 
593          if (receive_robot_movement((int) sd, &y, &x) == END) 
594            break; 
595        } 
596      } 
597 
598      / The CPU reaches this code only if the robot controller 
599         has disconnected using the polite way (sending a ”.”), so 
600         lets free the allocated resources for the corresponding 
601         robot. Notice that the robot remains forever in the matrix, 
602         although without further control. / { 
603        / Who is the polite client? / { 
604          char host[NI_MAXHOST], port[NI_MAXSERV]; 
605          int s = getnameinfo((struct sockaddr ) &sa, 
606                              sa_len, host, NI_MAXHOST, 
607                              port, NI_MAXSERV, NI_NUMERICSERV); 
608          if (!s) { 
609            printf(Closed_connection_with_%s:%sn, host, port); 
610          } 
611        } 
612        free(ta); // Threads args 
613        close(sd); // Socket descriptor 
614        pthread_exit(0); // Destroy the thread 
615      } 
616 
617    } / void service(void arg) / 
618 
619    / Create the thread / { 
620      pthread_t robot_tid; // Thread ID 
621      ta = malloc(sizeof(struct thread_args)); // Threads args 
622      ta>sd = serve_sd; // The socket descriptor 
623      ta>sa = serve_sa; // The socket address 
624      ta>sa_len = serve_sa_len; // Length of sa 
625      if (pthread_create(&robot_tid, NULL, robot, (void )(ta)) == 1) 
626        FATAL_ERROR(pthread_create); 
627    } 
628 
629  } // while(TRUE) 
630 
631  / Although this code is unreacheable (this is an eternal server), 
632     it is a good programming practice to tell the shell what 
633     happened. If we return a 0 then we communicate that no errors 
634     have been produced. / 
635  return 0; 
636}