Öffnen eines Sockets in C

Jinku Hu 12 Oktober 2023
Öffnen eines Sockets in C

In diesem Artikel werden verschiedene Methoden zum Öffnen eines Sockets in C erläutert.

Verwenden Sie die Funktionen listen und accept, um einen Socket in C zu öffnen

listen und accept sind Teil der UNIX-Netzwerkeinrichtungen, die manchmal als Sockets-API bezeichnet werden und die Kernfunktionalität für die Kommunikation zwischen Prozessen implementieren. Beachten Sie, dass ein Socket eine abstrakte Darstellung des Endpunkts für den Kommunikationspfad ist. Es gibt verschiedene Arten von Sockets, z. B. Unix Domain und Internet Domain, aber alle folgen ähnlichen Verfahren zum Herstellen der Kommunikation zwischen zwei Prozessen.

Beachten Sie, dass das Networking nur die Kommunikation zwischen zwei laufenden Programmen ist. Zunächst wird die Funktion socket aufgerufen, um den Endpunkt und das entsprechende Handle zu erstellen. Sockets werden mit Dateideskriptoren behandelt, die den regulären Dateien oder Pipes auf UNIX-Systemen ähneln. Nach dem Aufruf von socket gibt es je nach Art des zu entwickelnden Programms zwei Szenarien. Im Allgemeinen haben wir ein Client-Server-Modell in der Netzwerkkommunikation, bei dem ein Programm (Server) darauf warten muss, dass andere (Clients) eine Verbindung herstellen.

In diesem Beispiel implementieren wir ein serverseitiges Programm, das den spezifischen Endpunkt abhören und Clientverbindungen akzeptieren muss. In diesem Szenario wird die Funktion Binden nach socket aufgerufen, um an die spezifische Adresse zu binden, an der das Programm die eingehenden Verbindungen akzeptiert. Nach bind werden die Funktionen listen und accept aufgerufen, um einen sogenannten passiven Socket zu bilden. Beachten Sie, dass accept den Prozess blockiert, bis der andere Prozess mit dem Funktionsaufruf connect keine Verbindung mehr herstellt.

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

Normalerweise folgt auf den accept-Aufruf der Code, der die Behandlung eingehender Verbindungen implementiert. Ein externes Programm, das eine Verbindung zu diesem Server herstellen möchte, muss die Adresse und den Port kennen. Unter der Annahme, dass beide dem Prozess bekannt sind, ruft er nach dem Aufruf von socket die Funktion connect auf und wird hoffentlich mit dem Serverprozess verbunden. Sobald die Verbindung hergestellt ist, können beide Prozesse aus dem Socket-Deskriptor mit einem bidirektionalen Kanal schreiben und lesen. In realen Szenarien sollen mehrere Client-Prozesse gleichzeitig eine Verbindung zum Server herstellen. Der Verbindungsbehandlungscode sollte gleichzeitig implementiert werden. Andernfalls kann der Server nicht mehr als einen Client gleichzeitig bedienen.

#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

Verwandter Artikel - C Networking