You are on page 1of 77

Programacin en Redes

Ms. C. Santiago Cisneros

ESPOCH

BIBLIOGRAFA
Internetworking with TCP/IP, volumes I- III Douglas Comer and David Stevens - Prentice
Hall

By ordering
Network byte order y Host byte order son
dos formas en las que el sistema puede
almacenar
los
datos
en
memoria.
Est relacionado con el orden en que se
almacenan los bytes en la memoria RAM.

By ordering
Si al almacenar un short int (2 bytes) o un
long int (4 bytes) en RAM, en la posicin ms
alta se almacena el byte menos significativo,
entonces est en network byte order, caso
contrario es host byte order

By ordering
Esto depende del microprocesador que se est
utilizando, podramos estar programando en
un sistema host byte order o network byte
order, pero cuando enviamos los datos por la
red deben ir en un orden especificado, sino
enviaramos todos los datos al revs. Lo
mismo sucede cuando recibimos datos de la
red, debemos ordenarlos al orden que utiliza
nuestro sistema.

By ordering
Debemos cumplir las siguientes reglas :
Todos

los bytes que se transmiten hacia la


red, sean nmeros IP o datos, deben estar en
network byte order.
Todos

los datos que se reciben de la red,


deben convertirse a host byte order.

By ordering

Para realizar estas conversiones utilizamos las


funciones que se describen a continuacin.

Funciones de conversin
htons()

- host to network short - convierte


un short int de host byte order a network byte
order.
htonl()

- host to network long - convierte un


long int de host byte order a network byte
order.

Funciones de conversin
ntohs()

- network to host short - convierte


un short int de network byte order a host byte
order.
ntohl()

- network to host long - convierte un


long int de network byte order a host byte
order.

Funciones de conversin
Puede ser que el sistema donde se est
programando almacene los datos en network
byte order y no haga falta realizar ninguna
conversin, pero si tratamos de compilar el
mismo cdigo fuente en otra plataforma host
byte order no funcionar.

Funciones de conversin
Como conclusin, para que nuestro cdigo
fuente sea portable se debe utilizar siempre las
funciones de conversin.
Para ms informacin : man 3 byteorder

Funciones de conversin Ejemplo


#include
...
port = htons ( 3490 ); // Convertimos a
network byte order el nmero de port que
utilizamos.
...

Multiplexacin
La multiplexacin es la combinacin de dos o
ms canales de informacin en un solo medio
de transmisin usando un dispositivo llamado
multiplexor. El proceso inverso se conoce
como demultiplexacin. Un concepto muy
similar es el de control de acceso al medio.

Multiplexacin
Nuevamente si al almacenar un short int (2
bytes) o un long int (4 bytes) en RAM, en la
posicin ms alta se almacena el byte menos
significativo, entonces est en network byte
order, caso contrario es host byte order

Multiplexacin
Existen muchas estrategias de multiplexacin
segn el protocolo de comunicacin
empleado, que puede combinarlas para
alcanzar el uso ms eficiente; los ms
utilizados son:
La

multiplexacin por divisin de tiempo o


TDM (Time division multiplexing ).

Multiplexacin
La

multiplexacin por divisin de frecuencia


o FDM (Frequency-division multiplexing) y su
equivalente para medios pticos, por divisin
de longitud de onda o WDM (de Wavelength
division multiplexing).
La

multiplexacin por divisin en cdigo o


CDM (Code division multiplexing).

Multiplexacin
Cuando existe un esquema o protocolo de
multiplexacin pensado para que mltiples
usuarios compartan un medio comn, como
por ejemplo en telefona mvil o WiFi, suele
denominarse control de acceso al medio o
mtodo de acceso mltiple. Como mtodos
de acceso mltiple destacan:

Multiplexacin
El

acceso mltiple por divisin de frecuencia


o FDMA.
El

acceso mltiple por divisin de tiempo o


TDMA.
El

acceso mltiple por divisin de cdigo o


CDMA.

Direccionamiento
Cada

punto terminal de la comunicacin


tiene una direccin
Considerar

dos procesos de comunicacin


sobre el internet
La red debe ser especificada
El
punto terminad debe ser
especificado
El proceso debe ser especificado

Programas y procesos
Un

programa es un archivo ejecutable

Un

proceso o tarea es una instancia de un


programa que esta siendo ejecutado
Un

programa puede generar mltiples


procesos

10

Cliente - Servidor
Un

servidor
dispositivo.

es

un

proceso,

no

un

Un

servidor espera por requerimientos de un


cliente.
Un

cliente es un proceso que enva una


peticin a un servidor existente y usualmente
espera por una respuesta.

Cliente Servidor (Ejemplos)


Servidor

Time-of-day

Servidor

de archivos

Servidor

de impresin

Servidor

de acceso a disco

Servidor

de transacciones

11

Servidor
Los

servidores son generalmente ms


complicados
Tipos de servidores
Interactivo: Es el servidor que maneja
un cliente a la vez
Concurrente:

Es el servidor que
maneja varios cliente a la vez

API,
Network Application Programing
Interface
Son los servicio provedos a menudo por el
sistema operativo que proporcionan una
interfaz entre las aplicaciones y el protocolo
de software.
Grupo de procedimientos que puede usar un
programa de aplicacin para comunicarse por
una red. El nombre se debe a que el grupo
incluye un procedimiento socket que debe
llamarse para establecer la comunicacin

12

API,
Network Application Programing
Interface

API Network
Caractersticas
Interface

genrica de programacin
Soporte para orientacin de mensajes y
comunicaciones orientadas a conexin.
Servicios de entrada/salida
Independencia del sistema operativo
Servicios para la Capa de Presentacin

13

API Genrica
Caractersticas
Soporte

para mltiples familias de protocolo


Direccionamiento independiente
Servicios de entrada/salida
Servicios para clientes y servidores

Estructura de un programa en C

Lenguaje de programacin estructurado de


propsito general.

C tiene caractersticas adicionales que permiten


su uso a un nivel mas bajo que los lenguajes de
alto nivel tradicionales

14

Estructura de un programa en C

