accept, select i obsluga błędów

0

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

Jest kilka możliwych reakcji na taki błąd.

  1. 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.

  1. 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.

1 użytkowników online, w tym zalogowanych: 0, gości: 1