You are on page 1of 20

Definicin de socket

Un socket (enchufe), es un mtodo para la comunicacin entre un programa del cliente y un programa del servidor en una red. Un socket se define como el punto final en una conexin. Los sockets se crean y se utilizan con un sistema de peticiones o de llamadas de funcin a veces llamados interfaz de programacin de aplicacin de sockets (API,application programming interface).

Estructura y funciones
Creacin y cierre.

socket.

#include <sys/types.h> #include <sys/socket.h>

int socket(int dominio, int tipo, int protocolo);

La funcin socket crea un extremo de una comunicacin y devuelve un descriptor. Los argumentos son:

dominio Dominio de comunicaciones, el valor generalmente usado es AF_INET, tambin llamado como PF_INET. tipo Especifica la semntica de la comunicacin, sus valores ms comunes son SOCK_STREAM para TCP y SOCK_DGRAM para UDP. protocolo Protocolo particular para ser usado con el conector, generalmente valor 0.

Si tiene xito, la funcin devuelve un int que indica el valor del conector. En caso de error el valor es -1.

close.

#include <unistd.h>

int close(int fd);

La funcin close cierra un descriptor de fichero o socket. Su argumento es:

fd Descriptor del fichero o socket a cerrar.

La funcin close devuelve 0 si sucede. En caso de error devuelve el valor -1.

Asociacin a puertos y especificacin de propiedades.

bind.

#include <sys/types.h> #include <sys/socket.h>

int bind(int sockfd, struct sockaddr *addr, int addrlen);

La funcin bind asocia el socket dado por sockfd a la direccin local especificada por addr para que el socket quede asignado al puerto especificado en la misma. Sus argumentos son:

sockfd Descriptor del socket creado con anterioridad. addr Estructura de datos donde se especifica la direccin y puerto al que se asocia el socket. addrlen Longitud de la estructura de datos anterior.

La funcin bind se suele utilizar solo con sockets de tipo SOCK_STREAM. La estructura sockaddr no suele ser utilizada, siendo siempre utilizada en su lugar la estructura sockaddr_in, cuya declaracin puede verse a continuacin.

struct in_addr

{ unsigned long int s_addr; };

struct sockaddr_in { int sin_family; unsigned short int sin_port; struct in_addr sin_addr; }; En esta estructura, cada uno de los campos tiene la siguiente utilidad: sin_family Dominio de comunicaciones del socket, generalmente AF_INET. port Puerto al que se asocia el socket. sin_addr.s_addr Direccin IP a la que se asocia el socket. Para permitir conexiones de cualquier direccin utilizar el valor INADDR_ANY.

La funcin bind devuelve 0 en caso de xito. El valor -1 es devuelto si sucede un error.

listen.

#include <sys/socket.h>

int listen(int s, int backlog);

La funcin listen especifica que el socket dado por s desea aceptar conexiones. La descripcin de sus argumentos es:

s Descriptor del socket creado con anterioridad.

backlog Longitud mxima de la cola de conexiones pendientes, por compatibilidad con versiones anteriores, el valor mximo que debe especificarse es 5.

La funcin listen devuelve 0 si sucede y -1 en caso de error.

ioctl.

#include <sys/ioctl.h>

int ioctl(int d, int peticion, );

La funcin ioctl manipula los valores de los parmetros de un socket. La funcin ioctl posee un nmero variable de argumentos segn el valor a modificar, por ello solo explicaremos el caso que puede ser necesario utilizar en los programas propuestos. Dicho caso consiste en la modificacin del modo de funcionamiento del socket.

Un socket puede funcionar en modo bloqueante, en el cual espera hasta que se produzca una peticin solicitada (lectura de datos, escritura de datos, etc.), o bien en modo no bloqueante, en el cual intenta la peticin solicitada y si esta disponible la realiza, terminando inmediatamente, sin ningn tipo de espera, en caso contrario. En nuestro caso particular la funcin ioctl toma la forma:

int ioctl(int d, int peticion, int &tipo);

Donde los valores de los argumentos son:

d Descriptor del socket creado con anterioridad. peticion Propiedad a cambiar, en nuestro caso modo de funcionamiento del socket. tipo Modo de funcionamiento. Indica funcionamiento bloqueante (0) o nobloqueante (1).

La funcin ioctl devuelve 0 si sucede y -1 falla.

Aceptacin y peticin de conexiones.

accept.

#include <sys/types.h> #include <sys/socket.h>

int accept(int s, struct sockaddr *addr, int addrlen);

La funcin accept acepta una peticin de conexin al socket especificado por s. Los parmetros son:

