Czy jest sens obsługiwać błędy funkcji takich jak select i accept. Jeżęli mam serwer, który powinien działać nieprzerwanie, a tego typu funkcja zwraca błąd to co należy zrobić ?? Chyba nie przerywać działania programu ?
0
0
Jest kilka możliwych reakcji na taki błąd.
- Zawsze przerywać działanie programu najlepiej z jasnym i czytelnym logiem. Sam stosuję takie podejście w niewielkich programikach, tam gdzie głównie interesują mnie "OK scenario". Żeby zwiększyć czytelność kodu używam takiego helpera-a:
static void check_errors(const char *message, int result)
{
if (result < 0)
{
perror(message);
exit(-1);
}
}
z use case-ami jak np.:
int client_fd = accept(server_fd, (sockaddr*)&clientaddr, &clientlen);
check_errors("accept", client_fd);
Perror podglada zawartosć errno przez co dostajemy czytelny komunikat o błędzie np.
accept: A connection has been aborted.
- Kontynuuować z reakcją odpowiednią do zgłoszonego błędu.
Tak zazwyczaj robią większe aplikacje np. serwery http, które muszą być odporne na wszelkiego rodzaju błędy. Dla przykładu wspomniany przez ciebie accept.
Accept może sfailować z wielu powodów ( http://linux.die.net/man/2/accept). W trakcie handshake-a TCP host może się rozmyślić i wysłać segment RST, co spowoduje zwrócenie przez accept-a ECONNABORTED. Na obciążonym systemie może zabraknąć zasobów, wtedy poleci np. ENOMEM (brak pamięci na socket buffer) albo EMFILE (osiągnieto limit file deskryptorów). Ponadto jeśli socket jest w trybie nieblokujacym może się zdarzyć EAGAIN.
Wszystko to musi zostać obsłużone jakimś wypasionym switcho-casem w implementacji serwera, ale ze względu na izolację niezależnych klientów żaden z tych błędów accepta nie może mieć wpływu na stabilną pracę serwera. I rzeczywiście tak jest, przykład ze źródeł serwera Nginx, plik src/event/ngx_event_accept.c:
void ngx_event_accept(ngx_event_t *ev)
{
(...)
s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
if (s == (ngx_socket_t) -1)
{
err = ngx_socket_errno;
if (err == NGX_EAGAIN)
{
// socket jest w trybie nieblokującym i nie ma w tym momencie żadnego połączenia z zewnątrz,
// logujemy i wychodzimy z ngx_event_accept. Próbujemy znowu za jakiś czas.
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"accept() not ready");
return;
}
level = NGX_LOG_ALERT;
if (err == NGX_ECONNABORTED)
{
// ECONNABORTED, logujemy error [1], olewamy tego klienta i przechodzimy do obsługi kolejnych klientów [2]
level = NGX_LOG_ERR;
}
else
if (err == NGX_EMFILE || err == NGX_ENFILE)
{
// EMFILE lub ENFILE - wyczerpano pulę dostępnych file descriptorów, logujemy error [1], wyłączamy nasłuchiwanie na kolejnych klientów [3],
// i wychodzimy z ngx_event_accept [4]
level = NGX_LOG_CRIT;
}
ngx_log_error(level, ev->log, err, "accept() failed"); // [1]
if (err == NGX_ECONNABORTED)
{
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)
{
ev->available--;
}
if (ev->available) {
continue; // [2]
}
}
if (err == NGX_EMFILE || err == NGX_ENFILE)
{
if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle, 1) // [3]
!= NGX_OK)
{
return;
}
(..)
}
return; // [4]
}
(...)
}
Jak widać Nginx nawet w sytuacji braku zasobów nie wywołuje żadnego exit-a,
ani nie zamyka aktywnych połączeń tylko spokojnie kontynuuje działanie tyle że już bez nasłuchu.
Zarejestruj się i dołącz do największej społeczności programistów w Polsce.
Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.