You are on page 1of 10

Programacin en ambiente cliente servidor

Programacin de cliente y servidor en lenguaje C


Espinoza Manriquez Edgar Salvador y Vega Arroyo Jos Ral
a

ITESI, Irapuato Gto., Mxico

Abstract
Este reporte contiene informacin sobre la programacin de un servidor en lenguaje c utilizando un compilador del
entorno Windows el cual ser trasladado a UNIX en linux con distribucin de Ubuntu. El cual incluye informacin del uso
y manejo de sockets para la conexin mediante TCP entre 2 SO con diferente arquitectura.

1. Introduccin
Con este reporte pretende darse a conocer la bitcora de la programacin de un servidor en lenguaje
C, el cual deber hacer conexin con un cliente programado en c# el cual debe funcionar en el SO Windows y
posteriormente lograr una conexin entre alguna distribucin de Linux y OSX.
Este proyecto fue divido en dos partes, separando de la misma manera el grupo completo, teniendo as una
parte programando en c y la otra parte en c# para de esta forma llegar a un producto final el cual ser la
conexin que deberemos establecer con la pareja que nos haya tocado. Cabe aclarar que este reporte pertenece
solo a la parte de programacin en C.
Este programa correr en un equipo con las siguientes especificaciones:

HP Pavilion dm4
SO Windows 8.1 64bits (Ubuntu 14.10 64bits para el programa corriendo en Linux)
RAM de 6gb
HDD de 750 gb
Procesador Intel Core i5-2450M CPU @ 2.50Hz

Para llevar a cabo este programa necesitamos de cierto software y recursos extra en caso de no haberlos tenido
ya instalados en el equipo de trabajo. Como compilador para Windows utilizamos DevC++ para llevar a cabo
el revisado y desarrollo del programa, adems de instalar una librera especial diseada para la manipulacin
de sockets y poder as hacer conexin y comunicacin con el cliente.
Esta librera est disponible en la mayora de los ordenadores actuales sin embargo es posible que no est
instalada en algunos. En nuestro caso no fue necesario hacer la instalacin ni del compilador ni de la librera
puesto que en semestres pasados ya habamos utilizado el compilador para trabajos o proyectos referentes al
semestre y la librera ya se encontraba disponible en la laptop utilizada para hacer los programas.

Espinoza Manriquez Edgar Salvador

Para la parte de conexin con UNIX ser utilizada la distribucin de Linux UBUNTU donde est SO tendr
alojado al Servidor programado en C para hacer la conexin con OSX que tendr alojados el cliente
programado en C#.
Como compilador utilizamos GCC para distribuciones de Linux, en esta distribucin tampoco fue necesario
instalar la librera de manejo de sockets ya que el SO ya trae precargada la misma.
2. Desarrollo
Como primera prctica debimos investigar sobre el manejo de sockets en Windows mediante la
librera winsock.h, la cual es la que permite la manipulacin de estos dentro del entorno de DevC++ adems
de los diferentes tipos de sockets que existen y los dominios que pueden manejar.
Para empezar a codificar deberemos crear un nuevo proyecto al cual le dimos el nombre de uici, para
la codificacin tomamos de base el cdigo que viene en el libro de Unix Programacin Prctica el cual nos
proporcion el maestro.

2.1. Servidor (Windows)


El entorno utilizado para programar el servidor en c fue DevC++ versin 5.04 para 64 bits tomando como
referencia un cdigo ya establecido por el profesor, el cual se conecta con el cliente programado en c# a base
de sockets y seleccionando el primer puerto disponible para generar la conexin y comunicacin con el
mismo, este servidor permanece activo hasta que el cliente termine el proceso de conexin y cierre el
programa por lo que el servidor cerrara la comunicacin solo si se mata el proceso con un valor nulo.
Usando la librera dedicada al manejo de sockets (winsock.h y linwsock32.a) fue posible llevar a cabo el
siguiente programa:
Definimos el tipo short u_port_t.
typedef unsigned short u_port_t;
Abre descriptor de archivo (Leer el puerto)
int u_open(u_port_t port);//*****
identificador ligado a "port". Devuelve descriptor de
int u_listen (int fd, char* hostn);
archivo que escucha
#include <winsock.h>

Librera dedicada al manejo de sockets

#include <stdio.h>
#include <stdlib.h>
#include "uici.h"

Cabecera programada para llamada de funciones

#define MAXBACKLOG 10

SOCKET sock;

Tipo de dato socket

int u_open (u_port_t port)

Espinoza Manriquez Edgar Salvador

