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