Abrir un socket en C

Jinku Hu 12 octubre 2023
Abrir un socket en C

Este artículo explicará varios métodos de cómo abrir un socket en C.

Utilice las funciones listen y accept para abrir un socket en C

listen y accept son parte de las instalaciones de red de UNIX, a veces llamadas API de sockets, que implementan la funcionalidad principal para manejar las comunicaciones entre procesos. Tenga en cuenta que un socket es una representación abstracta del punto final de la ruta de comunicación. Existen diferentes tipos de sockets, por ejemplo, dominio Unix y dominio de Internet, pero todos siguen procedimientos similares para establecer la comunicación entre dos procesos.

Tenga en cuenta que la creación de redes es solo una comunicación entre dos programas en ejecución. Al principio, se llama a la función socket para crear el punto final y el identificador correspondiente. Los sockets se manejan con descriptores de archivos similares a los archivos o conductos regulares en los sistemas UNIX. Después de la llamada socket, tiene dos escenarios dependiendo del tipo de programa que esté desarrollando. Generalmente, tenemos un modelo cliente-servidor en la comunicación de red, donde un programa (servidor) necesita esperar a que otros (clientes) se conecten con él.

En este ejemplo, implementamos un programa del lado del servidor que necesita escuchar el punto final específico y aceptar conexiones de cliente. En este escenario, la función vincular se llama después de socket para vincularse a la dirección específica donde el programa aceptará las conexiones entrantes. Después de vincular, las funciones listen y accept se llaman para formar un llamado conector pasivo. Tenga en cuenta que accept bloquea el proceso hasta que el proceso diferente no establece una conexión mediante la llamada a la función connect.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}

Por lo general, la llamada a accept va seguida del código que implementa el manejo de conexiones entrantes. Un programa externo que quiera establecer una conexión con este servidor necesita conocer la dirección y el puerto. Suponiendo que ambos son conocidos por el proceso, invoca la función connect después de la llamada socket y, con suerte, se conecta al proceso del servidor. Una vez que se establece la conexión, ambos procesos pueden escribir y leer desde el descriptor de socket como si tuvieran un canal bidireccional. En escenarios del mundo real, se supone que varios procesos de cliente se conectan al servidor simultáneamente; el código de manejo de la conexión debe implementarse al mismo tiempo. De lo contrario, el servidor no podrá dar servicio a más de un cliente a la vez.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { MAXLINE = 4096, LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;
  char buf[MAXLINE];

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  size_t ret = read(connfd, buf, MAXLINE);
  if (ret == -1) handle_error("read");

  printf("read %d bytes\nmessage: %s\n", connfd, buf);

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Artículo relacionado - C Networking