{
int SOCKET; //descriptor de archivo del socket
struct sockaddr_in servidor;
int len = sizeof(struct sockaddr);
WSADATA w;
int error=WSAStartup(0x0202,&w);
if (error)
{
printf("No se puede inicializar el socket");
return FALSE;
}
if (w.wVersion!=0x0202);
{
printf("La version no es la correcta");
return FALSE;
}

Funcin de apertura de socket mediante


puerto:
Esta funcin devuelve un descriptor de
archivo con el puerto dado el cual es
asignado de manera automtica.
El cual se asegura de estar inicializando el
socket
El tipo de conexin utilizada es TCP por lo
que se utiliza AF_INET, SOCK_STREAM
como tipo de socket para la conexin

if((SOCKET =socket (AF_INET,SOCK_STREAM,0))<0)


return -1;
servidor.sin_family=AF_INET;
servidor.sin_port=htons((short)port);
servidor.sin_addr.s_addr=INADDR_ANY;
if(bind(SOCKET,(struct sockaddr*)&servidor,sizeof(servidor))<0
||(listen(SOCKET,MAXBACKLOG)<0))
return -1;
return SOCKET;
}

Espinoza Manriquez Edgar Salvador

int u_listen (int fd, char* hostn)


{
struct sockaddr_in cliente;
int len = sizeof (struct sockaddr);
int retval;
int cc;
char buf[1024];
struct hostent * hostptr;

Escucha para detectar la solicitud del nodo


especificado en el puerto dado

while (((retval= accept(fd,(struct sockaddr*)(&cliente),&len))==1) && (errno==4));


if(retval==-1)
return retval;
hostptr = gethostbyaddr((char*)&(cliente.sin_addr.s_addr),4,AF_INET);
if (hostptr==NULL)
strcpy(hostn,"Desconocido");
else
strcpy(hostn,(* hostptr).h_name);

El servidor usa est funcin para escuchar la


comunicacin; este se bloquea hasta recibir una
solicitud remota del puerto vinculado con el
descriptor de archivo dado asignado a hostn

do{
cc=recv(retval,buf,sizeof(buf),0);
if (cc==0) exit (0);
if(cc>0)
printf("\n %s dice %s",hostn,buf);
else
{
printf("\n %s esta desconectado",hostn);
closesocket(fd); break;
}
}while(1);
closesocket(fd);
retval= WSACleanup();
if(retval==SOCKET_ERROR)
{
printf("Error limpiando la libreria WinSock");
return -1;
}
return retval;
}

El servidor seguir estableciendo comunicacin


hasta que se reciba un valor nulo, mandando un
error y limpiando la librera.

Espinoza Manriquez Edgar Salvador

int main(int argc, char *argv[]) {


int fd;
char clienteremoto[20];
fd= u_open(23);

El servidor seguir estableciendo comunicacin


hasta que el cliente termine su proceso, es
entonces cuando la conexin termina

if (fd!=-1)
if(u_listen(fd,clienteremoto)!=-1);
getch();
return 0;
}

2.2. Servidor (Linux fallido)


