Transmisin TCP

Vicente González Ruiz

December 26, 2013

Contents

1 El servidor
2 Compilacin del servidor
3 Ejecucin del servidor
4 El cliente

En esta prctica vamos a disear un servidor simple basado en el TCP. El objetivo es aprender cmo funciona la API del TCP tanto en C como en Java.

1 El servidor

Nuestro servidor es una aplicacin escrita en C que mantiene una variable que puede ser modificada por los clientes. Cada cliente es servido en un hilo diferente que perdura tanto tiempo como la conexin con el cliente. Como puede haber ms de un cliente conectados de forma simultanea, se trata, por tanto, de un servidor concurrente.

El servidor imprimir por la consola el valor actual de una variable compartida. Este proceso se ejecutar hasta que pulsemos <CTRL> + <C> en la consola donde hemos ejecutado el servidor.

A continuacin se muestra el cdigo fuente del servidor (http://www.ace.ual.es/\~vruiz/redes/practicas/servidor.c):

 
1/* 
2 * servidor.c 
3 * 
4 * Acepta conexiones TCP a travs del puerto PORT para que distintos 
5 * clientes puedan modificar una variable compartida 
6 * (shared_value). Las acciones que pueden realizar los clientes 
7 * dependen del valor especificado en cada interaccin. Si el valor 
8 * introducido es <= -2, el cliente es desconectado. Si el valor 
9 * introducido es -1, el cliente es informado del valor actual de la 
10 * variable compartida. En cualquier otro caso, el valor introducido 
11 * es tomado por la variable compartida. 
12 * 
13 * Para detener el servidor, pulsar <CTRL>+<C> en la consola donde se 
14 * lanz el servidor. 
15 * 
16 * Compilar escribiendo: 
17 * 
18 * gcc servidor.c -o servidor -lpthread 
19 * 
20 * gse. 2007. 
21 * 
22 */ 
23 
24/****************************************************************************** 
25 * 
26 * Ficheros cabecera. 
27 * 
28 *****************************************************************************/ 
29 
30/* Entrada y salida de streams buffereados. */ 
31#include <stdio.h> 
32 
33/* Biblioteca de funciones estndar (exit(), EXIT_SUCCESS, 
34   EXIT_FAILURE, ...) */ 
35#include <stdlib.h> 
36 
37/* Manipulacin de cadenas y movimiento de memoria (memset(), ...). */ 
38#include <string.h> 
39 
40/* Biblioteca estndar de funciones relacionadas con el sistema 
41   operativo Unix (read(), write(), ...). */ 
42#include <unistd.h> 
43 
44/* POSIX Threads (pthread_t, pthread_create(), ...). */ 
45#include <pthread.h> 
46 
47/* Sockets. */ 
48/* Ms info en: 
49 * 
50 * http://www.csce.uark.edu/~aapon/courses/os/examples/concurrentserver.c 
51 * http://beej.us/guide/bgnet/ 
52 */ 
53 
54/* Tipos de datos primitivos del sistema. */ 
55#include <sys/types.h> 
56 
57/* Sockets (socket(), listen(), ...). */ 
58#include <sys/socket.h> 
59 
60/* Direcciones IP, opciones y definiciones. */ 
61#include <netinet/in.h> 
62 
63/* Servicio de resolucin de nombres. */ 
64#include <netdb.h> 
65 
66/* Signals (interrupciones) (signal(), SIGINT). */ 
67#include <signal.h>  /* signal(), SIGINT */ 
68 
69/****************************************************************************** 
70 * 
71 * Definiciones. 
72 * 
73 *****************************************************************************/ 
74 
75/* Puerto de escucha del servidor. */ 
76#define PORT 6789 
77 
78/* Nmero mximo de clientes que esperan a que la conexin sea 
79   establecida. */ 
80#define QLEN 1 
81 
82/****************************************************************************** 
83 * 
84 * Variables globales. 
85 * 
86 *****************************************************************************/ 
87 
88/****************************************************************************** 
89 * 
90 * Funciones. 
91 * 
92 *****************************************************************************/ 
93 
94/* Cuerpo principal del programa. */ 
95int main(int argc, char *argv[]) { 
96  work(argc, argv); 
97  return EXIT_SUCCESS; 
98} 
99 
100/* Funcin que realiza todo el trabajo. */ 
101work(int argc, char *argv[]) { 
102 
103  /* Un semforo para acceder "en exclusin mutua" a una zona de 
104     cdigo. */ 
105  pthread_mutex_t mut; 
106 
107  /* Creamos el semforo. */ 
108  pthread_mutex_init(&mut, NULL); 
109 
110  /* La variable compartida que puede ser controlada por los clientes. */ 
111  int shared_value = 0; 
112 
113  /* Creamos el socket TCP de escucha. */ 
114  int listen_sd; { 
115 
116    /* Obtenemos el nmero del protocolo. */ 
117    struct protoent *ptrp; /* Pointer to a protocol table entry. */ 
118    ptrp = getprotobyname("tcp"); 
119    if(ptrp ==NULL) { 
120      perror("getprotobyname"); 
121      exit(EXIT_FAILURE); 
122    } 
123 
124    /* Creamos el socket descriptor. */ 
125    listen_sd = socket (PF_INET, SOCK_STREAM, ptrp->p_proto); 
126    if(listen_sd < 0) { 
127      perror("socket"); 
128      exit(EXIT_FAILURE); 
129    } 
130 
131    /* Usaremos el puerto de servicio sin esperar a que ste est 
132       a la espera de un time-out de liberacin. */ 
133    int yes = 1; 
134    if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { 
135      perror("setsockopt"); 
136      exit(EXIT_FAILURE); 
137    } 
138  } 
139 
140  /* Asignamos una direccin (dir IP, puerto) al socket de escucha. */ { 
141    struct sockaddr_in sad;              /* Direccin del servidor. */ 
142    memset((char  *)&sad,0,sizeof(sad)); /* Borramos la estructura. */ 
143    sad.sin_family = AF_INET;            /* Usaremos Internet. */ 
144    sad.sin_addr.s_addr = INADDR_ANY;    /* Cualquiera de las IP 
145                                            asignadas al host vale. */ 
146    sad.sin_port = htons((u_short)PORT); /* Asignamos el puerto de 
147                                            escucha. */ 
148    if (bind(listen_sd, (struct sockaddr *)&sad, sizeof (sad)) < 0) { 
149      perror("bind"); 
150      exit(EXIT_FAILURE); 
151    } 
152  } 
153 
154  /* Escuchamos. El servidor todava no se bloquea. */ 
155  if (listen(listen_sd, QLEN) < 0) { 
156    perror("listen"); 
157    exit(EXIT_FAILURE); 
158  } 
159 
160  fprintf(stderr,"%s:esperandoconexiones...\n", argv[0]); 
161 
162  /* Si pulsamos CRTL+C, el programa acaba ejecutando la funcin 
163     end(). */ 
164  void end() { 
165    fprintf(stderr,"%s:<CTRL>+<C>detectado.Saliendo...\n",argv[0]); 
166 
167    /* Cerramos el socket de escucha. */ 
168    close(listen_sd); 
169 
170    /* Salimos. */ 
171    exit(EXIT_SUCCESS); 
172  } 
173 
174  signal(SIGINT, end); 
175 
176  /* Lazo del servidor. */ 
177  while(1) { 
178 
179    /* Socket para "servir" a los clientes. */ 
180    int serve_sd; 
181 
182    /* El servidor se bloquea esperando a que un cliente se 
183       conecte. */ { 
184      struct sockaddr_in cad;        /* Clients address. */ 
185      socklen_t alen = sizeof(cad);  /* Tamao de la direccin. */ 
186      serve_sd = accept(listen_sd, (struct sockaddr *)&cad, &alen); 
187      if(serve_sd<0) { 
188        perror("accept"); 
189        exit (EXIT_FAILURE); 
190      } 
191    } 
192 
193    fprintf(stderr,"%s:conexinaceptada!\n",argv[0]); 
194 
195    /* Hilo que controla a un cliente. */ 
196    void *service(void *arg) { 
197 
198      /* El socket de conmunicacin con el cliente. Ntese que cada 
199         cliente posee una variable "sd" diferente. */ 
200      int sd = (int)arg; 
201 
202      for(;;) { 
203        int new_shared_value; 
204 
205        inform_to_client(sd, shared_value); 
206        receive_from_client(sd, &new_shared_value); 
207 
208        /* Looping mientras new_shared_value > -2. */ 
209        if(new_shared_value < -1) break; 
210 
211        /* Modificamos shared_value mientras new_shared_value > -1. */ 
212        if(new_shared_value > -1) { 
213          /* Seccin crtica. */ 
214          pthread_mutex_lock(&mut); 
215          shared_value = new_shared_value; 
216          fprintf(stderr,"%s:shared_value=%d\n", argv[0], shared_value); 
217          pthread_mutex_unlock(&mut); 
218          /* Fin de la seccin crtica. */ 
219        } 
220 
221      } 
222 
223      /* El cliente ha introducido -2. */ 
224 
225      /* Cerramos la conexin con el cliente. */ 
226      close(sd); 
227 
228      /* Finalizamos el hilo. */ 
229      pthread_exit(0); 
230    } 
231 
232    /* Lanzamos el hilo. */ 
233    pthread_t service_tid; /* Thread identificator. */ 
234    if(pthread_create(&service_tid, NULL, service, (void *)serve_sd) == -1) { 
235      perror("pthread_create"); 
236      exit(EXIT_FAILURE); 
237    } 
238  } 
239 
240} 
241 
242/* Informa a un cliente sobre el valor de shared_value. */ 
243inform_to_client(int sd, int shared_value) { 
244  char message[80]; 
245  memset(message, 80, 0); 
246  sprintf(message,"shared_value=%d.Introduzcanuevovalor...", shared_value); 
247  send(sd, message, strlen(message), 0); 
248} 
249 
250/* Recibe del cliente un nuevo valor. */ 
251receive_from_client(int sd, int *shared_value) { 
252  char message[80]; 
253  read_text_line(sd, message); 
254  sscanf(message,"%d",shared_value); 
255} 
256 
257/* Lee una lnea de texto ASCII. */ 
258read_text_line(int sd, char *str) { 
259  int n; 
260  do { 
261    n = recv(sd, str, 1, 0); 
262  } while (n>0 && *str++ != \n); 
263}

2 Compilacin del servidor

Compile el servidor escribiendo:

gcc servidor.c -o servidor -lpthread

3 Ejecucin del servidor

Ejecute el servidor escribiendo:

./servidor

4 El cliente

El usar un socket TCP y transmitir los datos en ASCII nos permite utilizar el programa Telnet como cliente. He aqu una interaccin ejemplo:

usuario$ telnet localhost 6789  
Trying 127.0.0.1...  
Connected to localhost.  
Escape character is ’^]’.  
shared_value = 0. Introduzca nuevo valor ... 3  
shared_value = 3. Introduzca nuevo valor ... -1  
shared_value = 3. Introduzca nuevo valor ... -2  
Connection closed by foreign host.

Como puede verse, en cada interaccin el cliente obtiene el valor actual de la variable compartida shared_value y puede especificar un valor distinto. En funcin del valor especificado podemos:

Ejercicio 1: Escriba una versin del servidor usando el lenguaje de programacin Java. Compruebe que su versin funciona correctamente.