Funcin principal Main()

Cada funcin debe contener:


Cabecera
Lista de declaracin de argumentos
Una sentencia compuesta. (encerrada en un
par de llaves)
/* para escribir comentarios*/

Estructura de un programa en C
#include<stdio.h>
/*programa para calcular el area de un circulo*/
Main()
{
Float radio, area;
Printf(Radio = ?);
Scanf(%f, &radio);
Area=3.1416*radio*radio;
Printf(Area = %f,area);
}

15

Tipos de Datos
Int:

Cantidad entera. 2 bytes o una palabra


Char: Carcter. 1 byte
Float: Numero de coma flotante. 1 palabra.
Double: Nmero de coma flotante de doble
precisin. 2 palabras.
Algunos tipos bsicos de datos se pueden
ampliar usando los cualificadores de tipos de
datos short (corto) y long (largo), signed(con
signo) y unsigned(sin signo)

Entrada y Salida de Datos


Getchar();

Entrada de caracteres uno a uno

char c;
c= getchar();
Putchar();

Visualizar un carcter. Tx un
carcter al dispositivo de salida estndar.
char c;
putchar(c);

16

Entrada y Salida de Datos


Scanf();

Datos a la computadora desde el


dispositivo de in estndar
scanf (cadena de control, arg1, arg2, .., arg
n);

En la cadena de control se incluyen grupos


individuales de caracteres, con un grupo de
caracteres por cada dato de entrada . %
caracter de conversin que indica el tipo de
dato

Entrada y Salida de Datos


Printf();

Escribir datos en el dispositivo de


salida estndar
printf (cadena de control, arg1, arg2, .., arg
n);
En la cadena de control se incluyen
informacin de salida de arg 1, 2 etc.
Los argumentos pueden ser const, variables
simples o arrays.

17

Entrada y Salida de Datos


Gets()

y Puts(); Transferencia de informacin


entre la PC y los dispositivos de in y out.
Cada una de estas funciones aceptan un solo
argumento(debe ser un array de caracteres)

Socket
Es una interfaz de entrada-salida de datos que
permite la intercomunicacin entre procesos.
Es
una representacin abstracta de una
comunicacin.
Los procesos pueden estar ejecutndose en el
mismo o en distintos sistemas, unidos mediante
una red.

18

Socket

Los sockets trabajan con servicios


entrada/salida, archivos y FIFOS

de

Es una interfaz de entrada-salida de datos que


permite la intercomunicacin entre procesos.

Los sockets tienen necesidades especficas:


Establecimiento de la conexin
Direccionamiento

Sockets

Forma de comunicarse con otros programas


usando descriptores de fichero estndar de Unix.
Qu es un descriptor de fichero?
Cada fichero que esta siendo usado por un proceso tiene al
menos un descriptor de fichero (file descriptor) que lo identifica.
El descriptor de fichero es un nmero entero que se usa como
ndice en la tabla de descriptores de fichero.

Hay tres posiciones especiales: (0) entrada estndar, (1) salida


estndar, y (2) salida de errores.

19

Sockets
Para conectarse con un df a travs de internet se
usa la llamada al sistema socket(). Esta devuelve
un descriptor de fichero y se puede comunicar
con l usando las llamadas al sistema
especializadas send() y recv() ( man send , man
recv ).

20

Dominios de comunicacin.
Los sockets se crean dentro de un dominio de
comunicacin, igual que un archivo se crea
dentro de un filesystem. El dominio de
comunicacin nos dice donde se encuentran los
procesos que se van a intercomunicar.
Si los procesos estn en el mismo sistema, el
dominio de comunicacin ser AF_UNIX, si los
procesos estn en distintos sistemas y estos se
hallan unidos mediante una red TCP/IP, el
dominio de comunicacin ser AF_INET.

21

Dominios de comunicacin.
Los sockets no se han diseado solamente para
TCP/IP. La idea original fue que se usase la
misma interfaz tambin para distintas familias de
protocolos.
Algunos dominios:

AF_INET ( unidos mediante una red TCP/IP).


AF_UNIX (en el mismo sistema).
Otros dominios.

Tipos de Sockets
Existen varios tipos de sockets:
- DARPA, sock de internet
- sock de unix
- sock x.25
2 tipos de Sock de internet.
El primer tipo de sockets lo definen los sockets de flujo
[Stream sockets];
El otro, los sockets de datagramas [Datagram sockets],

22

Tipos de Sockets
"SOCK_STREAM" y "SOCK_DGRAM ".
En ocasiones, a los sockets de datagramas se les
llama tambin "sockets sin conexin". (Aunque
se puede usar connect() con ellos, si se quiere).

Tipos de sockets en el dominio


AF_INET.

Sockets Stream.

Sockets Datagram.

Sockets Raw.

23

Sockets Stream

Sockets Stream son los ms utilizados, hacen


uso del protocolo TCP, el cual nos provee un
flujo de datos bidireccional, secuenciado, sin
duplicacin de paquetes y libre de errores.

La especificacin del protocolo TCP se puede


leer en la RFC-793 .

Sockets Datagram

Hacen uso del protocolo UDP, el cual nos


provee un flujo de datos bidireccional, pero los
paquetes pueden llegar fuera de secuencia,
pueden no llegar o contener errores.

Por lo tanto el proceso que recibe los datos debe


realizar resecuenciamiento, eliminar duplicados y
asegurar la confiabilidad.

24

Sockets Datagram
Se llaman tambin sockets sin conexin, porque
no hay que mantener una conexin activa, como
en el caso de sockets stream.
Son utilizados para transferencia de informacin
paquete por paquete. Ejemplo: dns, tftp, bootp,
etc.
Ellos implementan un protocolo encima de UDP
que realiza control de errores.
La especificacion del protocolo UDP se puede
leer en la RFC-768.

Sockets Raw

No son para el usuario ms comn, son


provistos
principalmente
para
aquellos
interesados en desarrollar nuevos protocolos de
comunicacin o para hacer uso de facilidades
ocultas
de
un
protocolo
existente.

25

Sockets de Conexin