Para la programacin del servidor en linux se tom de referencia el programa hecho en windows, pero a
pesar de cambiar las libreras correspondientes a Windows, no fue posible adaptarlo por completo por lo que
se decidi hacer un programa nuevo basado en uno existente el cual fue modificado para las funciones
necesarias al proyecto.
A continuacin se explicar el cdigo basado en el usado en Windows y el libro Unix programacin
prctica de las pginas 439 y 440 y la cabecera (uici.h) localizada en la pgina 599:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include uici.h
Void main (int argc, char *argv[])
{
u_port_t portnumber;
int listenfd;
int communfd;
char client [MAX_CANON]
int bytes_copied;
if (argc != 2)
{
fprintf(stderr, Usage: %s Puerto\n, argv[0];
exit(1);
}

Espinoza Manriquez Edgar Salvador

portnumber = (u_port_t) atoi(argv[1]);


if ((listenfd = u_open (portnumber)) == -1 )
{
u_error(No estabecido para establecer conexin con puerto);
exit(1);
}
while((communfd = u_listen(listenfd, client)) != -1)
{
fprintf(stderr, Conexin establecida con %s \n,client);
}
exit (0);
}

Cabecera:
Typedef unsigned short u_port_t;
int u_open (u_port_t port);
int u_listen (int fd, char *hostn);
ssize_t u_read(int fd, void *buf, size_t nbyte);
ssize_t u_write(int fd, void *buf, size_t nbyte);

2.3. Servidor (Linux funcional)


Este cdigo final fue hecho basado en investigacin por libros y foros en internet para generar un servidor
el cual fue trabajado en conjunto con algunos compaeros para acelerar su funcionamiento, el siguiente
cdigo fue modificado para la necesidad establecida por el proyecto el cual tiene 2 ficheros de nombre server
y socket_server respectivamente, una cabecera dedicada a cada fichero con el mismo nombre y un makefile
que permite la compilacin desde terminal en ubuntu.
SERVER
#include "Socket_server.h"
#include <string.h>
#include <stdio.h>
main ()
{
int Socket_Servidor;
int Socket_Cliente;
char Cadena[100];

Descriptores del socket


server y del socket
cliente

Socket_Servidor = Abre_Socket_Inet ("port");


if (Socket_Servidor == -1)

Apertura del socket server con el


servicio dado de alta el cual fue
ingresado mediante consola con el
nombre port y asignado el puerto
17500

Espinoza Manriquez Edgar Salvador

{
printf ("No se puede abrir socket servidor\n");
exit (-1);
}
Socket_Cliente = Acepta_Conexion_Cliente (Socket_Servidor);
if (Socket_Servidor == -1)
{
printf ("No se puede abrir socket de cliente\n");
exit (-1);
}
Lee_Socket (Socket_Cliente, Cadena, 5);
printf ("Soy Servidor, he recibido : %s\n", Cadena);

strcpy (Cadena, "Adios");


Escribe_Socket (Socket_Cliente, Cadena, 6);

Funcin que espera y acepta


la conexin del cliente, el
cual lee la informacin
suponiendo que va a enviar 5
caracteres, esto debido a que
si se ingresa un nmero
mayor el servidor no
imprime nada hasta que el
numero permitido se cumpla
ms 1 caracter ms que
ocupa el espacio de enter
en cdigo ASCII

/* Se cierran los sockets */


close (Socket_Servidor);
}
SOCKET SERVER
int Lee_Socket (int fd, char *Datos, int Longitud)
{
int Leido = 0;
int Aux = 0;
if ((fd == -1) || (Datos == NULL) || (Longitud < 1))
return -1;
while (Leido < Longitud)
{
Aux = read (fd, Datos + Leido, Longitud - Leido);
if (Aux > 0)
{
Leido = Leido + Aux;
}
else
{
if (Aux == 0)
return Leido;

Esta Funcin lee los


datos del socket
generado, supone que se
le pasa a un buffer un
nuero determinado de
bytes leidos, en caso de
recibir un 0 o -1 esta lo
interpreta como error y
genera comprobacin de
parametros

Espinoza Manriquez Edgar Salvador

if (Aux == -1)
{

switch (errno)
{
case EINTR:
case EAGAIN:
usleep (100);
break;
default:
return -1;
}
}
}
}

En cualquier caso de error la variable declarada


errno indica el tipo de error cometido:
EINTR para cualquier interrupcin de sistema
antes de leer ningn dato
EGAIN para cuando el socket no est disponible
Ambos errores tienen un rango de espera de 100
microsegundos los cuales pueden ser modificados
Si existe un error diferente el programa provoca
que salga de la funcin con error

return Leido;
}
int Escribe_Socket (int fd, char *Datos, int Longitud)
{
int Escrito = 0;
int Aux = 0;

Una vez devueltos los caracteres ledos


Escribe_Socket devuelve el nmero de
bytes escritos o el error para despus
comprobar los parmetros de entrada.

if ((fd == -1) || (Datos == NULL) || (Longitud < 1))


return -1;

Despus entra un bucle que se detiene


hasta que hayamos escrito todos los
caracteres que nos han indicado, si
hemos conseguido escribir caracteres,
este actualiza la variable Escrito.

while (Escrito < Longitud)


{
Aux = write (fd, Datos + Escrito, Longitud - Escrito);
if (Aux > 0)
{

Una vez cerrado el socket devuelve un


nmero de caracteres ledos, en caso de
error devuelve 1

if (Aux == 0)
return Escrito;
else
return -1;
}
}
/* Devolvemos el total de caracteres leidos */
return Escrito;
}

Espinoza Manriquez Edgar Salvador

CABECERA SOCKET (Socket.h)


#ifndef SOCKET_H_INCLUDED
#define SOCKET_H_INCLUDED
int Lee_Socket (int fd, char *Datos, int Longitud);
int Escribe_Socket (int fd, char *Datos, int Longitud);

Llamada de las funciones


Lee_Socket y Escribe Socket para el
uso del fichero con el mismo
nombre (Socket.c)

#endif // SOCKET_H_INCLUDED

CABECERA SOCKET SERVER (socket_server.h)


#ifndef SOCKET_SERVIDOR_H_INCLUDED
#define SOCKET_SERVIDOR_H_INCLUDED
int Abre_Socket_Inet (char *Servicio);
int Abre_Socket_Unix (char *Servicio);
int Acepta_Conexion_Cliente (int Descriptor);

Llamada de las funciones


Abre_Socket_Inet para conexin
AF_INET
Abre_Socket_Unix para conexin
con sockets AF_UNIX
Y Acepta_Conexion_Cliente para
llevar a cabo la funcin de
conexin y comunicacin

#endif // SOCKET_SERVIDOR_H_INCLUDED
MAKEFILE
all : Socket.o Socket_server.o server
CPPFLAGS = -g -I.
server : server.c
cc -g -I. Socket.o Socket_server.o server.c -o server
clean :
rm *.o server

El comando de linux make nos


ayuda a compilar nuestros
programas. Presenta muchas
ventajas para programas grandes, en
los que hay muchos ficheros fuente
(muchos .c y muchos .h) repartidos
por varios directorios.

Espinoza Manriquez Edgar Salvador

References
Ttulo
Autores
Edicin
Editor
ISBN
N. de pginas

Unix programacin prctica


Kay A. Robbins, Steven Robbins
ilustrada
Pearson Educacin, 1997
9688809594, 9789688809594
659 pginas

Unix : programacin avanzada


Autor
Francisco Manuel Mrquez Garca
Edicin
3
Editor
Ra-Ma, 2004
ISBN
8478976035, 9788478976034
N. de pginas
632 pginas

10

You might also like