2015-01-22 3 views
2

Как указано в accept() человек страницы в RETURN VALUE разделе:ERRNO после принимать в программировании сокетов Linux

обработки
Linux accept()accept4()) Ошибка проходит уже ожидающие сетевые ошибки на новый сокет, как код ошибки от accept(). Это поведение отличается от других реализаций сокетов BSD. Для надежной работы приложение должно обнаруживать сетевые ошибки, определенные для протокола после accept(), и обрабатывать их как EAGAIN путем повторной попытки. В случае TCP/IP, это ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP и ENETUNREACH.

Означает ли это, нужно проверить значение errno сразу после возвращения accept() и перед проверкой возвращаемого значения accept()? если да, и если установлено errno, какие шаги необходимо предпринять?

вот фрагмент кода обработки мой код accept():

newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 
if((errno == ENETDOWN || errno == EPROTO || errno == ENOPROTOOPT || errno == EHOSTDOWN || 
    errno == ENONET || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENETUNREACH)) 
    return; 
if (newsockfd < 0) 
{ 
    // error 
} 
else if(newsockfd > 0) 
{ 
    // accepted a new connection 
} 
else 
{ 
    // blah blah blah 
} 

я пришел к выводу, что в данном случае можно было бы попробовать еще раз через некоторое время. Верно ли мое заключение?

ответ

2

Во-первых, вы проверяете accept() возвращаемое значение. Если accept() возвращаемое значение меньше 0, тогда вы должны проверить errno. Если это ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP или ENETUNREACH, то вы можете позвонить accept() снова. В противном случае произошло что-то плохое, и вы должны перестать звонить accept() (вы, например, пропустили плохой слуховой сокет, например, accept()).

Вот как я понимаю код.

А вот как обработка ошибок может быть сделано:

while (running) { 
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 
    if (newsockfd < 0) 
    { 
     // error 
     perror ("accept"); 
     if((errno == ENETDOWN || errno == EPROTO || errno == ENOPROTOOPT || errno == EHOSTDOWN || 
      errno == ENONET || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENETUNREACH)) { 
      continue; 
     } 
     exit (EXIT_FAILURE); 
    } 

    // accepted a new connection 
    // blah blah blah 
} 
+0

поэтому, если 'errno' не равно ни одному из отмеченных значений, нам нужно закрыть дескриптор файла' sockfd' и открыть новый? как насчет 'cli_addr'? – Barracuda

+0

Никогда не видеть, что кто-то снова открывает 'sockfd' после того, как accept не удалось. 'sockfd' - это слуховой сокет, возвращаемый из' bind'. Что может случиться с ним после его создания? Никогда этого не видеть. cli_addr is – alexander

+0

Также вы должны проверить для 'EAGAIN' и' EWOULDBLOCK' для сокетов сокетов, открытых с помощью 'O_NONBLOCK', и снова принять вызов. – alexander

3

По SUSv4:

После успешного завершения, accept() возвращает неотрицательное дескриптор файла сокета. В противном случае возвращается -1 и errno для указания ошибки.

Это означает, что вам нужно только проверить errno если таковые accept() возвращается -1.

Ваш код может выглядеть следующим образом:

ret = accept(fd, &addr, sizeof (addr)); 
if (ret == -1) { 
    switch (errno) { 
    case EAGAIN: 
    case EWOULDBLOCK: 
     /* do something */ 
     break; 
    case EBADF: 
     /* do something different */ 
     break; 
    default: 
     /* do something even more different */ 
    } 
} 

(. Как именно вы обрабатывать каждое условие ошибки будет зависеть от вашего приложения)

Кроме того, важно, чтобы проверить errnoсразу после проверяя возвращаемое значение accept(). Если вы сначала вызываете какие-либо другие функции (даже простые fprintf()), вы рискуете перезаписать errno с другой ошибкой.

+0

Я до проголосовал ответ за напоминание, чтобы проверить 'errno' немедленно, прежде чем делать что-либо, что может изменить его. – Barracuda

Смежные вопросы