Los sockets de flujo definen flujos de


comunicacin en dos direcciones, fiables y con
conexin.
Si se enva dos tems a travs del socket en el
orden "1, 2; llegarn al otro extremo en el orden
"1, 2", y llegarn sin errores.

Sockets sin Conexin


Por qu se les llama sockets sin conexin?

Los sockets de datagramas tambin usan IP para el


encaminamiento

Bsicamente no se tiene que mantener una


conexin abierta como se hara con los sockets
de flujo. Simplemente se monta un paquete, se
aade una cabecera IP con la informacin de
destino y se enva.

26

Sockets sin Conexin


Por qu se les llama sockets sin conexin?

Como se puede notar no se necesita conexin.


Generalmente se usan para transferencias de
informacin por paquetes. Aplicaciones que usan
este tipo de sockets son, por ejemplo tftp.

Resumen
Todo lo que se hace con los sockets de flujo es usar
send() para enviar datos.
Para los sockets de datagramas se tiene que encapsular el
paquete segn el mtodo de eleccin y se enva usando
sendto(). El ncleo implementa los niveles de Internet y de
Transporte para que sea transparente, y el hardware el
nivel de acceso a la red.

27

Creando un Socket
Los sockets se crean llamando a la funcin
socket(), esta funcin retorna un descriptor
de socket, que es tipo int.
Si hubo algn error, socket() retorna -1 y la
variable global errno se establece con un valor
que indica el error que se produjo

sockfd = socket ( int dominio/familia, int


tipo, int protocolo );

Creando un Socket

sockfd = socket ( int dominio/familia, int tipo,


int protocolo );
sockfd: es el descriptor de socket devuelto. Luego se
utilizar para conectarse, recibir conexiones, enviar y
recibir datos, etc.
dominio: Dominio donde se realiza la conexin.
Para este caso ser PF_INET(AF_INET )

28

Creando un Socket

sockfd = socket ( int dominio/familia, int tipo,


int protocolo );
tipo:
Podr
ser
SOCK_STREAM
o
SOCK_DGRAM o SOCK_RAW.
protocolo: 0 (cero, selecciona el protocolo ms
apropiado).

Creando un Socket - Ejemplo


#include
....
int sockfd;
sockfd =socket ( AF_INET, SOCK_STREAM, 0 );
...
O

Si se especifica al protocolo como cero, el sistema


selecciona el protocolo apropiado de uno de los
protocolos disponibles, dependiendo del tipo de socket
requerido.

29

Tipos de datos POSIX

Ms tipos de datos POSIX

30

Direccionamiento genrico

Estructura sockaddr
Almacenan el nombre del socket. Se utilizan con la funcin
bind(). Una direccin que me permite usar sockets para
comunicarme con subprocesos(hijos).

struct sockaddr
{
unsigned short sa_family; // AF_*
char sa_data[14]; // Direccin de protocolo.
};

sockaddr, almacena la direccin de protocolo para muchos tipos


de protocolos.
sa_family puede ser AF_INET, AF_UNIX u otros dominios
sa_data contiene la direccin IP y nmero de puerto asignado al
socket.

31

Estructura sockaddr_in
Almacenan el nombre del socket. Se utilizan con la funcin
bind().

struct sockaddr_in
{
short int sin_family; // AF_INET
unsigned short sin_port; // Numero de puerto.
struct in_addr sin_addr; // Direccin IP.
unsigned char sin_zero[8]; // Relleno.
};

32

Estructura sockaddr_in

Un tipo especial de la estructura sockaddr

Se cre la estructura sockaddr_in para el caso de


internet, para poder referenciar los elementos
de forma ms fcil.

Los punteros a la estructura sockaddr_in deben ser


precedidos con un cast tipo *struct sockaddr antes
de pasarlos como parmetros a funciones.

Estructura sockaddr_in

sin_family ser AF_INET

sin_port (nmero de puerto) y sin_addr (direccin IP)


debern estar en network byte order, osea habr que usar
htons().

sin_family no debe convertirse a network byte order porque


es solo usado por el kernel y no es enviado por la red.

sin_zero se utiliza para rellenar la estructura a la longitud de


sockaddr, debe estar inicializada a cero con la funcin
bzero().

33

AF_INET
Para PF_INET se necesitan:

16 bits pata el nmero de puerto


32 bits para la direccin ip (IPV4)

in_addr provee un nombre para lenguaje C que se pueda


asociar con una direccin IP.

34

Nuevamente estructuras
Network Byte Order

Nuevamente estructuras
Network Byte Order
Todos los valores almacenados en la estructura sockaddr_in
deberan estar en network byte order

35

Nuevamente estructuras
Network Byte Order

No es necesario usar las estructuras sockaddr, ya


que vamos a trabajar con familias de protocolos
reales.

Consecuentemente
sockaddr_in.

Sin embargo las funciones de C que levantan las


API de sockets esperan estructuras sockaddr

se

usan

las

estructuras

36

Asignando una direccin a un


Socket Funcin Bind
El socket se crea sin nombre, debemos asignarle uno para poder
recibir conexiones.
bind () se utiliza para darle un nombre al socket, sea una
direccin IP y nmero de puerto del host local por donde
escuchar, al especificar una IP del host local le estamos diciendo
por cual interfaz fsica escuchar (el sistema puede tener varias
interfaces ethernet, ppp, etc).
Es necesario llamar a bind() cuando se est programando un
servidor.

Asignando una direccin a un


Socket Funcin Bind
Cuando se est programando un cliente generalmente no se
utiliza esta funcin, el kernel le asignar al socket la direccin IP y
nmero de puerto disponible al llamar a ciertas funciones, por
ejemplo cuando llamamos a connect() para conectarnos con un
sistema remoto.
En el servidor es necesario llamar a bind() debido a que el
nmero de puerto debe ser conocido para que los clientes puedan
conectarse. Por ejemplo si estamos programando un servidor de
telnet debemos llamar a bind() para asignarle al socket el puerto
23.

37

Asignando una direccin a un