s Descriptor del socket creado con anterioridad. addr Estructura de datos donde se especificar la direccin y puerto del descriptor que se a conectado a este socket. addrlen Longitud de la estructura de datos anterior.

La estructura sockaddr no suele ser utilizada, siendo siempre utilizada en su lugar la estructura sockaddr_in, explicada con anterioridad.

La funcin devuelve un entero no negativo que es el descriptor del socket aceptado o -1 si sucede un error. El socket original (parmetro s) permanece en cualquier caso inalterado, pudiendo ser utilizado en posteriores llamadas a la funcin.

connect.

#include <sys/types.h> #include <sys/socket.h>

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

La funcin connect solicita poder conectar el socket especificado por sockfd a un socket remoto que es especifca en serv_addr. Los parmetros son:

sockfd Descriptor del socket creado con anterioridad. serv_addr Estructura de datos donde se especifica la direccin y puerto con el que deseamos establecer la conexin. addrlen Longitud de la estructura de datos anterior.

La estructura sockaddr no suele ser utilizada, siendo siempre utilizada en su lugar la estructura sockaddr_in, explicada con anterioridad.

La funcin devuelve el valor -1 si error o 0 si su llamada tiene xito. 5

Comprobacin del estado de un socket.

select.

#include <sys/types.h> #include <sys/time.h> #include <unistd.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

La funcin select comprueba el estado de un socket. La comprobacin afecta a tres conjuntos distintos de descriptores de socket (readfds, writefds y exceptfds). Los parmetros son:

n Valor, incrementado en una unidad, del descriptor ms alto de cualquiera de los tres conjuntos.

readfds Conjunto de sockets que sern comprobados para ver si existen caracteres para leer. Si el socket es de tipo SOCK_STREAM y no esta conectado, tambin se modificar este conjunto si llega una peticin de conexin. writefds Conjunto de sockets que sern comprobados para ver si se puede escribir en ellos. exceptfds Conjunto de sockets que sern comprobados para ver si ocurren excepciones. timeout Limite superior de tiempo antes de que la llamada a select termine. Si timeout es NULL, la funcin select no termina hasta que se produzca algn cambio en uno de los conjuntos (llamada bloqueante a select).

La declaracin de la estructura timeval es la siguiente:

struct timeval { unsigned long int tv_sec; /* Segundos */ unsigned long int tv_usec; /* Millonesimas de segundo */ };

Si no se desea comprobar alguna de las condiciones que proporciona la funcin select (lectura, escritura y excepciones), puede sustituirse el puntero al conjunto por un puntero NULL.

Para manejar el conjunto fd_set se proporcionan cuatro macros:

FD_ZERO(fd_set *set); FD_SET(int fd, fd_set *set); FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *est);

FD_ZERO inicializa el conjunto fd_set especificado por set. FD_SET y FD_CLR aaden o borran un descriptor de socket dado por fd al conjunto dado por set. FD_ISSSET mira si el descriptor de socket dado por fd se encuentra en el conjunto especificado por set.

La funcin devuelve el valor -1 en caso de error y un nmero, cuyo valor es 0 si se produce el timeout antes de que suceda ninguna modificacin en el estado de los descriptores incluidos en los conjuntos, o un valor mayor que 0 si se produce una modificacin en algn descriptor. El nmero mayor que 0 indica el nmero de descriptores que han sufrido una modificacin de forma simultanea, pues basta un solo cambio para salir de la funcin.

Lectura y escritura.

read.

#include <unistd.h>

int read(int fd, void *buf, int n_bytes);

La funcin read lee datos del socket especificado por fd. Sus parmetros son:

fd Descriptor del socket creado con anterioridad. buf Buffer que contendr los datos ledos. n_bytes Longitud del buffer en bytes, indica adems el tamao mximo en bytes de los datos a leer, pues n_bytes debe ser como mximo igual al tamao de buf.

La funcin devuelve -1 en caso de error y el nmero de bytes ledos, que pueden ser 0, si tiene xito la llamada. Generalmente 0 indica el final de los datos en el socket.

recv.

#include <sys/types.h> #include <sys/socket.h>

int recv(int s, void *buf, int lon, int flags);

La funcin recv lee datos del socket especificado por s. Sus parmetros son:

s Descriptor del socket creado con anterioridad. buf Buffer que contendr los datos ledos lon Longitud del buffer en bytes, indica adems el tamao mximo en bytes de los datos a leer, pues n_bytes debe ser como mximo igual al tamao de buf. flags Opciones de recepcin, generalmente valor 0.

