Apri un socket in C

Jinku Hu 12 ottobre 2023
Apri un socket in C

Questo articolo spiegherà diversi metodi su come aprire un socket in C.

Usa le funzioni listen e accept per aprire un socket in C

listen e accept fanno parte delle funzionalità di rete UNIX, a volte chiamate API Sockets, che implementano le funzionalità principali per gestire le comunicazioni tra processi. Si noti che un socket è una rappresentazione astratta dell’endpoint per il percorso di comunicazione. Esistono diversi tipi di socket, ad esempio il dominio Unix e il dominio Internet, ma tutti seguono procedure simili per stabilire la comunicazione tra due processi.

Tieni presente che la rete è solo la comunicazione tra due programmi in esecuzione. All’inizio, viene chiamata la funzione socket per creare l’endpoint e l’handle corrispondente. I socket vengono gestiti con descrittori di file simili ai normali file o pipe sui sistemi UNIX. Dopo la chiamata socket, ha due scenari a seconda del tipo di programma che stai sviluppando. In generale, abbiamo un modello client-server nella comunicazione di rete, in cui un programma (server) deve attendere che altri (client) si connettano con esso.

In questo esempio, implementiamo un programma lato server che deve ascoltare l’endpoint specifico e accettare le connessioni client. In questo scenario, la funzione bind viene chiamata dopo socket per collegarsi all’indirizzo specifico dove il programma accetterà le connessioni in entrata. Dopo bind, le funzioni listen e accept sono chiamate le funzioni per formare una cosiddetto socket passivo. Notare che accept blocca il processo fino a quando il diverso processo non stabilisce una connessione utilizzando la chiamata alla funzione 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);
}

Di solito, la chiamata accept è seguita dal codice che implementa la gestione delle connessioni in entrata. Un programma esterno che desidera stabilire una connessione con questo server deve conoscere l’indirizzo e la porta. Supponendo che entrambi siano noti al processo, invoca la funzione connect dopo la chiamata socket e si spera, si connette al processo del server. Una volta stabilita la connessione, entrambi i processi possono scrivere e leggere dal descrittore del socket poiché hanno un canale bidirezionale. Negli scenari del mondo reale, si suppone che più processi client si colleghino al server contemporaneamente; il codice di gestione della connessione dovrebbe essere implementato contemporaneamente. In caso contrario, il server non sarà in grado di servire più di un client alla volta.

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

Articolo correlato - C Networking