Obsługa wielu klientów
select i poll
Obsługa wielu klientów to bezpłatna lekcja C Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C Academy zawiera 4 lekcji w sumie.
Problem współbieżności
Prosty serwer obsługuje jednego klienta naraz. Jeśli klient działa powoli, wszyscy pozostali muszą czekać. Aby obsługiwać wielu klientów współbieżnie, potrzebny jest inny model.
Podejścia do współbieżności
Trzy typowe strategie:
- Osobny wątek lub proces dla każdego klienta: proste rozwiązanie, ale przy dużej skali wymaga znacznych zasobów
- Multipleksowanie operacji wejścia-wyjścia za pomocą
selectlubpoll: jeden wątek monitoruje wiele socketów - Pętle zdarzeń z użyciem
epoll/kqueue: skalują się do tysięcy połączeń
Działanie select
select() monitoruje zbiór deskryptorów plików i informuje, które z nich są gotowe do odczytu lub zapisu albo mają błędy — wszystko w ramach jednego blokującego wywołania. Dzięki temu jeden wątek może obsługiwać wiele socketów.
Podstawy fd_set
select używa maski bitowej fd_set. Do zarządzania nią służą makra: FD_ZERO, FD_SET, FD_CLR oraz FD_ISSET.
#include <sys/select.h>
void setup_set(fd_set *set, int listen_fd) {
FD_ZERO(set); /* clear all */
FD_SET(listen_fd, set); /* watch the listener */
}Wywoływanie select
select wymaga podania najwyższego numeru deskryptora powiększonego o jeden i modyfikuje zbiór w miejscu, oznaczając gotowe deskryptory. Ponieważ operacja niszczy zawartość zbioru, przed każdym wywołaniem trzeba go utworzyć ponownie.
#include <sys/select.h>
#include <stdio.h>
int wait_ready(int maxfd, fd_set *read_set) {
int n = select(maxfd + 1, read_set, NULL, NULL, NULL);
if (n < 0) perror("select");
return n; /* number of ready fds */
}Pętla serwera z użyciem select
Schemat działania jest następujący: utworzyć zbiór ponownie, wywołać select, a następnie dla każdego gotowego deskryptora albo zaakceptować nowego klienta (jeśli jest to deskryptor nasłuchujący), albo odczytać dane (jeśli jest to deskryptor klienta).
#include <sys/select.h>
void loop_skeleton(int listen_fd, fd_set *master, int maxfd) {
fd_set work;
for (;;) {
work = *master; /* copy, select destroys it */
select(maxfd + 1, &work, NULL, NULL, NULL);
for (int fd = 0; fd <= maxfd; fd++) {
if (!FD_ISSET(fd, &work)) continue;
/* fd == listen_fd -> accept; else -> recv */
}
}
}Śledzenie klientów
Po zaakceptowaniu nowego połączenia należy dodać jego deskryptor do zbioru głównego i zaktualizować maxfd. Gdy klient się rozłączy (recv zwróci 0), należy wywołać close i usunąć deskryptor ze zbioru za pomocą FD_CLR.
#include <sys/select.h>
#include <unistd.h>
void add_client(fd_set *master, int *maxfd, int conn) {
FD_SET(conn, master);
if (conn > *maxfd) *maxfd = conn;
}
void drop_client(fd_set *master, int conn) {
close(conn);
FD_CLR(conn, master);
}Ograniczenia select
select ma kilka wad:
- Ograniczenie do
FD_SETUPdeskryptorów (często 1024) - Przeszukiwanie wszystkich deskryptorów przy każdym wywołaniu w czasie O(n)
- Odtwarzanie zbioru w każdej iteracji
W przypadku wielu połączeń lepiej skalują się poll lub epoll.
Możliwości poll
poll() przyjmuje tablicę elementów struct pollfd zamiast stałej maski bitowej, dzięki czemu nie ma ograniczenia do 1024 deskryptorów i nie niszczy danych wejściowych przy każdym wywołaniu.
#include <poll.h>
void setup_poll(struct pollfd *pfd, int listen_fd) {
pfd[0].fd = listen_fd;
pfd[0].events = POLLIN; /* notify when readable */
}Wywoływanie poll
poll blokuje działanie do momentu, gdy co najmniej jeden deskryptor będzie gotowy (albo upłynie limit czasu), a następnie ustawia pole revents dla każdego gotowego elementu.
#include <poll.h>
#include <stdio.h>
int poll_ready(struct pollfd *pfds, int count) {
int n = poll(pfds, count, -1); /* -1 = block forever */
if (n < 0) perror("poll");
return n;
}Skalowanie: epoll
W przypadku tysięcy połączeń systemowy mechanizm Linuxa epoll (oraz mechanizm BSD kqueue) zwraca tylko gotowe deskryptory, zapewniając koszt O(1) na zdarzenie i unikając pełnego przeszukiwania wykonywanego przez select i poll.
Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat obsługi wielu klientów.
Podsumowanie
Dowiedziano się, jak obsługiwać wielu klientów za pomocą jednego wątku.
selectmonitoruje wiele deskryptorów; do zarządzania nimi służą makra FD_- Zbiór trzeba odtwarzać w każdej iteracji, ponieważ select go niszczy
- Zaakceptowanych klientów należy dodawać do zbioru, a po rozłączeniu usuwać
pollomija limit 1024 deskryptorów, aepollskaluje się do tysięcy połączeń
Często zadawane pytania
Czy lekcja „Obsługa wielu klientów” jest bezpłatna?
Tak — pełny tekst „Obsługa wielu klientów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C Academy, przejdź na CoddyKit PRO. Kurs C Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Obsługa wielu klientów”?
select i poll Ćwiczysz C Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C Academy?
Nie wymagamy żadnego doświadczenia. C Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Obsługa wielu klientów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C Academy?
Tak. Każda lekcja C Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Przegląd gniazd
- Serwer TCP
- Klient TCP
- Obsługa wielu klientów