Socket Funcin Bind
En la aplicacin cliente se puede llamar a bind() y asignarle un
nmero de puerto, pero no es necesario porque nadie va a tratar ni
podr conectarse con l.

int bind(int sockfd, const struct sockaddr *my_addr, int


addrlen)
sockfd : Es el descriptor de socket devuelto por la funcin
socket().
my_addr : Es un puntero a una estructura sockaddr que contiene
la IP del host local y el nmero de puerto que se va a asignar al
socket.
addrlen : Debe ser establecido al tamao de la estructura
sockaddr. sizeof(struct sockaddr).

Asignando una direccin a un


Socket Funcin Bind
sockaddr_in
struct sockaddr_in sin;
...bind ( sockfd, (struct sockaddr *) &sin, sizeof (sin) );
o

38

Ejemplo de la funcin Bind()

Otras funciones comnmente


usadas
inet_ntoa()
Convierte una direccin IP en unsigned long en network byte order,
a un string en nmeros y puntos.
Ejemplo:
printf("%s", inet_ntoa(ina.sin_addr));
inet_ntoa() retorna un puntero a un array de caracteres, que se
encuentra almacenado estticamente dentro de inet_ntoa(). Cada vez
que se llama a inet_ntoa() se sobreescribe la ltima direccin IP.

39

Otras funciones comnmente


usadas
inet_ntoa()
Ejemplo:
char *addr1, *addr2;
addr1=inet_ntoa(ina1.sin_addr) /* Supongamos que vale
200.41.32.127 */
addr2=inet_ntoa(ina2.sin_addr) /* Supongamos que vale
132.241.5.10 */
printf("direccion 1: \n", addr1);
printf("direccion 2: \n", addr2);
Esto imprimir:
direccion 1: 132.241.5.10
direccion 2: 132.241.5.10

Otras funciones comnmente


usadas
Se ve que la primer direccin IP es sobreescrita en la segunda
llamada a inet_ntoa() y se pierde su valor.
Para que esto no suceda, luego de la primera llamada a inet_ntoa() se
debe usar strcpy() para guardar la primera direccin IP y luego
llamar por segunda vez a inet_ntoa(), de esta manera no se pierde la
primera direccin IP.

40

Otras funciones comnmente


usadas
inet_addr()

Convierte una direccin IP en notacin nmeros y puntos, en un


unsigned long, retorna la direccin en network byte order. Retorna 1 si hubo error.
Ejemplo:
struct sockaddr_in ina;
....
ina.sin_addr.s_addr=inet_addr("192.168.1.1");

Asignacin de valores a una


variable tipo sockaddr_in
Debemos asignarle valores a una variable tipo sockaddr_in antes de llamar a las
funcin bind().
Veamos un ejemplo:
...
..
struct sockaddr_in my_addr;
......
my_addr.sin_family = AF_INET;
my_addr.sin port = htons ( 3490 ); // Numero de puerto por donde
escuchar el servidor.
my_addr.sin_addr.s_addr = inet_addr ("132.241.5.10"); // IP de la interface
por donde escuchar el servidor.
bzero ( &(my_addr.sin_zero), 8); // Relleno con ceros.

41

Asignacin de valores a una


variable tipo sockaddr_in
Si asignamos el valor cero a sin_port, el sistema nos dar automticamente un
puerto disponible.
my_addr.sin_port=0;
Podemos automatizar la asignacin de la IP, si ponemos el valor INADDR_ANY
a s_addr, el sistema le asignar la direccin IP local. Recordar que el programa
puede ejecutarse en distintas PC's con distintas IP's
my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
Las variables my_addr.sin_port y my_addr.sin_addr.s_addr deben estar en
network byte order, son valores que viajan por la red, pero my_addr.sin_family
no porque solo es utilizado por el kernel para saber que tipo de direccin contiene
la estructura.

Otras funciones comnmente


usadas

42

RESUMEN

43

44

Pasos para establecer la conexin


Caso sockets stream

45

Pasos para establecer la conexin


Caso sockets stream
Ambos, cliente y servidor, deben crean un socket mediante la
funcin socket(), para poder comunicarse.
El servidor llama a bind() para darle un nombre al socket, para luego
poder recibir conexiones, es decir establece por cual nmero de
puerto escuchar. Por ejemplo si este sera un servidor de telnet,
establecera el puerto 23. Para el cliente no es necesario establecer el
nmero de puerto, porque no recibir intentos de conexin, slo
intentar conectarse con el servidor .
El servidor habililita su socket para poder recibir conexiones,
llamando a la funcin listen(). El cliente no necesita realizar este
paso porque no va a recibir conexiones, solo intentar conectarse
con el servidor.

Pasos para establecer la conexin


Caso sockets stream
El servidor ejecuta la funcin accept() y queda en estado de espera,
la funcin accept() no retorna hasta que intenten conectarse. El
cliente usa la funcin connect() para realizar el intento de conexin,
en ese momento la funcin accept() del servidor retorna con un
parmetro que es un nuevo descriptor de socket, el cual se utiliza
para realizar la transferencia de datos por la red con el cliente.
Una vez establecida la conexin se utilizan las funciones send() y
recv() con el descriptor de socket del paso anterior para realizar la
transferencia de datos.
Para finalizar la conexin se utilizan las funciones close() o
shutdown().

46

Pasos para establecer la conexin


Caso sockets datagram

Pasos para establecer la conexin


Caso sockets datagram
Ambos, cliente y servidor, crean un socket mediante la funcin
socket().
El servidor debe establecer por qu nmero de puerto recibir los
datos. En este caso no existe la conexin, los datos se envan como
si fueran mensajes.
Para realizar transferencia de datos, utilizan las funciones sendto() y
recvfrom().

Se visualiza que no es necesario que el servidor llame a la funcin


listen(), ni tampoco el paso connect() / accept

47

Descripcin de funciones
listen ()
Se llama desde el servidor, habilita el socket para que pueda recibir
conexiones. Solo se aplica a sockets tipo SOCK_STREAM.
int listen ( int sockfd, int backlog)
sockfd : Es el descriptor de socket devuelto por la funcin socket()
que ser utilizado para recibir conexiones.
backlog : Es el nmero mximo de conexiones en la cola de entrada
de conexiones. Las conexiones entrantes quedan en estado de espera
en esta cola hasta que se aceptan ( accept () ).

