You are on page 1of 9

h|x|wtw atv|t wx t ctt

Ytvtw wx \zx|xt

h|x|wtw atv|t wx t ctt


Ytvtw wx \zx|xt

Si se quiere hacer un programa que necesita acceso a Internet o a una red local, los sockets son la respuesta. Para que dos programas se comuniquen entre si sobre una red, se necesitan al menos dos sockets: uno en nuestra mquina, y otro en el sistema remoto.

Uno esta actuando de servidor, a la espera de conexiones por parte de clientes. Otro socket es justamente el que se conectara al socket servidor.
3

Un socket es simplemente un tipo de archivo Unix especial. Al ser un archivo, tiene un nmero asociado cuando se abre o crea, llamado "file descriptor" Se puede leer, escribir y cerrar como cualquier archivo comn abierto con fopen(). Pero los sockets se crean, leen y escriben con otras funciones, y no se utilizan read() o write() para leer o escribir simplemente porque no nos proveen de la funcionalidad adicional que necesitamos para el caso de un socket.

Para

crear un socket se debe utilizar la funcin socket(). olvidar leer las pginas del manual de todas las funciones que se nombren aqu. leer la de socket(), se utiliza man 2 socket, y no man socket.

Primero,

No

se debe incluir sys/types.h junto con sys/socket.h y netinet/in.h, ya que los prototipos de las funciones que se van a utilizar se encuentran all definidos.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h>

Para

int socket(int domain, int type, int protocol);

Para navegar generalmente nos conectamos al puerto 80, para bajar mails, al 110 y para enviar mails, por el 25. Si creamos un programa que va a actuar de servidor necesitamos utilizar la funcin bind() para definir a que puerto van a tener que conectarse los clientes. Como norma general, se utilizan los valores mayores a 1024 y hasta el 65535.

Primer argumento (domain): entero que debe ser seteado como AF_INET, porque el socket va a ser utilizado sobre una red. Hay otros dos valores, uno para Unix local y otro para redes X.25. Segundo argumento (type): puede ser SOCK_STREAM o SOCK_DGRAM. El primero es para utilizar el protocolo TCP, y el segundo para UDP. Tercer argumento (protocol): conviene setearlo en cero, para que socket() elija el protocolo correcto en base a type.

Socket devuelve el descriptor de archivo (file descriptor), que debemos guardar en una variable creada a tal efecto. Este nmero ser utilizado en todas las dems funciones.
7

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

#include <sys/stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h>

Primer argumento (sockfd): toma por valor el del descriptor que nos dio socket(). Segundo argumento: puntero a una estructura llamada sockaddr que contiene informacin sobre nuestra IP y puerto. Tercer argumento: tamao de la estructura sockaddr, con poner sizeof (struct sockaddr) alcanza.

