Ein einfacher Echo-Server
Bedienen Sie viele Clients gleichzeitig.
Ein einfacher Echo-Server ist eine kostenlose C Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C Academy-Kurs umfasst insgesamt 4 Lektionen.
Was wir erstellen
Ein Echo-Server nimmt TCP-Verbindungen an und sendet genau die Bytes zurück, die ein Client sendet. Er ist das „Hello World“ der Netzwerkprogrammierung.
Wir erstellen ihn auf Basis von epoll, sodass ein einzelner Thread viele Clients gleichzeitig bedienen kann. Die Bestandteile sind: ein Listening-Socket, die epoll-Schleife und die Verarbeitung zum Lesen und anschließenden Schreiben pro Client.
Den Listening-Socket erstellen
Beginnen Sie mit socket() für einen IPv4-TCP-Endpunkt. Die drei Angaben sind AF_INET, SOCK_STREAM und das Protokoll 0.
Dies gibt einen Dateideskriptor zurück, den wir binden, auf Non-Blocking setzen und schließlich bei epoll registrieren. Prüfen Sie bei jedem Systemaufruf auf -1.
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1) { perror("socket"); exit(1); }Die Adresse wiederverwenden
Setzen Sie vor dem Binden mit setsockopt() die Option SO_REUSEADDR. Dadurch kann der Server neu gestartet und erneut an denselben Port gebunden werden, ohne das Ende des TIME_WAIT-Zustands abzuwarten.
Ohne diese Option schlägt ein schneller Neustart häufig mit EADDRINUSE fehl, was bei der Entwicklung für eine frustrierende Überraschung sorgt.
int yes = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);An einen Port binden
Füllen Sie eine struct sockaddr_in mit der Adressfamilie, dem Port und der Adresse. Verwenden Sie htons() für den Port und INADDR_ANY, um auf allen Schnittstellen zu lauschen.
Rufen Sie anschließend bind() auf, um den Socket an diese Adresse zu binden. htons konvertiert die Byte-Reihenfolge des Hosts in die erforderliche Byte-Reihenfolge des Netzwerks.
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8080);
bind(listen_fd, (struct sockaddr*)&addr, sizeof addr);Lauschen und auf Non-Blocking umstellen
Rufen Sie listen() auf, um den Socket als passiv zu markieren und eine Warteschlangengröße für ausstehende Verbindungen festzulegen. SOMAXCONN fordert das vom System unterstützte Maximum an.
Setzen Sie den Socket anschließend auf Non-Blocking, damit accept() die Ereignisschleife niemals blockiert. Nun kann er bei epoll registriert werden.
listen(listen_fd, SOMAXCONN);
set_nonblocking(listen_fd);Den Listener registrieren
Erstellen Sie die epoll-Instanz und fügen Sie den Listening-Socket mit EPOLLIN hinzu. Ein lesbares Ereignis bedeutet hier, dass ein neuer Client auf die Annahme wartet.
Wir speichern den fd in data.fd, damit wir den Listener innerhalb der Schleife erkennen können.
int epfd = epoll_create1(0);
struct epoll_event ev = { .events = EPOLLIN, .data.fd = listen_fd };
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);Einen Client annehmen
Wenn der Listener lesbar ist, rufen Sie accept() wiederholt auf, bis EAGAIN zurückgegeben wird. Jeder neue Client-Socket wird auf Non-Blocking gesetzt und für EPOLLIN registriert.
Von nun an teilt uns epoll mit, sobald dieser Client Daten sendet, die wir zurückgeben können.
int c = accept(listen_fd, NULL, NULL);
if (c != -1) {
set_nonblocking(c);
struct epoll_event cev = { .events = EPOLLIN, .data.fd = c };
epoll_ctl(epfd, EPOLL_CTL_ADD, c, &cev);
}Die Echo-Logik
Lesen Sie bei einem bereiten Client mit recv() in einen Puffer. Wenn Sie Bytes erhalten, senden Sie dieselben Bytes direkt mit send() zurück.
Dieses einfache Lesen und anschließende Schreiben ist der Echo-Vorgang. In einer produktiven Anwendung würden Sie nicht gesendete Bytes puffern; für eine Demonstration nehmen wir jedoch an, dass die kleine Antwort in den Sendepuffer passt.
char buf[4096];
ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r > 0) {
send(fd, buf, r, 0);
}Eine Trennung erkennen
Ein Rückgabewert von 0 bei recv() bedeutet, dass der Client die Verbindung ordnungsgemäß geschlossen hat. Ein negativer Rückgabewert mit etwas anderem als EAGAIN ist ein echter Fehler.
Entfernen Sie den Deskriptor in beiden Fällen aus epoll und schließen Sie ihn mit close(), um Ressourcen freizugeben und weitere Ereignisse zu verhindern.
if (r == 0 || (r < 0 && errno != EAGAIN)) {
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
}Die vollständige Schleife
Fügen Sie die Bestandteile zusammen: Warten Sie auf Ereignisse, unterscheiden Sie anhand des Deskriptors zwischen Listener und Client und reagieren Sie entsprechend.
Diese kompakte Schleife ist ein vollständig funktionsfähiger nebenläufiger Echo-Server. Ein Thread, viele Clients, keine Threads pro Verbindung.
for (;;) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) {
if (events[i].data.fd == listen_fd) accept_clients();
else echo_or_close(events[i].data.fd);
}
}Testen und nächste Schritte
Kompilieren Sie unter Linux und verbinden Sie sich mit nc localhost 8080; alles, was Sie eingeben, wird zurückgesendet. Öffnen Sie mehrere Terminals, um die Nebenläufigkeit zu demonstrieren.
Für eine robustere Implementierung sollten Sie Teilsendungen puffern, EPOLLOUT verarbeiten, mit vollständigem Leeren auf EPOLLET umstellen und EPOLLRDHUP für halb geschlossene Verbindungen verarbeiten.
Kurze Überprüfung
Überprüfen Sie Ihr Verständnis des Ablaufs eines Echo-Servers.
Zusammenfassung
Sie haben einen Echo-Server mit epoll erstellt: erstellen, SO_REUSEADDR setzen, binden, lauschen und in den nicht blockierenden Modus wechseln. Registrieren Sie den Listener, akzeptieren Sie Clients in einer Schleife und senden Sie empfangene Daten mit recv()/send() zurück.
Behandeln Sie einen Rückgabewert von 0 als Trennung und schließen Sie die Verbindung ordnungsgemäß. Auf Grundlage dieses Gerüsts können Sie einen leistungsfähigen Single-Thread-Netzwerkserver entwickeln.
Häufig gestellte Fragen
Ist die Lektion „Ein einfacher Echo-Server“ kostenlos?
Ja — der vollständige Text von „Ein einfacher Echo-Server“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Ein einfacher Echo-Server“?
Bedienen Sie viele Clients gleichzeitig. Du übst C Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um C Academy zu starten?
Keine Vorkenntnisse erforderlich. C Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „Ein einfacher Echo-Server“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser C Academy-Lektion Code schreiben und ausführen?
Ja. Jede C Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Blockierendes und nicht blockierendes I/O
- epoll einrichten
- Die Event Loop
- Ein einfacher Echo-Server