Llamadas al sistema

getaddrinfo()

La funciΓ³n getaddrinfo es una parte esencial del desarrollo de redes en C/C++. Permite a los programas resolver nombres de host y servicios, lo que significa que puede traducir un nombre de host (como www.ejemplo.com) a una direcciΓ³n IP y viceversa. Esto es especialmente ΓΊtil en situaciones en las que necesitas establecer una conexiΓ³n de red y necesitas saber la direcciΓ³n IP del host o el nombre del host a partir de una direcciΓ³n IP.

Aun asi, su uso es bastante simple. Ayuda a configurar las estructuras que necesitarΓ‘ mΓ‘s adelante.

Sintaxis

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

int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints, struct addrinfo **res);
  • node: El nombre del host o la direcciΓ³n IP que deseas resolver.

  • service: El nΓΊmero del puerto o el nombre del servicio al que deseas conectarte.

  • hints: Una estructura addrinfo que proporciona pistas sobre el tipo de conexiΓ³n que estΓ‘s buscando.

  • res: Un puntero a un puntero de estructura addrinfo que se llenarΓ‘ con los resultados.

La funciΓ³n devuelve 0 si tiene Γ©xito y un valor negativo si ocurre un error. El resultado de la funciΓ³n, es decir, la lista de estructuras addrinfo, se almacena en el puntero doble res. Es importante liberar esta memoria utilizando freeaddrinfo() una vez que ya no se necesite la lista de direcciones.

Uso basico

AquΓ­ hay una llamada de ejemplo si usted es un servidor que desea escuchar en la direcciΓ³n IP de su host, puerto 3490. Tenga en cuenta que esto en realidad no realiza ninguna escucha ni configuraciΓ³n de red; simplemente configura estructuras que usaremos mΓ‘s adelante:

int status;
struct addrinfo hints;
struct addrinfo *servinfo;  // will point to the results

memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

"

// servinfo now points to a linked list of 1 or more struct addrinfos

// ... do everything until you don't need servinfo anymore ....

freeaddrinfo(servinfo); // free the linked-list

AI_PASSIVE le dice a getaddrinfo() que asigne la direcciΓ³n de mi host local a las estructuras de socket. Esto es conveniente porque asΓ­ no tienes que codificarla directamente. (O puedes colocar una direcciΓ³n especΓ­fica como el primer parΓ‘metro de getaddrinfo() donde actualmente tengo NULL).

socket()

La funciΓ³n socket() en C/C++ es una llamada al sistema que crea un nuevo socket, que es un punto de conexiΓ³n de red utilizado para la comunicaciΓ³n entre diferentes mΓ‘quinas o procesos en la misma mΓ‘quina.

Sintaxis

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

int socket(int domain, int type, int protocol);
  • domain: Especifica el dominio del socket. Puede ser PF_INET para IPv4, PF_INET6 para IPv6 o AF_UNIX para comunicaciΓ³n local en el mismo sistema.

  • type: Indica el tipo de socket, como SOCK_STREAM para TCP (orientado a conexiΓ³n) o SOCK_DGRAM para UDP (sin conexiΓ³n).

  • protocol: Generalmente se establece en 0 para que el sistema seleccione el protocolo adecuado segΓΊn el tipo de socket y el dominio.

La funciΓ³n devuelve un descriptor de socket si tiene Γ©xito y -1 si ocurre un error.5

Uso basico

Lo que realmente quieres hacer es usar los valores de los resultados de la llamada a getaddrinfo() e introducirlos en socket() directamente de esta manera:

int sockfd;
struct addrinfo hints, *res;

// do the lookup
// [pretend we already filled out the "hints" struct]
getaddrinfo("www.example.com", "http", &hints, &res);

// again, you should do error-checking on getaddrinfo(), and walk
// the "res" linked list looking for valid entries instead of just
// assuming the first one is good (like many of these examples do).
// See the section on client/server for real examples.
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
    fprintf(stderr, "sockfd error: %s\n", gai_strerror(status));
    exit(1);
}

// resto of the code

close(serverSocket);

socket() simplemente le devuelve un descriptor de socket que puede usar en llamadas posteriores al sistema, o -1 en caso de error. La variable global errno se establece en el valor del error (consulte la pΓ‘gina de manual de errno para obtener mΓ‘s detalles y una nota rΓ‘pida sobre el uso de errno en programas multiproceso).

bind()

La funciΓ³n bind() en C/C++ se utiliza para asociar un socket con una direcciΓ³n IP y un nΓΊmero de puerto especΓ­ficos en el sistema local. Esto es fundamental para los servidores que esperan conexiones entrantes en un puerto especΓ­fico.

