En esta sección, (en la cual se nombrarán algunas de las funciones más utilizadas para la programación en C de sockets), se mostrará la sintaxis de la función, las bibliotecas necesarias a incluir para llamarla, y algunos pequeños comentarios. Además de las que se mencionan aquí, existen muchas funciones más, aunque sólo decidí incluir éstas. Tal vez sean incluidas en una futura versión de este documento[1]. Nuevamente, para ver ejemplos sobre el uso de estas funciones, se podrá leer la sección de nombre Un ejemplo de Servidor de Flujos y la sección de nombre Un ejemplo de Cliente de Flujos, en las cuales hay código fuente de un Cliente de Flujos y un Servidor de Flujos.
#include <sys/types.h> #include <sys/socket.h> int socket(int domain,int type,int protocol); |
Analicemos los argumentos:
domain. Se podrá establecer como AF_INET (para usar los protocolos ARPA de Internet), o como AF_UNIX (si se desea crear sockets para la comunicación interna del sistema). Éstas son las más usadas, pero no las únicas. Existen muchas más, aunque no se nombrarán aquí.
type. Aquí se debe especificar la clase de socket que queremos usar (de Flujos o de Datagramas). Las variables que deben aparecer son SOCK_STREAM o SOCK_DGRAM según querramos usar sockets de Flujo o de Datagramas, respectivamente.
protocol. Aquí, simplemente se puede establecer el protocolo a 0.
La función socket() nos devuelve un descriptor de socket, el cual podremos usar luego para llamadas al sistema. Si nos devuelve -1, se ha producido un error (obsérvese que esto puede resultar útil para rutinas de verificación de errores).
#include <sys/types.h> #include <sys/socket.h> int bind(int fd, struct sockaddr *my_addr,int addrlen); |
Analicemos los argumentos:
fd. Es el descriptor de fichero socket devuelto por la llamada a socket().
my_addr. es un puntero a una estructura sockaddr
addrlen. contiene la longitud de la estructura sockaddr a la cuál apunta el puntero my_addr. Se debería establecer como sizeof(struct sockaddr).
La llamada bind() se usa cuando los puertos locales de nuestra máquina están en nuestros planes (usualmente cuando utilizamos la llamada listen()). Su función esencial es asociar un socket con un puerto (de nuestra máquina). Análogamente socket(), devolverá -1 en caso de error.
Por otro lado podremos hacer que nuestra dirección IP y puerto sean elegidos automáticamente:
server.sin_port = 0; /* bind() elegirá un puerto aleatoriamente */ server.sin_addr.s_addr = INADDR_ANY; /* pone la Ip del seridor automáticamente */ |
Un aspecto importante sobre los puertos y la llamada bind() es que todos los puertos menores que 1024 están reservados. Se podrá establecer un puerto, siempre que esté entre 1024 y 65535 (y siempre que no estén siendo usados por otros programas).
#include <sys/types.h> #include <sys/socket.h> int connect(int fd, struct sockaddr *serv_addr, int addrlen); |
Analicemos los argumentos:
fd. Debería configurarse como el fichero descriptor del socket, el cuál fue devuelto por la llamada a socket().
serv_addr. Es un puntero a la estructura sockaddr la cuál contiene la dirección IP destino y el puerto.
addrlen. Análogamente de lo que pasaba con bind(), este argumento debería establecerse como sizeof(struct sockaddr).
La función connect() se usa para conectarse a un puerto definido en una dirección IP. Devolverá -1 si ocurre algún error.
#include <sys/types.h> #include <sys/socket.h> int listen(int fd,int backlog); |
Veamos los argumentos de listen():
fd. Es el fichero descriptor del socket, el cual fue devuelto por la llamada a socket()
backlog. Es el número de conexiones permitidas.
La función listen() se usa si se están esperando conexiones entrantes, lo cual significa, si se quiere, que alguien pueda conectarse a nuestra máquina.
Después de llamar a listen(), se deberá llamar a accept(), para así aceptar las conexiones entrantes. La secuencia resumida de llamadas al sistema es:
socket()
bind()
listen()
accept()/* En la próxima sección se explicará como usar esta llamada */
Como todas las funciones descritas arriba, listen() devolverá -1 en caso de error.
#include <sys/types.h> #include <sys/socket.h> int accept(int fd, void *addr, int *addrlen); |
Veamos los argumentos de la función:
fd. Es el fichero descriptor del socket, que fue devuelto por la llamada a listen().
addr. Es un puntero a una estructura sockaddr_in en la quel se pueda determinar qué nodo nos está contactando y desde qué puerto.
addrlen. Es la longitud de la estructura a la que apunta el argumento addr, por lo que conviene establecerlo como sizeof(struct sockaddr_in), antes de que su dirección sea pasada a accept().
Cuando alguien intenta conectarse a nuestra computadora, se debe usar accept() para conseguir la conexión. Es muy fácil de entender: alguien sólo podrá conectarse (asóciese con connect()) a nuestra máquina, si nosotros aceptamos (asóciese con accept()) ;-)
A continuación, Se dará un pequeño ejemplo del uso de accept() para obtener la conexión, ya que esta llamada es un poco diferente de las demás.
(...) sin_size=sizeof(struct sockaddr_in); /* En la siguiente línea se llama a accept() */ if ((fd2 = accept(fd,(struct sockaddr *)&client,&sin_size))==-1){ printf("accept() error\n"); exit(-1); } (...) |
A este punto se usará la variable fd2 para añadir las llamadas send() y recv().
#include <sys/types.h> #include <sys/socket.h> int send(int fd,const void *msg,int len,int flags); |
Y sobre los argumentos de esta llamada:
fd. Es el fichero descriptor del socket, con el cual se desea enviar datos.
msg. Es un puntero apuntando al dato que se quiere enviar.
len. es la longitud del dato que se quiere enviar (en bytes).
flags. deberá ser establecido a 0[2] .
El propósito de esta función es enviar datos usando sockets de flujo o sockets conectados de datagramas. Si se desea enviar datos usando sockets no conectados de datagramas debe usarse la llamada sendto(). Al igual que todas las demás llamadas que aquí se vieron, send() devuelve -1 en caso de error, o el número de bytes enviados en caso de éxito.
#include <sys/types.h> #include <sys/socket.h> int recv(int fd, void *buf, int len, unsigned int flags); |
Veamos los argumentos:
fd. Es el descriptor del socket por el cual se leerán datos.
buf. Es el búfer en el cual se guardará la información a recibir.
len. Es la longitud máxima que podrá tener el búffer.
flags. Por ahora, se deberá establecer como 0.
Al igual de lo que se dijo para send(), esta función es usada con datos en sockets de flujo o sockets conectados de datagramas. Si se deseara enviar, o en este caso, recibir datos usando sockets desconectados de Datagramas, se debe usar la llamada recvfrom(). Análogamente a send(), recv() devuelve el número de bytes leídos en el búfer, o -1 si se produjo un error.
#include <sys/types.h> #include <sys/socket.h> int recvfrom(int fd,void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen); |
Veamos los argumentos:
fd. Lo mismo que para recv()
buf. Lo mismo que para recv()
len. Lo mismo que para recv()
flags. Lo mismo que para recv()
from. Es un puntero a la estructura sockaddr.
fromlen. Es un puntero a un entero local que debería ser inicializado a sizeof(struct sockaddr).
Análogamente a lo que pasaba con recv(), recvfrom() devuelve el número de bytes recibidos, o -1 en caso de error.
#include <unistd.h> close(fd); |
La función close() es usada para cerrar la conexión de nuestro descriptor de socket. Si llamamos a close() no se podrá escribir o leer usando ese socket, y si alguien trata de hacerlo recibirá un mensaje de error.
#include <sys/socket.h> int shutdown(int fd, int how); |
Veamos los argumentos:
fd. Es el fichero descritor del socket al que queremos aplicar esta llamada.
how. Sólo se podrá establecer uno de estos nombres:
0. Prohibido recibir.
1. Prohibido enviar.
2. Prohibido recibir y enviar.
Es lo mismo llamar a close() que establecer how a 2. shutdown() devolverá 0 si todo ocurre bien, o -1 en caso de error.
#include <unistd.h> int gethostname(char *hostname, size_t size); |
Veamos de qué se tratan los argumentos:
hostname. Es un puntero a un array que contiene el nombre del nodo actual.
size. La longitud del array que contiene al nombre del nodo (en bytes).
La función gethostname() es usada para obtener el nombre de la máquina local.
[1] | Se puede indagar más sobre éstas y las demás funciones relacionadas con Sockets en UNIX, leyendo las páginas de manual correspondientes a cada una. Nota del Traductor. |
[2] | El argumento ``flags'', también contenido en otras funciones de manejo de sockets, no es una característica propia del mensaje, y puede omitirse. En este caso, hace distinción entre los modos de enviar un paquete, además de otras cosas. Para más información, se puede leer el manual de send() (man 2 send). Nota del T. |