Проблемы при использовании SOCK_DGRAM для получения информации с сервера - PullRequest
0 голосов
/ 09 декабря 2010

У меня есть следующая программа (сокеты DGRAM) для получения времени с сервера времени.Программа не распечатывает текущее время, как ожидалось.

#define DAYTIME_SERVER_PORT  13

int main(int argc, char *argv[])
{

    int connectionFd, in;
    struct sockaddr_in servaddr;
    char buf[BUFSIZ];

    connectionFd = socket(AF_INET, SOCK_DGRAM, 0);
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(DAYTIME_SERVER_PORT);
    inet_pton(AF_INET,"time.mit.edu", &servaddr.sin_addr);

    sendto( connectionFd,buf ,1, 0,(struct sockaddr_in *)&servaddr, sizeof(servaddr) );

    in = recv(connectionFd, buf, BUFSIZ, 0 ) ; 
    buf[in] = 0 ; 
    printf("Time is %s \n",buf);

    close(connectionFd);


}

Мои вопросы - Где я делаю ошибку?Любые изменения кода и ценные предложения приветствуются.Я не уверен в том, как использовать функцию recvform, и, следовательно, попробовал recv.Дневной сервер, к которому я подключаюсь, time.mit.edu

Ответы [ 3 ]

9 голосов
/ 09 декабря 2010

inet_pton() не ищет имена хостов, такие как time.mit.edu.Это просто для преобразования строковой формы IP-адреса, такого как "18.7.21.144", в адрес сокета.

Вы должны использовать getaddrinfo(), чтобы найти имя, подобное "time.mit.edu".Он также может выполнять поиск номера порта daytime.Ваш код будет выглядеть так:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
    struct addrinfo *addr;
    struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM };
    int err;
    int connectionFd, in;
    char buf[1024];

    err = getaddrinfo("time.mit.edu", "daytime", &hints, &addr);
    if (err) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
        return 1;
    }

    connectionFd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);

    sendto(connectionFd, "." , 1, 0, addr->ai_addr, addr->ai_addrlen );

    freeaddrinfo(addr);

    in = recv(connectionFd, buf, sizeof buf - 1, 0 ) ;
    buf[in] = 0 ;
    printf("Time is %s \n",buf);

    close(connectionFd);
    return 0;
}

(Смотри, Ма: дневное время с поддержкой IPv6;)

Конечно, вы должны также проверять возвращаемое значение socket(), sendto() и recv(), так же, как я сделал для getaddrinfo().В частности, рассмотрим, что произойдет, если recv() завершится неудачно и вернет -1 ...

2 голосов
/ 09 декабря 2010

Вы не проверяете ни возвращаемое значение функций socket(2), sendto(2), ни recv(2), которые возвращают -1 при ошибка и установите глобальную errno(3) переменную. Возьмите в привычку всегда проверять, какие системные вызовы возвращаются.

Вы указываете recv() BUFSIZ как доступное пространство, а затем записываете в байт один после полученной длины. На самом деле это может не быть проблемой, если BUFSIZ действительно велико (больше, чем любая дейтаграмма UDP, т.е. 2^16), но вы можете переполнить буфер, если получите полные данные buf.

Что касается реальной проблемы в вашем коде - @caf уже ответил на это.

0 голосов
/ 09 декабря 2010

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

  • Какое значение возвращается из sendto? Это может сказать вам, по крайней мере, если вы подключаетесь ОК.

  • Убедитесь, что ваш адрес правильный. Возможно, было бы проще жестко закодировать IP-адрес time.mit.edu непосредственно в servaddr.sin_addr, чтобы устранить любую вероятность того, что inet_pton вызывает проблему.

  • Какое значение имеет BUFSIZ? Обычно он установлен в stdio.h, но вы уверены, что он не равен нулю?

  • Вы не устанавливаете протокол явно в IPPROTO_UDP. Обычно это нормально, потому что ваша библиотека сокетов выберет соответствующий протокол, когда вы установите протокол = 0. Используйте getsockopt (SO_PROTOCOL_INFO), чтобы проверить его правильность (или установить его явно при вызове socket ()).

  • Ваш брандмауэр мешает? Попробуйте telneting к time.mit.edu:13. Он должен распечатать время и затем закрыть соединение.

О recvfrom ... он такой же, как и recv, за исключением того, что он захватывает адрес узла-партнера. Эта разница здесь несущественна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...