Sintaxis

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

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd: El descriptor de archivo del socket que se va a asociar.

  • addr: Un puntero a una estructura sockaddr que contiene la direcciΓ³n IP y el nΓΊmero de puerto.

  • addrlen: La longitud de la estructura sockaddr.

La funciΓ³n bind() en C/C++ devuelve 0 en caso de Γ©xito y -1 en caso de error.

Uso basico

Aqui tienes un ejemplo que vincula el socket al host en el que se ejecuta el programa, el puerto 3490:

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

getaddrinfo(NULL, "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// bind it to the port we passed in to getaddrinfo():
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
    close(sockfd);
    perror("server: bind");
    exit(1);
}

A veces, es posible que notes que intentas volver a ejecutar un servidor y bind() falla, indicando "Address already in use" (DirecciΓ³n ya en uso). ΒΏQuΓ© significa eso? Bueno, un pequeΓ±o fragmento de socket que estaba conectado todavΓ­a estΓ‘ presente en el kernel y estΓ‘ ocupando el puerto. Puedes esperar a que se libere (alrededor de un minuto) o agregar cΓ³digo a tu programa que le permita reutilizar el puerto, como se muestra a continuaciΓ³n:

int yes = 1;
// Otra forma para personas que usan Solaris: char yes = '1';

// Elimina el molesto mensaje de error "Address already in use" (DirecciΓ³n ya en uso)
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
    perror("setsockopt");
    exit(1);
}

Un pequeΓ±o detalle adicional sobre bind(): hay momentos en los que no serΓ‘ absolutamente necesario llamarlo. Si te estΓ‘s conectando (connect()) a una mΓ‘quina remota y no te importa quΓ© puerto local estΓ‘s utilizando (como en el caso de telnet donde solo te importa el puerto remoto), puedes simplemente llamar a connect(). Esta funciΓ³n verificarΓ‘ si el socket no estΓ‘ vinculado (bind()) y lo vincularΓ‘ a un puerto local no utilizado si es necesario.

connect()

La funciΓ³n connect() en C/C++ se utiliza para establecer una conexiΓ³n a un socket previamente creado y configurado. Esta funciΓ³n es comΓΊnmente usada en clientes para conectarse a servidores.

Sintaxis

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd: El descriptor de socket que se quiere conectar.

  • addr: Un puntero a una estructura sockaddr que contiene la direcciΓ³n del servidor al que se desea conectar.

  • addrlen: El tamaΓ±o de la estructura sockaddr.

La funciΓ³n connect() devuelve un valor entero. En caso de Γ©xito, devuelve 0. Si falla, devuelve -1 y establece la variable errno para indicar el tipo especΓ­fico de error que ocurriΓ³ durante la conexiΓ³n.

Uso basico

Tengamos un ejemplo en el que realizamos una conexiΓ³n de socket a "www.example.com", puerto 3490:

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo("www.example.com", "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// connect!

if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
    perror("Error al conectar con el servidor");
    close(sockfd);
    exit(-1);
}

Listen()

La funciΓ³n listen() en C/C++ se utiliza en servidores para esperar conexiones entrantes de clientes. DespuΓ©s de crear un socket y conectarlo a una direcciΓ³n, el servidor llama a listen() para comenzar a escuchar conexiones entrantes, que seran hacepatadas mediante accept().

Sintaxis

int listen(int sockfd, int backlog);
  • sockfd: El descriptor de socket que se quiere escuchar.

  • backlog: El nΓΊmero mΓ‘ximo de conexiones pendientes que el sistema permitirΓ‘ en la cola de espera hasta que las accept()es

Nuevamente, como es habitual, listen() devuelve -1 y establece errno en caso de error. Devuelve 0 en caso de existo

Uso basico

Bien, como probablemente puedas imaginar, necesitamos llamar a bind() antes de llamar a listen() para que el servidor estΓ© ejecutΓ‘ndose en un puerto especΓ­fico. (Β‘Debes poder decirle a tus amigos a quΓ© puerto conectarse!) AsΓ­ que si vas a estar escuchando conexiones entrantes, la secuencia de llamadas al sistema que harΓ‘s es:

getaddrinfo();
socket();
bind();
listen();
/* accept() goes here */ 

*DejarΓ© ese espacio como muestra de cΓ³digo, ya que es bastante autoexplicativo. (El cΓ³digo en la secciΓ³n de accept(), a continuaciΓ³n, es mΓ‘s completo). La parte realmente complicada de todo este asunto es la llamada a accept().llll

accept()

La funciΓ³n accept() en C/C++ se utiliza para aceptar una conexiΓ³n entrante en un socket previamente configurado. Es especialmente importante en el lado del servidor para manejar mΓΊltiples clientes.

Lo que va a pasar es esto: alguien muy, muy lejos intentarΓ‘ connect() a su mΓ‘quina en un puerto en el que estΓ‘ listen(). Su conexiΓ³n estarΓ‘ en cola esperando ser accept(). Llamas a accept() y le dices que obtenga la conexiΓ³n pendiente.

