Sockets

Aus dev.kaibel.net
Zur Navigation springen Zur Suche springen

Socket-Programmierung in C

Die Socket-Programmierung in C ermöglicht die Kommunikation zwischen Prozessen über ein Netzwerk. Sie bildet die Grundlage vieler Internetanwendungen wie Webserver, Chatprogramme oder Dateitransfers.

Obwohl C keine "Socket-Klasse" im objektorientierten Sinne besitzt, wird der Begriff oft verwendet, um die Funktionalität rund um die Socket-API zu beschreiben, die über Header-Dateien wie <sys/socket.h> und <netinet/in.h> bereitgestellt wird.

Grundlagen

Ein Socket ist eine Schnittstelle (Endpunkt) für die Kommunikation zwischen zwei Rechnern über ein Netzwerk. In C werden Sockets über Dateideskriptoren (int-Werte) dargestellt, ähnlich wie Dateien.

Typische Kommunikation:

Client <----> Server

---

Wichtige Funktionen der Socket-API

1. socket()

Erzeugt einen neuen Socket.

int socket(int domain, int type, int protocol);
  • domain – Kommunikationsdomäne, z. B.:
 * AF_INET für IPv4
 * AF_INET6 für IPv6  
  • type – Typ des Sockets:
 * SOCK_STREAM (TCP)
 * SOCK_DGRAM (UDP)  
  • protocol – Meist 0 (Standardprotokoll für Typ)
    • Beispiel:**
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

---

2. bind()

Verknüpft den Socket mit einer lokalen Adresse (z. B. Port).

int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
    • Beispiel (Server):**
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);

bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

---

3. listen()

Versetzt den Socket in den "Lauschmodus" für eingehende Verbindungen (nur TCP).

int listen(int sockfd, int backlog);

---

4. accept()

Nimmt eine eingehende Verbindung an.

int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);

---

5. connect()

Verbindet einen Client-Socket mit einem Server.

int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

---

6. send() / recv()

Dient dem Senden und Empfangen von Daten über TCP.

ssize_t send(int sockfd, const void* buf, size_t len, int flags);
ssize_t recv(int sockfd, void* buf, size_t len, int flags);

---

7. close()

Schließt den Socket.

close(sockfd);

---

Beispiel: Einfacher TCP-Server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    const char* message = "Hallo vom Server!";

    // Socket erstellen
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // Adresse zuweisen
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    bind(server_fd, (struct sockaddr*)&address, sizeof(address));

    // Auf Verbindungen warten
    listen(server_fd, 3);
    printf("Server wartet auf Verbindung...\n");

    // Verbindung annehmen
    new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
    printf("Client verbunden!\n");

    // Daten empfangen und antworten
    read(new_socket, buffer, sizeof(buffer));
    printf("Client sagt: %s\n", buffer);
    send(new_socket, message, strlen(message), 0);

    close(new_socket);
    close(server_fd);
    return 0;
}

---

Beispiel: Einfacher TCP-Client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[1024] = {0};

    // Socket erstellen
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // IP-Adresse des Servers konvertieren
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);

    // Verbindung herstellen
    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    // Nachricht senden
    send(sock, "Hallo Server!", strlen("Hallo Server!"), 0);

    // Antwort empfangen
    read(sock, buffer, sizeof(buffer));
    printf("Server antwortet: %s\n", buffer);

    close(sock);
    return 0;
}

---

Ablauf einer TCP-Verbindung

Server: socket() → bind() → listen() → accept()
Client: socket() → connect()
Beide:  send()/recv()
Beide:  close()

---


Zusammenspiel von socket(), listen() und accept()

Die Funktionen socket(), listen() und accept() gehören zur Berkeley-Socket-API und werden verwendet, um in C oder C++ eine TCP/IP-Verbindung zwischen einem Server und einem Client aufzubauen.

Sie befinden sich in der Header-Datei <sys/socket.h> und werden unter UNIX/Linux-Systemen genutzt.

Übersicht

Funktion Zweck
socket() Erstellt einen neuen Kommunikationsendpunkt (Socket)
listen() Versetzt den Socket in den Wartemodus für eingehende Verbindungen
accept() Akzeptiert eine eingehende Verbindung und erstellt einen neuen Socket für den Client

1. socket()

Erstellt einen neuen Socket und liefert einen Dateideskriptor zurück.

int socket(int domain, int type, int protocol);

Parameter

  • domain – Protokollfamilie, z. B.:
    • AF_INET für IPv4
    • AF_INET6 für IPv6
  • type – Art der Verbindung:
    • SOCK_STREAM für TCP
    • SOCK_DGRAM für UDP
  • protocol – normalerweise 0 (Standardprotokoll)

Rückgabewert

Ein Dateideskriptor (int) für den Socket oder -1 bei Fehler.

Beispiel

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
    perror("socket failed");
    exit(1);
}

2. listen()

Versetzt einen TCP-Socket in den Server-Wartemodus. Der Socket muss vorher mit bind() an eine IP-Adresse und einen Port gebunden sein.

int listen(int sockfd, int backlog);

Parameter

  • sockfd – Socket-Dateideskriptor (von socket())
  • backlog – maximale Anzahl wartender Verbindungen in der Warteschlange

Rückgabewert

0 bei Erfolg, -1 bei Fehler.

Beispiel

if (listen(sockfd, 5) < 0) {
    perror("listen failed");
    exit(1);
}

3. accept()

Akzeptiert eine eingehende Verbindung und erstellt einen neuen Socket für die Kommunikation mit dem Client.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Parameter

  • sockfd – der lauschende Socket (aus listen())
  • addr – Zeiger auf eine Struktur, die die Adresse des Clients enthält
  • addrlen – Zeiger auf die Länge der Adressstruktur

Rückgabewert

Ein neuer Socket-Deskriptor (int) für die Client-Verbindung oder -1 bei Fehler.

Beispiel

struct sockaddr_in clientAddr;
socklen_t addrlen = sizeof(clientAddr);

int newsockfd = accept(sockfd, (struct sockaddr*)&clientAddr, &addrlen);
if (newsockfd < 0) {
    perror("accept failed");
    exit(1);
}

Gesamtbeispiel: Einfacher TCP-Server

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { perror("socket"); return 1; }

    sockaddr_in serverAddr{};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    if (bind(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(sockfd, 5) < 0) {
        perror("listen");
        return 1;
    }

    std::cout << "Server wartet auf Verbindungen..." << std::endl;

    sockaddr_in clientAddr{};
    socklen_t addrlen = sizeof(clientAddr);

    int clientSock = accept(sockfd, (struct sockaddr*)&clientAddr, &addrlen);
    if (clientSock < 0) {
        perror("accept");
        return 1;
    }

    std::cout << "Verbindung hergestellt!" << std::endl;
    ::close(clientSock);
    ::close(sockfd);
}

Quellen


Siehe auch

Quellen

  • Stevens, W. Richard: UNIX Network Programming, Vol. 1, Prentice Hall.
  • ISO/IEC 9899:2018 – Programming Language C
  • GNU/Linux man pages: man 2 socket, man 2 bind, man 2 connect