Resumen Socket

Anuncio
Resumen Socket
Un socket es un canal de comunicación de datos. Una vez que dos procesos se
conectan a través de n soket, usan un descriptor de socket para leer y escribir datos en el
socket. Para obtener uno de estos descriptores usamos la llamada de sistema socket. Esta
llamada tiene tres argumentos que son:
int domain
int type
int protocol
Hay vario tipos posibles de dominios para los sockets. Estan definidos en
/usr/include/sys/socket.h. Podemos encontrarlos directamente en /usr/includes/sys/socket.h
o en versiones posteriores de la biblioteca de C, en el específico del sistema operativo
/usr/include/bits/socket.h.
AF_UNIX: Protocolos internos de UNIX.
AF_INET: Protocolos de internet ARPA.
AF_ISO: Protocolo de la organización de estándares internacional.
AF_NS: Protocolos del sistema de red de Xerox.
Casi siempre vamos a querer usar el protocolo AF_INET. Este tipo de socket es el
más flexible, ya que nos permite usar comunicación local y de red. Hay varios tipos de
sockets:
SOCK_STREAM: Proporciona una conexión fiable, secuencial y en dos direcciones.
SOCK_DGRAM: Una conexión poco fiable. Se usa con los sockets UDP, que se
explicaron en su debido momento.
SOCK_RAW: Usados para protocolos de red internos.
SOCK_SEQPACKET: Sólo se usan en el protocolo AF_NS.
SOCK_RDM: no implementados.
Usaremos sockets de tipo SOCK_STREAM en el ejemplo, ya que nos permite una
comunicación en dos direcciones entre procesos que están conectados mediante el sockets.
Puede haber otras situaciones que requieran configuraciones de este parámetro.
El tercer y último argumento usamos el valor cero en este parámetro en nuestro
ejemplo que daremos.
La llamada de sistema devuelve un descriptor de socjet que podremos usarlo con las
demas llamadas de sistema socket o un -1 que indica que no se pudo asignar un socket.
BIND
La función bind asocia un proceso con un socket. Usamos normalmente bind en
procesos servidor para configurar un socket para conexiones de clientes entrantes. Los
argumentos de la función de sistema bind son:
1
int socket
struct sockaddr * mi_direccion
int largo_mi_direccion
El promer argumento es el valor de socket devuelto por la llamada previa a la funció
socket. El segundo argumento es la direccion de una estructura sockaddr.- el tercero es el
tamaño de la estructura de datos a la que señala sockaddr. Se necesita por que diferentes
protocolos usan diferentes tipos de estructuras sockaddr.
Llamadas de sistema de conexión
SI estamos escribiendo una aplicación cliente, el siguiente paso es establecer la
conexión con el servidor con el que se quiere realizar la conexión. Por otro lado, si estamos
escribiendo una aplicación servidor, vamos a querer configurar el socket para esperar
conexiones de aplicaciones clientes.
Las dos primeras llamadas de sistema listen y accept se usan en programación de
servidores. En aplicaciones cliente sólo necesitamos una llamada de sistema para la
conexión. Esta llamada recibe el nombre de connect.
LISTEN
Después de haber creado el socket y haberlo asociado con el proceso usando bind
un proceso de tipo servidor puede llamar a la función listen para escuchar conexiones de
sockets entrantes. Los argumentos de la llamada listen son:
int socket
int tamaño_cola_entrada
El primer argumento de listen es el valor entero del socket devuelto por la llamada
previa a la función socket. El segundo establece el tamaño de la cola entrante. Un proceso
servidor usa frecuentemente la llamada de sistema fork para crear un duplicado de sí mismo
que administra las llamadas de socket entrantes. este método cobra mucho sentido si
esperamos múltiples conexiones de clientes simultáneas. Sin embargo para la mayoría de
los programas normalmente es más adecuado y sencillo procesar las conexiones entrantes
de una en una y establecer el tamaño de la cola con un valor grande.
ACCEPT
Según las llamadas entrantes llegan a un socket que está escuchando, se pondrán a
la cola hasta que el programa servidor esté listo para procesarlas. Cuando el servidor está
listo para procesar una neva conexión, usará la llamada de sistema accept para recuperar na
llamada pendiente de la cola del socket. La llamada accept devolverá un nuevo descriptor
de socket. Este se usará para la comunicación entre las aplicaciones cliente y servidor. Al
mismo tiempo el socket original es todavía capaz de escuchar nuevas llamadas entrantes.
2
Los argumentos de la llamada de sistema accept son:
int socket
struct sockaddr * mi_direccion
int *tamaño_mi_direccion
El primer argumento de accept es el descriptor de socket del socket que escucha. El
segundo es un puntero al área de datos que se rellena con la información sobre la conexión
entrante. El tercero es un puntro a un entero que se establece con el número máximo de
bytes que puede acomodar mi_direccion. Si la llamada accept debe rellenar mi_direccion
con menos datos, tamaño_mi_direccion se cambiará al tamaño de los datos.
Algunos programas servidor no usan la llamada accept para administrar las
peticiones entrantes.
CONNECT
La llamada de sistema connect se usa para conectar un socket local con un servidor
remoto. Un uso típico es la de especificar la información de un computador host de un
proceso destino que se está ejecutando con un computador remoto. Los argumentos son:
int socket
struct sockeaddr * direccion_servidor
int tamaño_direccion_servidor
El primer argumento está definido opr el valor de retorno a la función socket. El
segundo se usa para especificar la dirección del protocolo.
TRANSFERENCIA DE DATOS
Una vez que se ha establecido la conexión, es el momento de comenzar a transferir
datos entre el servidor y el cliente. Hay dos llamadas de sistema para este cometido. Recv
se usa para recibir datos, y send para enviarlos.
RECV
Usamos la función recv para recibir mensajes de un socket conectado. Los
argumentos de recv son:
int socket
void *buf
int tama_buf
unsigned int flags
El socket definido por el primer argumento ya debe haber sido conectado con un
puerto usando connect. El segundo argumento es un puntero a un bloque de memoria donde
3
se almacenan los mensajes recibidos. El tercero especifica el tamaño en bytes del bloque de
memoria reservado. El cuatro contiene indicadores de operación podemos combinar valores
con el operador booleano y 1 en el cuarto argumento. Nosotros usaremos siempre un valor
cero para los indicadores de los ejemplos.
MSG_OOB : Procesa datos fuera de banda (útil para administrar mensajes de control de
alta prioridad). Normalmente usamos cero para un comportamiento normal.
MSG_PEEK: Recoge un mensaje entrante sin leerlo.
MSG_WAITALL: Espera a que el buffer de datos recibidos esté completamente lleno antes
de volver.
SEND
Usaremos la llamada de sistema send para pasar los datos a otro programa usando
un socket. Tanto la aplicación servidor como el cliente usan la función send. Las
aplicaciones cliente usan para enviar peticiones de servicio a procesos servidor remotos y
los procesos servidor la usan para devolver datos a los clientes.
Los argumentos son:
int socket
const void * datos_mensaje
int largo_datos_mensaje
unsigned int flags
El primer argumento es el valor de socket devuelto por la llamada a la función
socket. El segundo contiene cualqueir dato que tenga que ser transferido. El tercero
especifica el tamaño en bytes de los datos del mensaje. El cuarto es siempre cero en nuestro
ejemplo pero se pueden usar las siguientes constantes:
MSG_OOB: Proceso datos fuera de banda.
MSG:DONTROUTE: No usa enrutamiento.
4
EJEMPLO DE SERVIDOR:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
...
int puerto = 9000;
main()
{
struct sockaddr_in s;
struct sockaddr_in p;
int sd;
int tsd;
int tama_dire;
char buf[256];
int i, len;
sd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&s, sizeof(s));
s.sin_family = AF_INET;
s.sin_addr.s_addr = INADDR_ANY;
s.sin_port = htons(puerto);
bind(sd, (struct sockaddr *)&s, sizeof(s));
listen(sd, 20)
while(1)
{
tsd = accept(sd, (struct sockaddr *)&p, &tama_dire);
recv(tsd, buf, 256, 0);
printf(“Recibido del cliente: %s”, buf);
//convertiremos los caracteres en mayúsculas
len = strlen(buf);
for (i =0; i<len; i++)
buf[i] = toupper(buf[i]);
send(tsd, buf, len, 0);
5
close(tsd);
}
}
EJEMPLO DE CLIENTE:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
...
int puerto = 9000;
char *host_name = “127.0.0.1”;
main()
{
char buf[256];
char mensaje[256];
int sd;
struct sockaddr_in p;
struct hostent *nombre_server;
char * str =”Una cadena para comprobar”;
nombre_server = gethostbyname(host_name);
bzero(&s, sizeof(s));
p.sin_family = AF_INET;
p.sin_addr.s_addr = htonl(INADDR_ANY)
p.sin_add-s_addr = ((struct in_addr *)(nombre_server->h_addr))->d_addr;
p.sin_port = htons(puerto);
sd = socket(AF_INET, SOCK_STREAM, 0);
connect(sd, (void *)&p, sizeof(p));
send(sd, str, strlen(str),0);
recv(sd, buf, 256,0);
printf(“Mensaje recibido del servidor %s”,buf);
close(sd);
}
6
Descargar