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