main() { int enchufe; struct sockaddr_in nos; enchufe = socket(AF_INET, SOCK_STREAM, 0); nos.sin_family = AF_INET; nos.sin_port = htons(3490); nos.sin_addr.s_addr = INADDR_ANY; memset(&(nos.sin_zero), 0, 8); bind(enchufe, (struct sockaddr *)&nos, sizeof(struct sockaddr));

10

nos.sin_port = htons(3490); nos.sin_addr.s_addr = INADDR_ANY;

La funcin htons() debe ser utilizada para cambiar nuestros nmeros (o sea, del host) al Orden de Bytes de Red ("Network Byte Order").

INADDR_ANY indica que nuestra IP debe ser llenada automticamente, en vez de ser provista por el programador.

hton = host to network short ntohl = network to host long

Si queremos que bind seleccione automticamente un puerto libre, se puede igualar nos.sin_port a cero, sin htons().
11 12

A la hora de hacer un cliente se debe indicar a que IP y puerto se quiere conectar, para luego efectuar dicha conexin. int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
Primer argumento: el descriptor que dio socket() y que se guarda en una variable. Segundo y tercero ya fue visto en bind(), slo que esta vez en vez de usarlo en bind() para esperar conexiones desde cierta IP y puerto, se usa en connect() para conectarse a cierta IP y puerto.

#include <sys/stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> main() { int enchufe; struct sockaddr_in destino; enchufe = socket(AF_INET, SOCK_STREAM, 0); destino.sin_family = AF_INET; destino.sin_port = htons(23); destino.sin_addr.s_addr = inet_addr("10.12.110.57"); memset(&(destino.sin_zero), 0, 8); connect(enchufe, (struct sockaddr *)&destino, sizeof(struct sockaddr))

13

14

destino.sin_port = htons(23);

En el ejemplo la conexin se realiza a 10.12.110.57, puerto 23.


destino.sin_addr.s_addr = inet_addr("10.12.110.57");

Cuando se esta programando un servidor, luego de socket() y bind(), se deben esperar conexiones, con listen() y cuando una llegue, aceptarla con accept().
int listen(int sockfd, int backlog);

La funcin inet_addr() convierte un IP como cadena en un entero sin signo (unsigned long). La funcion inversa, de unsigned long a cadena es inet_ntoa (por network to ascii). ATENCION: Todas las funciones devuelven -1 ante error, y la variable errno nos indicar que error es.
15

Primer argumento: El descriptor que dio socket(). Segundo argumento: indica que cantidad mxima de clientes que pueden estar en la cola, esperando ser aceptados. Un nmero comn es 5 o 10.

16

Finalmente se estan esperando conexiones en el socket, en un determinado puerto, llega un cliente, espera ser aceptado y cuando se acepta... accept()nos da un nuevo descriptor de socket. Ahora se tienen dos descriptores.

int accept(int sockfd, void *addr, int *addrlen);


Primer argumento ya no es necesario explicarlo. El segundo argumento: puntero a una estructura sockaddr_in donde se guardara la IP y puerto desde el cual se conecta el cliente. El tercero debe ser sizeof(struct sockaddr_in).

El original, que est esperando nuevas conexiones El nuevo, en el que ya podemos enviar y recibir con send() y recv().

17

18

#include main() { int escucha, cliente; struct sockaddr_in nos; struct sockaddr_in ellos; int addr_len; escucha = socket(AF_INET, SOCK_STREAM, 0); nos.sin_family = AF_INET; nos.sin_port = htons(3490); nos.sin_addr.s_addr = INADDR_ANY; memset(&(nos.sin_zero), 0, 8); bind(escucha, (struct sockaddr *)&nos, sizeof(struct sockaddr)); listen(escucha, 10); addr_len = sizeof(struct sockaddr_in); cliente = accept(escucha, (struct sockaddr *)&ellos, &addr_len);

Para enviar datos se utiliza la funcin send()

int send(int sockfd, const void *msg, int len, int flags)
Primer parmetro (int sockfd): descriptor de nuestro socket ya conectado. Segundo parmetro (msg): puntero a los datos que se quieren enviar. Tercer parmetro (len): cantidad de esos datos que se quieren enviar. Ultimo parmetro (flags): con slo ponerlo en cero alcanza.

20

19

char *msg = "Hola Mundo...!"; int len, bytes_enviados; len = strlen(msg); bytes_enviados = send(sockfd, msg, len, 0);

int recv(int sockfd, void *buf, int len, unsigned int flags)
Primer argumento sigue siendo lo mismo. Segundo argumento: puntero a una variable de tipo char que generalmente se llama "buffer" Tercero argumento (len): tamao de este buffer Cuarto argumento (flags): debe ser cero, como en send().

recv() devuelve la cantidad de bytes recibidos, o sino nos da -1 en caso de error. De vuelta, errno indicar que problema hubo. recv()puede devolver CERO. Esto indica que la otra computadora ha cerrado la conexin. A decir verdad, este es uno de los mtodos ms comunes para saber si el vnculo sigue establecido.
22

21

Para

los sockets no conectados (UDP) se utilizan variantes de send() y recv()casi equivalentes, con la nica diferencia que se agregan un par de parmetros relacionados con el destino y origen de los datos. Estas funciones se llaman sendto() y recvfrom().

int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)
Los

