sendto: ресурс временно недоступен (ошибка 11)

#c #sockets #udp #sendto

#c #сокеты #udp #sendto

Вопрос:

У меня проблема с sendto.

У меня есть получатель, который получает обновленные пакеты с помощью recvfrom, а затем отвечает отправителю с помощью sendto.

К сожалению, я получаю ошибку 11 (ресурс временно недоступен). Я использую два сокета.

Фактически отправлен первый пакет, но не последующие:

sendto :: Успех

ошибка: 0.

sendto :: Ресурс временно недоступен

ошибка: 11.

sendto :: Ресурс временно недоступен

Это выдержка из моего кода:

     int sockfd, sockSend;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
            perror("socket");

    if ((sockSend = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
            perror("socket");

    if (fcntl(sockfd, F_SETOWN, getpid()) < 0) {
            perror("fcntl"); 
    }
    if (fcntl(sockfd, F_SETFL, O_RDONLY | O_NONBLOCK | FASYNC) < 0) {
            perror("fcntl"); 
    } 

    if (bind(sockfd, (struct sockaddr *) amp;serv_addr, sizeof(serv_addr))
                    < 0)
            perror("bind");
  

И в обработчике SIGIO:

     len = sizeof(recv_addr);
    char buffer[payload];
    bzero(buffer, payload);
    n = recvfrom(sockfd, buffer, payload, MSG_DONTWAIT, (struct sockaddr *)amp;recv_addr, amp;len);

    while (n > 0) {

                            sprintf(response, "%dn%dn%dn", items, target_buf, pb_sp);          
                            sendto(sockSend, response, strlen(response), 0, (struct sockaddr *) amp;recv_addr, sizeof(recv_addr));
                            // sleep(1);

                            perror("sendto :");
                            printf("error: %d.n", errno);

     }
  

Может ли эта проблема возникнуть из-за того, что порт все еще горячий, и мне нужно подождать, прежде чем использовать его повторно? Я пытался сменить порт, но это не помогло.

Обновление: если режим ожидания (1) закомментирован, то пакеты действительно отправляются!

Большое спасибо за вашу помощь.

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

1. Используете ли вы два сокета с одним и тем же портом? Если да, то в чем причина?

2. Возможно, SO_REUSEADDR это помогло бы. Или вы могли бы просто оставить порт открытым, а не открывать и закрывать его, или использовать новый случайно назначенный номер порта каждый раз, если это возможно.

3. Я добавил больше кода. У меня есть 2 сокета, но только один привязан к порту (для получения данных на определенном порту), другой может отправлять данные с любого порта.

4. R: Я только что пытался использовать SO_REUSEADDR, и это не помогло. Что вы подразумеваете под сохранением предполагаемого порта? Я не думаю, что он закрывается, поскольку я не вызываю close ()?

5. Я понял, что использование sleep () устраняет проблему, но я не уверен, как устранить проблему без этого.

Ответ №1:

Ошибка, которую вы получаете:

EAGAIN или EWOULDBLOCK: сокет помечен как неблокирующий, и запрошенная операция будет заблокирована. POSIX.1-2001 допускает возврат любой ошибки в этом случае и не требует, чтобы эти константы имели одинаковое значение, поэтому переносимое приложение должно проверять обе возможности.

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

Не устанавливайте для него значение неблокирующего или повторите попытку после того, select как это будет возможно.

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

1. Большое спасибо. Но O_NONBLOCK предназначен для sockfd, разве sockSend не должен блокировать (поведение по умолчанию)? Я использую sockfd для получения и sockSend для отправки данных. Я удалил O_NONBLOCK в fcntl (), и происходит то же самое поведение. Это было то, что вы упоминали, верно?

2. При удалении O_NONBLOCK, а также MSG_DONTWAIT он начинает работать! Большое вам спасибо! Есть ли какой-нибудь способ сохранить один блокирующий сокет (для отправки) и один неблокирующий (для получения), пожалуйста?!

Ответ №2:

Если вам нужно настроить сокет на неблокирующий, вы можете сделать это безопасно (и только?) с помощью select :

select() и pselect() позволяют программе отслеживать несколько файловых дескрипторов, ожидая, пока один или более файловых дескрипторов не станут «готовыми» к некоторому классу операций ввода-вывода (например, возможен ввод). Файловый дескриптор считается готовым, если возможно выполнить соответствующую операцию ввода-вывода (например, чтение(2)) без блокировки.