C socket UPD отправить / отменить тайм-аут / повторить попытку

#c #sockets #timeout #sendto

#c #сокеты #тайм-аут #отправить

Вопрос:

Как я могу повторить попытку отправки (скажем, используя цикл while или что-то подобное) в следующем коде, который у меня есть, всякий раз, когда у меня есть тайм-аут? Я сократил некоторые части своего кода.

Я не знаком с кодами ошибок C и обработкой ошибок, поэтому я не знаю, где перехватывать / обрабатывать ошибку и какой код ошибки искать.

 sock = socket(create socket....)
if (sock < 0 ) { 
    exit(EXIT_FAILURE); 
} 
servaddr initializations.....

sendto(sock, etc etc........);

struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,amp;timeout,sizeof(timeout)) < 0) {
    perror("Error");
}
addrlen = sizeof(servaddr);
if(recvfrom (sock, etc, etc......) < 0)
{
     printf("revfrom failed.n");
}
  

Комментарии:

1. Что вы имеете в виду под «как я мог бы создать повторную попытку»? Просто проверьте возвращаемое значение и повторно выполните системный вызов, если он завершится неудачей. В чем проблема с этим?

2. Извините, я имел в виду, как я могу повторить отправку, если есть тайм-аут. Я не могу понять, где в моем коде я могу вставить цикл.

3. recvfrom устанавливает errno значение EAGAIN или EWOULDBLOCK , если сбой произошел из-за истечения времени ожидания.

Ответ №1:

От man 7 socket :

SO_RCVTIMEO и SO_SNDTIMEO :

Укажите время ожидания приема или отправки до сообщения об ошибке. Аргументом является a struct timeval . Если функция ввода или вывода блокируется в течение этого периода времени, и данные были отправлены или получены, возвращаемым значением этой функции будет объем переданных данных; если данные не были переданы и время ожидания было достигнуто, то -1 возвращается errno значение EAGAIN или EWOULDBLOCK , или EINPROGRESS (для connect(2) )точно так же, как если бы сокет был указан как неблокирующий. Если тайм-аут установлен на ноль (по умолчанию), то операция никогда не будет тайм-аута. Тайм-ауты действуют только для системных вызовов, которые выполняют ввод-вывод сокета (например, read(2) , recvmsg(2) , send(2) , sendmsg(2) ); тайм-ауты не влияют на select(2) , poll(2) , epoll_wait(2) , и так далее.

Итак, в вашем случае код для продолжения попыток при достижении тайм-аута будет выглядеть примерно так:

 struct timeval timeout = {.tv_sec = 5, .tv_usec = 0};

if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, amp;timeout, sizeof(timeout)) < 0) {
    perror("setsockopt failed");
    // Handle error
}

// ...

while (sendto(sock, /*...*/) == -1) {
    if (errno != EAGAIN amp;amp; errno != EWOULDBLOCK) {
        // Some unexpected error happened.
        perror("sendto failed");
    }

    // Otherwise it was a timeout, just continue trying.
}
  

Обратите внимание, что SO_SNDTIMEO это для отправки и SO_RCVTIMEO для получения. Если вы хотите установить оба, выполните два setsockopt вызова.


В любом случае, мне кажется, что вы тратите свое время на это. Если вы хотите продолжать попытки, пока не получите данные, тогда просто не беспокойтесь о каких-либо setsockopt действиях, поскольку поведение по умолчанию заключается в том, чтобы ждать неопределенно долго, пока не будут получены данные:

Если тайм-аут установлен на ноль (по умолчанию), то операция никогда не будет тайм-аута.