Descripcin de funciones
accept()
Se utiliza en el servidor, con un socket habilitado para recibir
conexiones ( listen() ). Esta funcin retorna un nuevo descriptor de
socket al recibir la conexin del cliente en el puerto configurado.
La llamada a accept() no retornar hasta que se produce una
conexin
o
es
interrumpida
por
una
seal.
int accept ( int sockfd, void *addr, int *addrlen)

48

Descripcin de funciones
accept()
sockfd : Es el descriptor de socket habilitado para recibir
conexiones.
addr : Puntero a una estructura sockadd_in. Aqu se almacenar
informacin de la conexin entrante. Se utiliza para determinar que
host est llamando y desde qu nmero de puerto.
addrlen : Debe ser establecido al tamao de la estructura sockaddr.
sizeof(struct sockaddr).
accept() no escribir ms de addrlen bytes en addr . Si escribe
menos bytes, modifica el valor de addrlen a la cantidad de bytes
escritos.

Ejemplo:

Descripcin de funciones

...
int sockfd, new_sockfd;
struct sockaddr_in my_addr;
struct sockaddr_in remote_addr;
int addrlen;
...
//Creo el socket
sockfd = socket (AF_INET, SOCK_STREAM, 0 );
...
// Se le asigna un nmero de puerto al socket por donde el servidor escuchar.
// Antes de llamar a bind() se debe asignar valores a my_addr.
bind (sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr) );
// Se habilita el socket para poder recibir conexiones.
listen ( sockfd, 5);
addrlen = sizeof (struct sockaddr );
...
// Se llama a accept() y el servidor queda en espera de conexiones.
new_sockfd = accept ( sockfd, &remote_addr, &addrlen);

49

Descripcin de funciones
connect()
Inicia la conexin con el servidor remoto, lo utiliza el cliente para
conectarse.

int connect ( int sockfd, struct sockaddr *serv_addr, int addrlen )


sockfd: Es el descriptor de socket devuelto por la funcin socket().
serv_addr: Es una estructura sockaddr que contine la direccin IP y
nmero de puerto destino.
addrlen: Debe ser inicializado al tamao de struct sockaddr
( sizeof (struct sockaddr) ).

Descripcin de funciones
send() y recv()
Despus de establecer la conexin, se puede comenzar con la
transferencia de datos.
Estas dos funciones son para realizar transferencia de datos sobre
sockets stream.
send() y recv() son idnticas a write() y read(), excepto que se agrega
un parmetro.

50

Descripcin de funciones
send()
send ( int sockfd, const void *msg, int len, int flags )
sockfd : Descriptor socket por donde se enviarn los datos.
msg : Puntero a los datos a ser enviados.
len : Longitud de los datos en bytes.
flags : Banderas/Leer: man 2 send
send() retorna la cantidad de datos enviados, la cual podr ser menor
que la cantidad de datos que se escribieron en el buffer para enviar .
send() enviar la mxima cantidad de datos que pueda manejar y
retorna la cantidad de datos enviados, es responsabilidad del
programador comparar la cantidad de datos enviandos con len y si
no se enviaron todos los datos, enviarlos en la prxima llamada a
send().

Descripcin de funciones
recv()
recv ( int sockfd, void *buf, int len, unsigned int flags )
sockfd : Descriptor socket por donde se recibirn los datos.
buf : Puntero a un buffer donde se almacenarn los datos recibidos.
len : Longitud del buffer buf.
flags : Banderas/Leer: man 2 recv
Si no hay datos a recibir en el socket , la llamada a recv() no retorna
(bloquea) hasta que llegan datos, se puede establecer al socket como
no bloqueante (fcntl ) de manera que cuando no hay datos para
recibir la funcin retorna -1 y establece la variable
errno=EWOULDBLOCK, recv() retorna el nmero de bytes
recibidos.

51

Descripcin de funciones
sendto() y recvfrom()
Funciones para realizar transferencia de datos sobre sockets
datagram

Descripcin de funciones
sendto()
int sendto(int sockfd, const void *msg, int len, unsigned int
flags, const struct sockaddr *to, int tolen)
sockfd : Descriptor socket por donde se enviarn los datos.
msg : Puntero a los datos a ser enviados.
len : Longitud de los datos en bytes.
flags : Banderas/Leer: man 2 sendto
to : Puntero a una estructura sockaddr que contiene la direccin IP y
nmero de puerto destino.
tolen : Debe ser inicializado al tamao de struct sockaddr
( sizeof (struct sockaddr) ).
sendto() retorna el nmero de bytes enviados, el cual puede ser
menor que len, igual que en send

52

Descripcin de funciones
recvfrom()
int recvfrom ( int sockfd, void *buf, int len, unsigned int flags, struct
sockaddr *from, int *fromlen )
sockfd : Descriptor socket por donde se recibirn los datos.
buf : Puntero a un buffer donde se almacenarn los datos recibidos.
len : Longitud del buffer buf.
flags : Banderas
from : Puntero a una estructura sockaddr que contiene la direccin IP y nmero
de puerto del host origen de los datos.
fromlen : Debe ser inicializado al tamao de struct sockaddr
( sizeof (struct sockaddr) ).
Si no hay datos a recibir en el socket , la llamada a recvfrom() no retorna (bloquea)
hasta que llegan datos, se puede establecer al socket como no bloqueante (fcntl )
de manera que cuando no hay datos para recibir la funcin retorna -1 y establece
la variable errno=EWOULDBLOCK, recvfrom() retorna el numero de bytes
recibidos, igual que recv().

Descripcin de funciones
close () y shutdown ()
Finalizan la conexin del descriptor de socket.

close ( sockfd)
Despus de utilizar close, el socket queda deshabilitado para realizar lecturas o
escrituras.

shutdown (sockfd, int how)


