P8. Comunicaciones - Sistemas Informáticos Industriales (1774)

Anuncio
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
Práctica 8: Sockets en Windows
Modelo cliente/servidor Streams Sockets (TCP)
1. Objetivos
Los objetivos de esta práctica son:
o
o
Aprender a programar sockets de tipo Stream para transmisión por TCP
Aprender el funcionamiento de la librería WinSock
Para ello realizaremos un chat desarrollando un servidor y varios clientes.
2. Introducción teórica
Como hemos visto en clase de teoría, fundamentalmente, debemos tener claro qué
tipo de aplicación queremos programar con sockets, ya que en función de la rapidez
con la que deben transmitirse los datos y si deseamos o no tener un control de errores,
debemos diferenciar entre Sockets Stream y Sockets Datagram.
Recordemos que los primeros emplean el protocolo TCP, mientras que los segundos
hacen uso del protocolo UDP. Cada uno tiene sus ventajas e inconvenientes, por
ejemplo, recordemos que TCP nos provee un flujo de datos bidireccional,
secuenciado, libre de errores y sin duplicación de paquetes; mientras que en UDP los
paquetes pueden llegarnos fuera de secuencia, incluso no llegar o que éstos
contengan errores. Sin embargo, UDP es extensamente empleado para transferencia
de información ágil, como vídeo o audio a través de la red.
En la práctica que nos ocupa, emplearemos el primero de ellos (TCP) para llevar a
cabo una comunicación entre un servidor y varios clientes remotos.
Un aspecto a tener en cuenta para la realización de un chat es que deberán pedirse al
usuario los mensajes que quiera enviar. Para ello, podemos usar las funciones scanf o
gets. Pero estas funciones presentan un gran inconveniente, ya que una vez
ejecutadas, el programa se bloquea hasta que el usuario introduzca una cadena de
texto y pulse intro. Por tanto, mientras un usuario no introduzca su mensaje no puede
comprobar si está recibiendo mensajes. En programación, la solución más
ampliamente utilizada para resolver esto es el uso de hilos (threads). Estos permiten
que varias funciones se estén ejecutando de forma simultánea, y por tanto, mientras
una función solicita información al usuario, otra está comprobando si recibe mensajes.
Puesto que en clase no se ha visto el uso de hilos, solucionaremos este problema
creando programas (exe) independientes, cada uno de los cuales tenga una
determinada función.
1/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
Por tanto, la estructura para realizar esta práctica estará formada por un servidor y 2
clientes:
•
Tendremos un cliente tipo receptor. Su objetivo será el de recibir desde el
servidor todos los mensajes escritos por ambos usuarios. Este sería el
equivalente a la ventana del chat donde se muestran los mensajes.
•
Tendremos un cliente tipo escritor. Su objetivo será el de solicitar al usuario los
mensajes que desee mandar y enviarlos al servidor. Este sería el equivalente a
la ventana del chat donde el usuario escribe los mensajes.
•
Finalmente tendremos un servidor que se encargará de aceptar las conexiones
con los 4 clientes. Este recibirá los mensajes de los clientes receptores y los
enviará a los dos clientes escritores.
Cada usuario deberá ejecutar en su ordenador tanto un cliente escritor como un cliente
receptor. El servidor podrá ejecutarse en una de los ordenadores de los clientes o
incluso en un tercer ordenador.
2/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
3. Descripción del servidor y clientes y tareas a realizar
Para facilitar la práctica, se proporcionan los códigos tanto del servidor (P8_servidor.c)
como de ambos clientes (P8_cliente_receptor.c y P8_cliente_escritor.c) faltando
únicamente el bloque donde se intercambian los menajes. A continuación se describe
la secuencia de comunicación en pseudo código para servidor y clientes indicando la
parte a incluir por el alumno para completar la práctica:
P8_servidor.c
1. Se definirán las variables y estructuras que intervienen en la comunicación.
2. Se inicicializará la DLL de winsock.
3. Se obtendrá la IP que utilizará nuestro servidor (localhost).
4. Se creará el socket y se asociará IP y puerto (3000) al mismo.
5. Se habilitan conexiones entrantes.
6. Comunicación inicial:
a. Se aceptarán 4 conexiones entrantes, 2 de clientes receptores y 2 de
clientes escritores.
i. Al establecer la conexión se pregunta al cliente de que tipo es,
receptor o escritor, y se almacena dicha información en las
variables.
ii. Además, si es un cliente escritor se pedirá a este que se
identifique con un nombre de usuario (nick).
7. Una vez finalizado y puesto que no se van a aceptar más conexiones entrantes
se cierra el socket de escucha.
8. Finalmente se envía a los 4 clientes un mensaje indicándoles que todos están
preparados para comenzar el intercambio de mensajes.
9. Parte a incluir por el alumno:
a. Se realizará un bucle para recibir mensajes de los clientes escritores. Al
recibir un mensaje de uno de ellos (y en caso de no estar vacío) se
enviará a los dos clientes receptores. Antes de enviar cada mensaje se
añadirá al principio de este el nick del cliente que lo ha enviado.
b. En caso de que el mensaje recibido sea “exit” o “EXIT” se finalizará el
bucle y se enviará a los clientes escritores dicha palabra para que estos
sepan que también deben finalizar.
10. Se cierra el socket y la librería winsock.
3/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
P8_cliente_receptor.c
1. Se definirán las variables y estructuras que intervienen en la comunicación
2. Se inicicializará la DLL de winsock.
3. Se indicará la IP que utilizará el servidor (localhost)
4. Se creará el socket y se definirá el puerto destino (3000).
5. Se realizará la conexión con el servidor
6. Comunicación inicial:
a. Se espera recibir un mensaje de bienvenida por el servidor donde
pregunta qué tipo de cliente es.
b. Se envía al servidor el mensaje “Cliente receptor”.
c. Se queda a la espera del mensaje del servidor que indique que todos
los clientes están preparados.
7. Parte a incluir por el alumno:
a. Se realizará un bucle para recibir mensajes del servidor (y en caso de
no estar vacío) mostrarlos por pantalla.
b. En caso de que el mensaje recibido sea “exit” o “EXIT” se finalizará el
bucle
8. Se cerrará el socket y se cerrará la librería winsock.
4/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
P8_cliente_escritor.c
1. Se definirán las variables y estructuras que intervienen en la comunicación
2. Se inicicializará la DLL de winsock.
3. Se indicará la IP que utilizará el servidor (localhost)
4. Se creará el socket y se definirá el puerto destino (3000).
5. Se realizará la conexión con el servidor
6. Comunicación inicial:
a. Se espera recibir un mensaje de bienvenida por el servidor donde
pregunta qué tipo de cliente es.
b. Se envía al servidor el mensaje “Cliente escritor”.
c. Se espera un mensaje por parte del servidor donde nos solicita el nick.
d. Se solicita al usuario su nick y se envía al servidor.
e. Se queda a la espera del mensaje del servidor que indique que todos
los clientes están preparados.
7. Parte a incluir por el alumno:
a. Se realizará un bucle donde se solicita al usuario el mensaje que desee
mandar y se envía al servidor.
b. En caso de que el mensaje recibido sea “exit” o “EXIT” se finalizará el
bucle.
c. Tras enviar cada mensaje se comprobará si el servidor nos ha enviado
algún mensaje que indique que otro cliente ha decidido finalizar la
conexión.
8. Se cerrará el socket y se cerrará la librería winsock.
5/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
4. Cómo añadir librería libws2_32.a a nuestro proyecto
Para poder usar la librería winsock es necesario seguir los siguientes pasos para incluirla en
nuestro proyeto:
1. File
New Project y escogemos: Empty Project.
2. Click con botón derecho en Project1 y New File.
3. Pestaña: Project
Project Options.
4. En la nueva ventana que aparece nos vamos a la pestaña Parameters y
Pulsamos el botón Add Library or Objet.
5. Buscamos la librería libws2_32.a que está ubicada en (según la instalación):
a. “C:/Dev-Cpp/MinGW32/lib/”
b. "C:/Program Files (x86)/Dev-Cpp/MinGW32/lib/libws2_32.a"
6/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
ANEXO: Principales estructuras a emplear con sockets bajo Windows
y prototipos de las funciones más importantes.
Recordemos las principales estructuras que se deben emplear en sockets bajo el
sistema operativo Windows:
•
Estructura sockaddr_in:
Es una estructura que contiene una dirección de internet
struct sockaddr_in
{
short
sin_family; /* must be AF_INET */
u_short sin_port;
struct in_addr sin_addr;
char
sin_zero[8]; /* Not used, must be zero */
};
•
Estructura hostent:
Estructura que define un host en internet
struct hostent
{
char
*h_name;
/* official name of host */
char
**h_aliases;
/* alias list */
int
h_addrtype;
/* host address type */
int
h_length;
/* length of address */
char
**h_addr_list; /* list of addresses from name server
*/
#define h_addr h_addr_list[0] /* address, for backward
compatiblity */
};
Asimismo, tal y como hemos visto en clase de teoría, los prototipos de algunas de las
funciones más importantes que debemos emplear son las siguientes:
•
Función WSAStartup
Inicializa la Dll encargada de los sockets de Windows.
int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData);
• Función WSACleanup
Libera la DLL encargada de los sockets de Windows.
int WSACleanup (void);
• Función socket:
SOCKET socket (int af, int type, int protocol);
7/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
•
Función bind
int bind (SOCKET sock, const struct sockaddr FAR* name, int name
len);
•
Función listen
int listen (SOCKET sock, int backlog);
•
Función accept
SOCKET accept (SOCKET sock, struct sockaddr FAR* addr, int FAR*
addrlen);
• Función connect
int connect (SOCKET sock, const struct sockaddr FAR* name, int n
amelen);
• Función send
int send (SOCKET sock, const char FAR * buf, int len, int flags)
;
• Función recv
int recv (SOCKET sock, char FAR* buf, int len, int flags);
• Función closesocket
int closesocket (SOCKET sock);
• Función getprotobyname
Retorna un puntero a una estructura la cual contiene el nombre y el número del
protocolo correspondiente con el nombre ingresado.
struct protoent FAR * getprotobyname (const char FAR * name );
•
Función getsockname
Devuelve la dirección del socket.
int getsockname (SOCKET sock, struct sockaddr FAR* name, int FAR
* namelen );
•
Función gethostbyaddr
Obtiene información del Host a través de su dirección.
struct hostent FAR * gethostbyaddr (const char FAR * addr, int l
en, int type);
• Función gethostbyname
Obtiene información del Host a través de su nombre.
struct hostent FAR * gethostbyname (const char FAR * name);
8/9
Escuela Politécnica Superior de Elche
Grado de Ing. Electrónica y Automática Industrial
Sistemas Informáticos Industriales
Curso 2015-16
• Función gethostname
Obtiene información del Host Local Predeterminado.
int gethostname (char FAR * name, int namelen );
• Función inet_addr
Convierte una dirección IP en notación números y puntos, en un unsigned long,
retorna la dirección en network byte order.
unsigned long inet_addr (const char FAR * cp );
• FUNCIÓN ADICIONAL
Estas dos líneas permiten que la función recv no se quede bloqueada a la espera de
recibir un mensaje. El tiempo seleccionado es de 0.1 segundos. Por tanto, si en 0.1
sesgundos no ha recibido ningún mensaje la ejecución del programa continúa. De esta
forma, en un bucle se pueden comprobar las recepciones de varios clients.
DWORD sock_timeout = 0.1*1000;
setsockopt(comm_socket,
SOL_SOCKET,
SO_RCVTIMEO,
(char*)&sock_timeout, sizeof(sock_timeout));
9/9
Descargar