Ouvrez un socket en C

Jinku Hu 12 octobre 2023
Ouvrez un socket en C

Cet article explique plusieurs méthodes pour ouvrir une socket dans C.

Utilisez les fonctions listen et accept pour ouvrir un socket en C

listen et accept font partie des fonctionnalités de mise en réseau UNIX, parfois appelées API Sockets, qui implémentent la fonctionnalité de base pour gérer les communications entre processus. Notez qu’une socket est une représentation abstraite du point de terminaison du chemin de communication. Il existe différents types de sockets, par exemple, le domaine Unix et le domaine Internet, mais tous suivent des procédures similaires pour établir la communication entre deux processus.

Gardez à l’esprit que la mise en réseau n’est que la communication entre deux programmes en cours d’exécution. Dans un premier temps, la fonction socket est appelée pour créer le noeud final et le handle correspondant. Les sockets sont gérés avec des descripteurs de fichiers similaires aux fichiers ou canaux normaux sur les systèmes UNIX. Après l’appel socket, il a deux scénarios selon le type de programme que vous développez. En général, nous avons un modèle client-serveur dans la communication réseau, où un programme (serveur) doit attendre que d’autres (clients) se connectent avec lui.

Dans cet exemple, nous implémentons un programme côté serveur qui doit écouter le point de terminaison spécifique et accepter les connexions client. Dans ce scénario, la fonction bind est appelée après socket pour se lier à l’adresse spécifique où le programme acceptera les connexions entrantes. Après bind, les fonctions listen et accept sont appelées pour former une socket dite passive. Notez que accept bloque le processus jusqu’à ce que le processus différent n’établisse pas de connexion à l’aide de l’appel de fonction 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);
}

Habituellement, l’appel accept est suivi du code qui implémente la gestion des connexions entrantes. Un programme externe qui souhaite établir une connexion avec ce serveur doit connaître l’adresse et le port. En supposant que les deux sont connus du processus, il invoque la fonction connect après l’appel socket et, espérons-le, se connecte au processus serveur. Une fois la connexion établie, les deux processus peuvent écrire et lire à partir du descripteur de socket comme ayant un canal bidirectionnel. Dans les scénarios du monde réel, plusieurs processus client sont censés se connecter au serveur simultanément; le code de gestion de connexion doit être implémenté simultanément. Sinon, le serveur ne pourra pas desservir plus d’un client à la fois.

#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);
}
Auteur: 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

Article connexe - C Networking