La funcin devuelve -1 en caso de error y el nmero de bytes ledos, que pueden ser 0, si tiene xito la llamada. Generalmente 0 indica el final de los datos en el socket.

recvfrom.

#include <sys/types.h> #include <sys/socket.h>

int recv(int s, void *buf, int lon, int flags, struct sockaddr *desde, int *londesde);

La funcin recvfrom lee datos del socket especificado por s. Sus argumentos son:

s Descriptor del socket creado con anterioridad. buf Buffer que contendr los datos ledos lon Longitud del buffer en bytes, indica adems el tamao mximo en bytes de los datos a leer, pues n_bytes debe ser como mximo igual al tamao de buf. flags Opciones de recepcin, generalmente valor 0. desde Estructura de datos que contendr la direccin IP y el puerto desde el que se han recibido los datos ledos. londesde Longitud de la estructura de datos anterior.

La estructura sockaddr no suele ser utilizada, siendo siempre utilizada en su lugar la estructura sockaddr_in, explicada con anterioridad.

La funcin devuelve el valor -1 si error o el nmero de bytes leidos si su llamada tiene xito. Generalmente 0 indica el final de los datos en el socket.

write.

#include <unistd.h>

int write(int fd, void *buf, int num);

La funcin write escribe hasta num bytes de datos al socket especificado por fd. Sus parmetros son:

fd Descriptor del socket creado con anterioridad. buf Buffer que contiene los datos a escribir. num Nmero de bytes a escribir en el socket.

La funcin devuelve -1 en caso de error y el nmero de bytes realmente escritos si tiene xito. Es necesario tener en cuenta que la funcin no tiene porque poder escribir todos los bytes solicitados en una sola llamada.

send.

#include <sys/types.h> #include <sys/socket.h>

int send(int s, const void *buf, int num, int flags);

La funcin send escribe hasta num bytes de datos al socket especificado por s. Sus parmetros son:

s Descriptor del socket creado con anterioridad. buf Buffer que contiene los datos a escribir en el socket. num Nmero de bytes a escribir en el socket. flags Opciones de envo, generalmente valor 0.

La funcin devuelve -1 en caso de error y el nmero de bytes realmente escritos si tiene xito. Es necesario tener en cuenta que la funcin no tiene porque poder escribir todos los bytes solicitados en una sola llamada.

sendto.

#include <sys/types.h> #include <sys/socket.h>

int sendto(int s, const void *buf, int num, int flags, const struct sockaddr *to, int tolen);

La funcin sendto escribe hasta num bytes de datos mediante el socket especificado por s. Sus parmetros son:

s Descriptor del socket creado con anterioridad. buf Buffer que contiene los datos a escribir en el socket. num Nmero de bytes a escribir en el socket. flags Opciones de envo, generalmente valor 0. to Estructura de datos que contiene la direccin IP y el puerto al que se desean escribir los datos.

tolen Longitud de la estructura de datos anterior.

La estructura sockaddr no suele ser utilizada, siendo siempre utilizada en su lugar la estructura sockaddr_in, explicada con anterioridad.

La funcin devuelve -1 en caso de error y el nmero de bytes realmente escritos si tiene xito. Es necesario tener en cuenta que la funcin no tiene porque poder escribir todos los bytes solicitados en una sola llamada.

Conversin entre formatos de representacin de direcciones IP.

inet_aton.

#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>

int inet_aton(const char *cp,struct in_addr *inp);

La funcin inet_aton convierte la direccin de Internet dada por cp desde la notacin estndar de nmeros y puntos (por ejemplo 147.156.1.1) a la representacin binaria en orden de bytes de red y la guarda en la estructura a la que apunta inp. Sus parmetros son:

cp Cadena de caracteres con la direccin Internet a convertir. inp Estructura que contendr la direccin convertida.

La estructura in_addr ha sido explicada con anterioridad.

La funcin devuelve 0 en caso de error y un valor distinto de 0 si la direccin proporcionada es vlida.

inet_ntoa.

#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>

char *inet_ntoa(struct in_addr in);

La funcin inet_ntoa convierte la direccin de Internet representada en formato binario en orden de bytes de red a una cadena de caracteres en la notacin estndar de nmeros y puntos. Sus parmetros son:

in Estructura que contiene las direccin a convertir.

La funcin devuelve un puntero a la cadena de caracteres con la direccin Internet en la notacin estndar de nmeros y puntos. Dicho puntero hace referencia a una variable esttica que es sobrescrita en cada llamada a la funcin.

Conversin entre formatos de representacin de datos en el computador y en la red.

