Sockets
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_INETfür IPv4 *AF_INET6fü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_INETfür IPv4AF_INET6für IPv6
type– Art der Verbindung:SOCK_STREAMfür TCPSOCK_DGRAMfür UDP
protocol– normalerweise0(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 (vonsocket())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 (auslisten())addr– Zeiger auf eine Struktur, die die Adresse des Clients enthältaddrlen– 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
- getaddrinfo (C-Funktion)
- bind (C-Funktion)
- connect (C-Funktion)
- send (C-Funktion)
- recv (C-Funktion)
- TCP/IP-Modell
- UDP (Protokoll)
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