primeros 4 parmetros son equivalentes a los de send(). El quinto parmetro es un puntero a una estructura de tipo sockaddr, seguramente la recordaran de bind() y connect(). El sexto parmetro, es el tamao de dicha estructura: sizeof(struct sockaddr). Esta estructura contiene el puerto y direccin IP de destino.
24

23

int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)

int recvfrom (int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen)

En verdad, la estructura que se usa en general es sockaddr_in, pero esta es compatible con sockaddr, por lo que se utiliza un recurso para disfrazarla; esto se llama type casting. Se pone entre parntesis el tipo que va a hacer de disfraz, mientras que detrs y fuera del parntesis se pone la variable original. sendto() devuelve la cantidad de bytes enviados igual que send(), y si devuelve -1, ya saben: debemos examinar la variable errno para saber que error es.

25

Igual a recv() ms dos parmetros adicionales.

from es un puntero a una estructura sockaddr que ser llenada con el puerto e IP de origen de los datos. fromlen es un puntero a una variable int que inicialmente contendr el valor devuelto por sizeof(struct sockaddr), al finalizar recvfrom(), este puntero contendr el tamao de la direccin ahora contenida en from.
26

Para cerrar un socket se utiliza close(), al igual que con archivos. Al cerrarlo se previenen lecturas y escrituras.

int shutdown (int sockfd, int how) sockfd es el socket que se quiere cerrar. El segundo parmetro (how) puede tomar 3 valores diferentes, dependiendo de lo que se quiera hacer:
0: Cierra la capacidad de recepcin 1: Cierra la capacidad de envo 2: Ni recepcin, ni envo.

Si alguien intentara leer o escribir en este socket ahora cerrado, va a recibir el error correspondiente. Existe una segunda manera de cerrar un socket, una forma que provee de un poco ms de control. La funcin se llama shutdown()y la sintaxis es la siguiente:

shutdown()devuelve 0 si la operacin se realiz con xito, o -1 en caso de error. Por supuesto, errno indicar que error es.

int shutdown (int sockfd, int how)

27

28

En verdad shutdown() no cierra un socket, solamente modifica su capacidad de recibir o enviar. Para liberar los recursos que utiliza un socket, hay que usar close(). No queda otra. En rigor de verdad, cuando hacemos close(), estamos cerrando nuestra interfaz al socket, no el socket en si. El cerrarlo es trabajo del ncleo (el kernel Linux), y esto puede llevar un mximo de 4 minutos. Durante este tiempo, el socket estar en estado TIME_WAIT.

Todas las funciones cuando quieren indicar un error, devuelven -1 y setean la variable errno al nmero de error correspondiente. Para saber que error es tenemos una funcin llamada perror(), que viene de print error (imprimir error), que lo que hace es mostrar en pantalla una frase descriptiva del error. Como parmetro, acepta uno solo, que es un mensaje que se quiere que se muestre justo delante del error. Este mensaje es generalmente el modulo o funcin donde se produjo el error, o simplemente el nombre del programa error = connect(...); if (error == -1) { perror("conexin"); exit(2); }
30

29

Sockets con TCP

SERVIDOR socket() bind() listen() socket()

CLIENTE

connect() accept() read() write() close() write() read() close()

31

32

Servidor
socket bind listen accept read write close

Sockets con UDP

SERVIDOR socket() socket() bind() sendto() bind() recvfrom() sendto()

CLIENTE

Punto de sincronizacin

comunicacin
connect write read close

recvfrom() close()

socket

close()

Cliente

33

34

Hacer Trabajo Prctico N 3 de Linux

35

You might also like