htonl.

#include <netinet/in.h>

unsigned long int htonl(unsigned long int hostlong); 10

La funcin htonl convierte el entero largo (32 bytes) dado por hostlong desde el orden de bytes del hosts al orden de bytes de la red.

htons.

#include <netinet/in.h>

unsigned short int htons(unsigned short int hostshort);

La funcin htons convierte el entero corto dado (16 bytes) dato por hostshort desde el orden de bytes del hosts al orden de bytes de la red. ntohl.

#include <netinet/in.h>

unsigned long int ntohl(unsigned long int netlong);

La funcin ntohl convierte el entero largo (32 bytes) dado por netlong desde el orden de bytes de la red al orden de bytes del hosts.

ntohs.

#include <netinet/in.h>

unsigned short int ntohs(unsigned short int netshort);

La funcin ntohs convierte el entero corto (16 bytes) dado por netshort desde el orden de bytes de la red al orden de bytes del hosts.

Dominios y tipos de sockets


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 dnde 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 stos se hallan unidos mediante una red TCP/IP, el dominio de comunicacin ser AF_INET. Cabe aclarar que existen otros 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. En esta introduccin slo trataremos el dominio AF_INET Tipos de sockets en el dominio AF_INET.

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
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 comprobar la secuencia, eliminar duplicados y asegurar la integridad. 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. Entonces podramos preguntar, Cmo hacen estos programas para funcionar si pueden perder datos?, ellos implementan un protocolo encima de UDP que realiza control de errores. La especificacin del protocolo UDP se puede leer en la RFC-768.

Sockets Raw
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.

Ejemplo genrico de un socket


Funcionamiento genrico
Normalmente, un servidor se ejecuta sobre una computadora especfica y tiene un socket que responde en un puerto especfico. El servidor nicamente espera, escuchando a travs del socket a que un cliente haga una peticin. En el lado del cliente: el cliente conoce el nombre de host de la mquina en la cual el servidor se encuentra ejecutando y el nmero de puerto en el cual el servidor est conectado. Para realizar una peticin de conexin, el cliente intenta encontrar al servidor en la mquina servidora en el puerto especificado. Si todo va bien, el servidor acepta la conexin. Adems de aceptar, el servidor obtiene un nuevo socket sobre un puerto diferente. Esto se debe a que necesita un nuevo socket (y, en consecuencia, un numero de puerto diferente) para seguir atendiendo al socket original para peticiones de conexin mientras atiende las necesidades del cliente que se conect. Por la parte del cliente, si la conexin es aceptada, un socket se crea de forma satisfactoria y puede usarlo para comunicarse con el servidor. Es importante darse cuenta que el socket en el cliente no est utilizando el nmero de puerto usado para realizar la peticin al servidor. En lugar de ste, el cliente asigna un nmero de puerto local a la mquina en la cual est siendo ejecutado. Ahora el cliente y el servidor pueden comunicarse escribiendo o leyendo en o desde sus respectivos sockets.

JAVA Sockets
El paquete java.net de la plataforma Java proporciona una clase Socket, la cual implementa una de las partes de la comunicacin bidireccional entre un programa Java y otro programa en la red. La clase Socket se sita en la parte ms alta de una implementacin dependiente de la plataforma, ocultando los detalles de cualquier sistema particular al programa Java. Usando la clase java.net.Socket en lugar de utilizar cdigo nativo de la plataforma, los programas Java pueden comunicarse a travs de la red de una forma totalmente independiente de la plataforma. De forma adicional, java.net incluye la clase ServerSocket, la cual implementa un socket el cual los servidores pueden utilizar para escuchar y aceptar peticiones de conexin de clientes. Nuestro objetivo ser conocer cmo utilizar las clases Socket y ServerSocket. Por otra parte, si intentamos conectar a travs de la Web, la clase URL y clases relacionadas (URLConnection, URLEncoder) son probablemente ms apropiadas que las clases de sockets. Pero de hecho, las clases URL no son ms que una conexin a un nivel ms alto a la Web y utlilizan como parte de su implementacin interna los sockets. Modelo de comunicaciones con Java