Permite deshabilitar la comunicacin en una determinada direccin o en
ambas direcciones.
how : Especifica en qu direccin se deshabilita la comunicacin. Puede tomar
los siguientes valores :
0 : Se deshabilita la recepcin.
1 : se deshabilita el envo.
2 : se deshabilitan la recepcin y el envo, igual que en close

53

Paso de argumentos

main(int argc, char *argv[])


{
int s;
if (argc == 4) {
// Creamos el socket
s = socket(AF_INET,SOCK_DGRAM,0);
if (s != -1) {
bs.sin_family = AF_INET;
bs.sin_port = htons(0); //Coge cualquier puerto disponible
bs.sin_addr.s_addr = htonl(INADDR_ANY); //Coge cualquier IP
//Asigna un nombre local al socket
if( bind(s,(struct sockaddr*)&bs, sizeof(bs)) != -1)

Paso de argumentos
{
des.sin_family = AF_INET;
des.sin_addr.s_addr = inet_addr(argv[1]);
des.sin_port = htons(atoi(argv[2]));

Para correr
Gcc nombre.c o nombre(creo el archivo ejecutable)
./nombre IP destino puerto mensaje
./ argv 0 argv 1
argv2 argv3

54

getpeername() --Quin eres t?


La funcin getpeername() te dir quin est al otro lado de un socket
de flujo conectado:
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

sockfd es el descriptor del socket de flujo conectado,


addr es un puntero a una estructura struct sockaddr (o struct
sockaddr_in) que guardar la informacin acerca del otro lado de
la conexin,
addrlen es un puntero a un int, que deberas inicializar a
sizeof(struct sockaddr) .

GETHOSTNAME()
La funcin gethostname () devuelve el nombre del ordenador
sobre el que tu programa se est ejecutando. El nombre puede
usarse entonces con gethostbyname(), para determinar la
direccin IP de tu mquina local.
#include <unistd.h>
int gethostname(char *hostname, size_t size);
Los argumentos son sencillos: hostname es un puntero a una
cadena de caracteres donde se almacenar el nombre de la
mquina cuando la funcin retorne, y size es la longitud en bytes
de esa cadena de caracteres.
gethostname () retorna 0 si se ejecuta con xito.

55

struct hostent()
struct hostent *gethostbyname (const char *name)
Se utiliza para convertir un nombre de un host a su direccin IP,
sea utiliza el servidor de nombres.
Ejemplo: Supongamos que estamos programando un cliente
telnet.
$ telnet algun.sitio.com
La aplicacin cliente primero debe trasladar el nombre del sitio
(algun.sitio.com) a conectarse a su direccin IP, para luego poder
realizar todos los pasos de conexin anteriormente descritos.
Retorna un puntero a una estructura hostent, que esta formada
como sigue:

struct hostent()
struct hostent
{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
#define h_addr h_addr_list[0]
En el caso de producirse algun error devuelve NULL y establece
la variable h_errno con el numero de error, en vez de la variable
errno ( ver man herror)

56

struct hostent()
Descripcion de los campos de la estructura hostent:
h_name : Nombre oficial del host.
h_aliases : Array de nombres alternativos.
h_addrtype : Tipo de direccin que se retorno ( AF_INET ).
h_length : Longitud de la direccin en bytes.
h_addr_list : Array de direcciones de red para el host.
h_addr : La primer direccin en h_addr_list.

Resumen

57

58

59

CODIGO DEL CLIENTE


Connect()
La llamada connect() es como sigue:

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

60

CODIGO DEL CLIENTE


Veamos un ejemplo:
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define DEST_IP "10.12.110.57"
#define DEST_PORT 23
main()
{
int sockfd;
struct sockaddr_in dest_addr; /* Guardar la direccin de
destino */

61

CODIGO DEL CLIENTE


sockfd = socket(AF_INET, SOCK_STREAM, 0);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(DEST_PORT);
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
/* no olvides comprobar los errores de connect()! */
connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct
sockaddr));

SEND() Y RECV()
Estas dos funciones sirven para comunicarse a travs de
sockets de flujo o sockets de datagramas conectados. Si quieres usar
sockets de datagramas desconectados normales se tiene que usar sendto
() y recvfrom ().
La llamada al sistema send():
int send(int sockfd, const void *msg, int len, int flags);
char *msg = Hola!";
int len, bytes_sent;
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);

62

SEND() Y RECV()
La llamada al sistema recv() es similar en muchos aspectos:

int recv(int sockfd, void *buf, int len, unsigned int flags);
sockfd es el descriptor del fichero del que se va a leer, buf es el buffer donde se
va a depositar la informacin leida, len es la longitud mxima del buffer, y
flags, que puede asignarse a 0.

SENDto() Y RECVfrom()
Puesto que los sockets de datagramas no estn conectados a una mquina
remota, para enviar un paquete debemos especificar la direccin de destino!
Asi:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct
sockaddr *to, int tolen);
la misma que send() aadiendo dos items ms de informacin. to
es un puntero a una estructura struct sockaddr que contiene la direccin IP y
el puerto de destino. Al argumento tolen asgnale el valor sizeof(struct
sockaddr).

63

SENDto() Y RECVfrom()
La misma semejanza presentan recv() y recvfrom(). La sinopsis de
recvfrom() es:
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
struct sockaddr *from, int *fromlen);
De nuevo, es igual que recv() pero con dos argumentos ms. from
es un puntero a una estructura struct sockaddr local que ser rellenada con la
direccin IP y el puerto de la mquina de origen. fromlen es un puntero a un
int local que tiene que inicializarse a sizeof(struct sockaddr).

close() y shutdown()
Hasta ahora se ha utilizado (send()) para enviar y se ha estado
recibiendo con (recv() ) datos. Para cerrar la conexin de tu
descriptor de socket, hay que usar normalmente la funcin Unix
close() que cierra descriptores de fichero:

close(sockfd);
Slo en el caso que quieras un poco ms de control sobre cmo
se cierra el socket puedes usar la funcin shutdown(). Te permite cortar la
comunicacin en un cierto sentido, o en los dos (tal y como lo hace close())

64

close() y shutdown()
int shutdown(int sockfd, int how);
sockfd es el descriptor de socket que quieres desconectar, y how es uno de los siguientes
valores:
0 -- No se permite recibir ms datos
1 -- No se permite enviar ms datos

2 -- No se permite enviar ni recibir ms datos (lo mismo que close())


Si se usa shutdown() en un socket de datagramas, simplemente inhabilitar el
socket para posteriores llamadas a send() y recv().
Es importante destacar que shutdown() no cierra realmente el descriptor
de fichero slo cambia sus condiciones de uso. Para liberar un descriptor
de socket necesitas usar close().

Ejemplo cliente-servidor
TCP simple
Luego del accept() el servidor queda
en estado de espera hasta que el
cliente intente conectarse.
El cliente trata de conectarse con
connect(); accept() retorna con un
nuevo descriptor de socket, el cual es
utilizado por el server para realizar la
transferencia de datos con el cliente.
Mientras esta realizando transferencia
de datos con un cliente, los intentos
de conexin de otros clientes son
almacenados en una cola de
conexiones, que luego ser atendida.
Los clientes se atienden de a uno por
vez en este tipo de aplicacin clienteservidor.

65

Ejemplo cliente-servidor TCP concurrente


Luego del accept() el servidor
queda en estado de espera. Cuando
el cliente intenta conectarse,
accept() retorna con un nuevo
descriptor de socket, pero antes de
comenzar la transferencia de datos,
el servidor realiza un fork(),
apareciendo en memoria un
proceso hijo igual que el servidor,
que solo se dedica a realizar la
transferencia de datos con el
cliente, mientras que el proceso
padre retorna al estado en espera.
De esta forma, el servidor puede
atender a varios clientes en forma
simultanea, por cada cliente que
esta
atendiendo
en
forma
simultanea, aparecer un proceso
hijo ejecutndose.

Servidor / Cliente Stream


Servidor
Veremos un servidor stream simple, lo nico que hace es enviar la
frase "Hello World" hacia el cliente.
Para probarlo, ejecutar esta programa en una terminal y ejecutar el
telnet desde otra terminal de la siguiente manera:
telnet localhost 3490
Cdigo fuente del servidor
Cliente
Este cliente se conecta con el servidor anterior y visualiza el string
"Hello World
Cdigo fuente del cliente

66

Servidor / Cliente Datagram


Servidor
Este servidor espera recibir paquetes en el puerto 4950, nos informa
quien nos envio el paquete, que longuitud tiene y nos muestra su
contenido.
Cdigo fuente del servidor
Cliente
Este cliente enviara un mensaje al servidor anterior, especificandose
en la linea de comando el nombre del host donde esta ejecutandose
el servidor y el mensaje a enviar.

Cdigo fuente del cliente

Sockets no bloqueantes
Supongamos que creamos un socket y nos conectamos a un
servidor, sabemos que el socket nos provee una comunicacin
bidireccional, podemos enviar y recibir datos simultneamente.
Llamamos a la funcin recv() para recibir datos, pero el servidor
en ese momento no tiene nada para enviarnos, entonces la funcin
recv() no retorna. Justo en ese momento queremos enviar datos
hacia el servidor, pero no podemos porque la funcin recv() no
retorno y nos bloqueo el programa.
Debemos encontrar alguna forma para que la funcin recv()
retorne aunque el servidor no envi nada. Esto se realiza
estableciendo al socket como no bloqueante.

67

Sockets no bloqueantes

Cuando creamos un socket, se establece como bloqueante, al llamar


a ciertas funciones como accept(), recv() , recvfrom(), etc, se
bloquea el programa.
Para establecer al socket como no bloqueante utilizamos la funcin
fcntl() de la siguiente manera :

int fcntl ( int sockfd, int cmd, long arg);


sockfd: descriptor de socket sobre el cual se va a realizar alguna
operacin.
cmd: determina el comando que se va a aplicar, para nuestro caso
usaremos el comando F_SETFL, el cual establece los flag del
descriptor al valor especificado en arg.
arg: argumentos que necesita el comando, para establecer el socket
como no bloqueante ser O_NONBLOCK.

Sockets no bloqueantes
Ejemplo:
#include <unistd.h>
#include <fcntl.h>
...
sockfd=socket(AF_INET, SOCK_STREAM, 0);
fcntl (sockfd, F_SETFL, O_NONBLOCK);
...
Una vez establecido el socket como no bloqueante, se puede llamar
a la funciones bloqueantes como recv() para recibir datos, si no hay
datos
disponibles
recv()
devuelve
-1
y
establece
errno=EWOULDBLOCK. Se puede ir consultando (polling) el
socket para saber si hay datos disponibles, pero esta no es una
solucin muy buena, se consume tiempo de CPU consultando al
socket si tiene datos. Consecuentemente se usa la Funcin select()

68

select()

Nos permite monitorear un conjunto de descriptores de sockets y


nos avisa cuales tienen datos para leer, cuales estn listos para
escribir, y cuales produjeron excepciones.
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select ( int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct
timeval *timeout);
Monitorea 3 conjuntos distintos de sockets, aquellos agregados al
conjunto readfds a quienes los monitorea para ver si hay caracteres
disponibles para leer, al conjunto writefds los monitorea para ver si
estn listos para ser escritos y al conjunto exceptfds los monitorea
para ver si se producen excepciones. Los conjuntos de descriptores
son tipo fd_set.

select()
Se proveen 4 macros para manejar los conjuntos de descriptores :
FD_ZERO ( fd_set *set ): limpia un conjunto de descriptores.
FD_SET ( int fd, fd_set *set ): agrega fd a un conjunto.
FD_CLR ( int fd, fd_set *set ): borra fd de un conjunto.
FD_ISSET ( int fd, fd_set *set ): Verifica si fd esta dentro de un
conjunto. Se utiliza luego del retorno de select() para verificar cual
descriptor cambio su estado.

69

select()

n: Es el nmero de descriptor mss alto de cualquiera de los 3


conjuntos, mas uno.
timeout: Select puede retornar por dos causas, se produce algn
cambio en un descriptor o paso ms del tiempo especificado en
timeout sin producirse cambios. Si se establece timeout a cero se
retorna inmediatamente, si establecemos timeout a NULL se
monitorea hasta que se produce algn cambio en los conjuntos,
sea puede bloquear. Cuando retorna, timeout indica el tiempo
restante.
Cuando retorna select(), modifica los conjuntos de descriptores
reflejando cual de los descriptores esta listo para leer, cuales para
escribir y cuales causaron excepciones.
Si se produce un error, retorna -1 y se establece errno con el
numero de error.

select()
Veamos la estructura timeval que es utilizada para definir un tiempo
de espera para la lectura o escritura de datos. Quizs cada 96
segundos se quiera imprimir "An estoy vivo..." aunque no haya
sucedido nada. Esta estructura de tiempo te permite establecer un
perodo mximo de espera.
Si transcurre ese tiempo y select() no ha encontrado an ningn
descriptor de fichero que est listo, la funcin regresar para que se
pueda seguir procesando.
struct timeval

{
int tv_sec;
int tv_usec;
};

/* segundos */
/* micro segundos */

70

select()
Ejemplo : Monitoreamos la entrada estandard ( descriptor 0 )
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define STDIN 0
/* descriptor para la entrada estandard */
main()
{
struct timeval timeout;
fd_set readfds;
timeout.tv_sec = 2;
timeout.tv_usec = 500000 ;
FD_ZERO ( &readfds );
FD_SET ( STDIN, &readfds );
select ( STDIN+1, &readfds, NULL, NULL, &timeout );
if ( FD_ISSET ( STDIN, &readfds ) )
printf (" Se oprimio una tecla\n");
else
printf(" se vencio el tiempo\n");
}

(THREADS)
Un thread es un concepto sencillo: un simple flujo de control
secuencial. Con un nico thread existe en cualquier instante un
nico punto de ejecucin. El programador no necesita aprender
nada nuevo para programar un nico thread.
Sin embargo cuando se tienen mltiples hilos en un programa
significa que en cualquier instante el programa tiene mltiples
puntos de ejecucin, uno en cada uno de sus threads. El
programador decide cuando y donde crear mltiples threads,
ayudndose de una librera o paquete en tiempo de ejecucin
Ahora veremos cmo usar mltiples hebras o hilos de control
para efectuar mltiples tareas dentro de un mismo proceso. Un
aspecto ineludible al hablar de compartir recursos son los
mecanismos de sincronizacin necesarios para conservar la
consistencia de los recursos compartidos.

71

(THREADS)
Uno de los problemas que se tenia al utilizar mltiples threads de ejecucin
es que hasta hace poco no exista un estndar para ello la extensin Posix1.c
se aprob en junio de 1995 con la adopcin de un estndar Posix para los
hilos, se estn haciendo mas comunes las aplicaciones con Threads.
Todas las fuentes que empleen Posix, deben incluir el archivo de
encabezado pthread.h con la directiva

Una vez que tenemos la librera instalada deberemos compilar el programa


y ligarlo con dicha librera en base al compilador que se utilice. La librera
de hilos POSIX.1c debe ser la ltima librera especificada en la lnea de
comandos del compilador

(THREADS)
Por ejemplo la forma mas usual de hacer lo anterior, si estamos usando un
compilador GNU como gcc, es el comando:

Cada hilo tiene la siguiente informacin especifica:


Un identificador de hilo nico
La poltica de planificacin y la prioridad
Una variante errno por hilo
Datos especficos por hilo
Gestores de cancelacin por hilo

72

(THREADS)

(THREADS)
Las operaciones llevadas a cabo sobre un hilo son:
Creacin y destruccin
Sincronizacin entre hilos
Posibilidad de disponer para cada thread memoria local propia
Gestin de prioridades entre hilos
Gestin de seales

73

(THREADS)
Un thread tiene identificador (ID), una pila, una prioridad de ejecucin
y una direccin de inicio de ejecucin:

Administracin de Hilos
Un paquete de manejo de hilos generalmente incluye funciones para:
crear y destruir un hilo, itineracin, forzar exclusin mutua y espera
condicionada.
Los hilos de un proceso comparten variables globales, descriptores de
archivos abiertos, y pueden cooperar o interferir con otros hilos.

(THREADS)
Todas las funciones de hilos del POSIX comienzan con pthread. Entre ellas estn:

74

(THREADS)
Identificacin de Hilos
As como un proceso tiene un PID (Process Identification), cada hilo
tiene un identificador de hilo. Mientras los PID son enteros no
negativos, el ID de un hilo es dependiente del SO y puede ser una
estructura.

(THREADS)
Creacin de Hilos
Los procesos normalmente corren como un hilo nico. La creacin
de un nuevo hilo se logra va pthread_create.

Nota: restrict es una palabra reservada para sealar al compilador que el dato
referenciado slo ser accedido en la funcin a travs de ese puntero.

75

(THREADS)
Creacin de Hilos
El hilo principal debe dormir para asegurar que el hilo logre hacer
su parte. El hilo nuevo debe llamar a pthread_self() porque no hay
garanta que el valor pasado por el hilo principal lo tenga. Todo
esto por desconocer qu hilo corre primero.
Trmino de un Hilo
Si un hilo invoca a exit, _Exit o _exit, todo el proceso terminar.
Un hilo puede terminar de tres maneras sin terminar el proceso:
Retornando de su rutina de inicio, cancelado por otro hilo del
mismo proceso, o llamando pthread_exit.

(THREADS)
Trmino de un Hilo

76

(THREADS)
Detaching y Joining en hilos (desasociar y reunin)
Todo hilo ocupa recursos del SO para su operacin. Entre ellos se
encuentra el estatus de trmino el cual es retenido hasta el llamado
a pthread_join; sin embargo, los recursos ocupados por un hilo
pueden ser retornados inmediatamente despus que ste termina si
llamamos a pthread_detach. En este caso un llamado a
pthread_join fallar .

77

You might also like