Β‘Le devolverΓ‘ un nuevo file descriptor de archivo de socket para usar en esta ΓΊnica conexiΓ³n! AsΓ­ es, Β‘de repente tienes dos descriptores de archivos de socket por el precio de uno! El original todavΓ­a estΓ‘ escuchando mΓ‘s conexiones nuevas y el reciΓ©n creado finalmente estΓ‘ listo para send() y recv().

Sintaxis

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd: El descriptor de socket en el que se espera una conexiΓ³n entrante.

  • addr: Un puntero a una estructura sockaddr donde se almacenarΓ‘ la direcciΓ³n del cliente.

  • addrlen: Un puntero a un entero sin signo que indica el tamaΓ±o de la estructura sockaddr.

Nuevamente, como es habitual, listen() devuelve -1 y establece errno en caso de error. Devuelve 0 en caso de existo

Uso basico

Como antes, esto es un montΓ³n para absorber en una sola parte, asΓ­ que aquΓ­ hay un fragmento de cΓ³digo de muestra para su lectura:

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

#define MYPORT "3490"  // the port users will be connecting to
#define BACKLOG 10     // how many pending connections queue will hold

int main(void)
{
    struct sockaddr_storage their_addr;
    socklen_t addr_size;
    struct addrinfo hints, *res;
    int sockfd, new_fd;

    // !! don't forget your error checking for these calls !!

    // first, load up address structs with getaddrinfo():

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

    getaddrinfo(NULL, MYPORT, &hints, &res);

    // make a socket, bind it, and listen on it:

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    bind(sockfd, res->ai_addr, res->ai_addrlen);
    listen(sockfd, BACKLOG);

    // now accept an incoming connection:

    addr_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);

    // ready to communicate on socket descriptor new_fd!

Nuevamente, ten en cuenta que utilizaremos el descriptor de socket new_fd para todas las llamadas a send() y recv(). Si solo esperas una ΓΊnica conexiΓ³n en todo momento, puedes cerrar el socket de escucha (sockfd) para evitar mΓ‘s conexiones entrantes en el mismo puerto, si asΓ­ lo deseas.

send() and recv()

Estas dos funciones son para comunicarse a travΓ©s de sockets de flujo o sockets de datagramas conectados. Si deseas utilizar sockets de datagramas no conectados normales, deberΓ‘s consultar la secciΓ³n sobre sendto() y recvfrom().

send()

La funciΓ³n send() en C/C++ se utiliza para enviar datos a travΓ©s de un socket. Es comΓΊnmente usada en aplicaciones de red para enviar informaciΓ³n desde un cliente a un servidor o viceversa.

size_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd: El descriptor de socket por el cual enviar los datos.

  • buf: Puntero al bΓΊfer que contiene los datos a enviar.

  • len: TamaΓ±o de los datos a enviar, en bytes.

  • flags: Opciones para el envΓ­o de datos.

send() devuelve el nΓΊmero de bytes realmente enviados! Esto podrΓ­a ser menos que la cantidad que le indicaste que enviara. A veces le pides que envΓ­e un montΓ³n de datos y simplemente no puede manejarlo. EnviarΓ‘ tanto de los datos como pueda y confiarΓ‘ en que enviarΓ‘s el resto mΓ‘s tarde. Recuerda, si el valor devuelto por send() no coincide con el valor en len, depende de ti enviar el resto de la cadena.

En caso de error devuelve -1.

recv()

La funciΓ³n recv() en C/C++ se utiliza para recibir datos desde un socket. Es comΓΊnmente usada en aplicaciones de red para recibir informaciΓ³n desde un cliente o servidor. Esta funciΓ³n se utiliza en el lado del receptor para leer los datos enviados por el lado del remitente.

size_t recv(int sockfd, void *buf, size_t len, int flags);
  • sockfd: El descriptor de socket desde el cual recibir los datos.

  • buf: Puntero al bΓΊfer donde se almacenarΓ‘n los datos recibidos.

  • len: TamaΓ±o mΓ‘ximo de los datos que se pueden recibir, en bytes.

  • flags: Opciones para la recepciΓ³n de datos.

recv() devuelve el nΓΊmero de bytes realmente leΓ­dos en el bΓΊfer o -1 en caso de error (con errno establecido en consecuencia).

Β‘Espera! Β‘recv() tambiΓ©n puede devolver 0! Esto solo puede significar una cosa: Β‘el lado remoto ha cerrado la conexiΓ³n contigo! Un valor de retorno de 0 es la forma en que recv() te indica que esto ha ocurrido.

Uso basico

char *msg = "Beej was here!";
int len, bytes_sent;
.
.
.
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);

Last updated