El modelo de sockets ms simple es: El servidor establece un puerto y espera durante un cierto tiempo (timeout segundos), a que el cliente establezca la conexin. Cuando el cliente solicite una conexin, el servidor abrir la conexin socket con el mtodo accept(). El cliente establece una conexin con la mquina host a travs del puerto que se designe en puerto# El cliente y el servidor se comunican con manejadores InputStream y OutputStream - Apertura de Sockets Si estamos programando un CLIENTE, el socket se abre de la forma: Socket miCliente; miCliente = new Socket( "maquina", numeroPuerto ); Donde maquina es el nombre de la mquina en donde estamos intentando abrir la conexin y numeroPuerto es el puerto (un nmero) del servidor que est corriendo sobre el cual nos queremos conectar. Cuando se selecciona un nmero de puerto, se debe tener en cuenta que los puertos en el rango 0-1023 estn reservados para usuarios con muchos privilegios (superusuarios o root). Estos puertos son los que utilizan los servicios estndar del sistema como email, ftp o http. Para las aplicaciones que se desarrollen, asegurarse de seleccionar un puerto por encima del 1023. En el ejemplo anterior no se usan excepciones; sin embargo, es una gran idea la capturade excepciones cuando se est trabajando con sockets. El mismo ejemplo quedara como: Socket miCliente; try { miCliente = new Socket( "maquina",numeroPuerto ); } catch( IOException e ) { System.out.println( e ); } Si estamos programando un SERVIDOR, la forma de apertura del socket es la que muestra el siguiente ejemplo: Socket miServicio; try { miServicio = new ServerSocket( numeroPuerto ); } catch( IOException e ) { System.out.println( e );

} A la hora de la implementacin de un servidor tambin necesitamos crear un objeto socket desde el ServerSocket para que est atento a las conexiones que le puedan realizar clientes potenciales y poder aceptar esas conexiones: Socket socketServicio = null; try { socketServicio = miServicio.accept(); } catch( IOException e ) { System.out.println( e ); }

Creacin de Streams

Creacin de Streams de Entrada


En la parte CLIENTE de la aplicacin, se puede utilizar la clase DataInputStream para crear un stream de entrada que est listo a recibir todas las respuestas que el servidor le enve. DataInputStream entrada; try { entrada = new DataInputStream( miCliente.getInputStream() ); } catch( IOException e ) { System.out.println( e ); } La clase DataInputStream permite la lectura de lneas de texto y tipos de datos primitivos de Java de un modo altamente portable; dispone de mtodos para leer todos esos tipos como: read(), readChar(), readInt(), readDouble() y readLine(). Deberemos utilizar la funcinque creamos necesaria dependiendo del tipo de dato que esperemos recibir del servidor. En el lado del SERVIDOR, tambin usaremos DataInputStream, pero en este caso pararecibir las entradas que se produzcan de los clientes que se hayan conectado: DataInputStream entrada; try { entrada = new DataInputStream( socketServicio.getInputStream() ); } catch( IOException e ) {

System.out.println( e ); }

Creacin de Streams de Salida


En el lado del CLIENTE, podemos crear un stream de salida para enviar informacin al socket del servidor utilizando las clases PrintStream o DataOutputStream: PrintStream salida; try { salida = new PrintStream( miCliente.getOutputStream() ); } catch( IOException e ) { System.out.println( e ); } La clase PrintStream tiene mtodos para la representacin textual de todos los datos primitivos de Java. Sus mtodos write y println() tienen una especial importancia en este aspecto. No obstante, para el envo de informacin al servidor tambin podemos utilizar DataOutputStream: DataOutputStream salida; try { salida = new DataOutputStream( miCliente.getOutputStream() ); } catch( IOException e ) { System.out.println( e ); } La clase DataOutputStream permite escribir cualquiera de los tipos primitivos de Java, muchos de sus mtodos escriben un tipo de dato primitivo en el stream de salida. De todos esos mtodos, el ms til quizs sea writeBytes(). En el lado del SERVIDOR, podemos utilizar la clase PrintStream para enviar informacin al cliente: PrintStream salida; try { salida = new PrintStream( socketServicio.getOutputStream() ); } catch( IOException e ) { System.out.println( e );

} Pero tambin podemos utilizar la clase DataOutputStream como en el caso de envo de informacin desde el cliente.

Cierre de Sockets
Siempre deberemos cerrar los canales de entrada y salida que se hayan abierto durante la ejecucin de la aplicacin. En la parte del cliente: try { salida.close(); entrada.close(); miCliente.close(); } catch( IOException e ) { System.out.println( e ); } Y en la parte del servidor: try { salida.close(); entrada.close(); socketServicio.close(); miServicio.close(); } catch( IOException e ) { System.out.println( e ); } Es importante destacar que el orden de cierre es relevante. Es decir, se deben cerrar primero los streams relacionados con un socket antes que el propio socket, ya que de esta forma evitamos posibles errores de escrituras o lecturas sobre descriptores ya cerrados.

You might also like