2015-09-30 12 views
-1

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

Следующий код представляет собой автономный пример, который воспроизводит ошибку:

#include <stdio.h> 
#include <stdint.h> 
#include <unistd.h> 
#include <string.h> 
#include <stdlib.h> 
#include <time.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 

#define SERVER_HOST "www.google.com" 
#define UDP_PORT "4000" 

static struct sockaddr_in *destination_addr = NULL; 
static int client_port; 

int main(){ 

    uint8_t bytes[5] = { 0xaa, 0xab, 0xac, 0xad, 0xaf}; //some data to send 

    uint16_t length = 5; 
    int status; 


    //initialize socket and bind 
    if (destination_addr == NULL) { 
     struct addrinfo hints; 
     struct addrinfo *servinfo, *p; 
     srand(time(NULL)); 
     memset(&hints, 0, sizeof hints); 
     hints.ai_family = AF_INET; 
     hints.ai_socktype = SOCK_DGRAM; 
     hints.ai_flags = AI_PASSIVE; 

     if ((status = getaddrinfo(SERVER_HOST, UDP_PORT, &hints, &servinfo)) != 0) { 
      printf("Unable to send UDP. Reason: %s", gai_strerror(status)); 
      return 0; 
     } 


     for (p = servinfo; p != NULL; p = p->ai_next) { 
      if (p->ai_addr != NULL) 
       destination_addr = (struct sockaddr_in *) p->ai_addr; 
     } 
     client_port = 1027 + rand()%50000; 
     freeaddrinfo(servinfo); 
     printf("Created destination_addr with IP %s\n", inet_ntoa(destination_addr->sin_addr)); 
    } 


    int send_socket_fd = socket(AF_INET, SOCK_DGRAM, 0); 
    if (send_socket_fd == -1) { 
     printf("Unable to create UDP socket. Reason: %s", strerror(errno)); 
     return 0; 
    } 
    printf("IP after socket creation is %s\n", inet_ntoa(destination_addr->sin_addr)); 
    int yes = 1; 
    if (setsockopt(send_socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1) { 
     perror("setsockopt"); 
     return 0; 
    } 
    printf("IP after sockopt is %s\n", inet_ntoa(destination_addr->sin_addr)); 

    // bind to local address 
    char str_client_port[6]; 
    snprintf(str_client_port, 5, "%d", client_port); 
    struct addrinfo *source_addr_info; 
    struct addrinfo hints; 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_DGRAM; 


    // ***** destination_addr changes after this call ***** 
    getaddrinfo (NULL, str_client_port, &hints, &source_addr_info); 


    printf("IP after getaddrinfo is %s\n", inet_ntoa(destination_addr->sin_addr)); 
    bind(send_socket_fd, source_addr_info->ai_addr, source_addr_info->ai_addrlen); 
    printf("IP after binding is %s\n", inet_ntoa(destination_addr->sin_addr)); 

    // send 
    int bytes_sent = sendto(send_socket_fd, bytes, length, 0, (struct sockaddr *)destination_addr, sizeof *destination_addr); 
    printf("Sent to IP %s\n", inet_ntoa(destination_addr->sin_addr)); 
    if (bytes_sent != length){ 
     if (bytes_sent == -1){ 
      printf("UDP send failed. Reason: %s", strerror(errno)); 
     } 
     else { 
      printf("UDP: not all bytes could be sent."); 
     } 
    } 


    close(send_socket_fd); 
    return 1; 

} 

Выход генерируется посредством выполнения этой программы в моей машине:

Created destination_addr with IP 64.233.167.105 
IP after socket creation is 64.233.167.105 
IP after sockopt is 64.233.167.105 
IP after getaddrinfo is 0.0.0.0 
IP after binding is 0.0.0.0 
Sent to IP 0.0.0.0 

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

+0

Ну, конечно, это меняется. Это то, что функция должна делать. Перед тем, как вы получите новый адрес, вы должны связать его со старым адресом. Или используйте две структуры адресов. Неужели это очевидно? – EJP

+1

Я сомневаюсь, что 'p-> ai_addr' (a.k.a.' destination_addr') является допустимым указателем после 'freeaddrinfo'. – molbdnilo

+0

@EJP Я использую две структуры адресов: 'destination_addr' и' source_addr_info-> ai_addr'. Не должно быть причин, по которым вызов 'getaddrinfo' в' source_addr_info' должен изменить значение 'destination_addr'. Если, как указывает @molbdnilo, память, освобожденная 'freeaddrinfo', перезаписывается вызовом' getaddrinfo'. Я буду следовать этому пути для решения проблемы. – joanlofe

ответ

0

Решено. Как отметил @molbdnilo, ошибка была вызвана вызовом freeaddrinfo. Чтобы исправить это, я теперь копирую значение, указанное p->ai_addr, чтобы оно не терялось при освобождении. Я заменял:

if (p->ai_addr != NULL) 
    destination_addr = (struct sockaddr_in *) p->ai_addr; 

с

if (p->ai_addr != NULL){ 
    destination_addr = malloc(sizeof *destination_addr); 
    memcpy(destination_addr, (struct sockaddr_in *)p->ai_addr, sizeof *p->ai_addr); 
} 

, и она работала.

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

  • Нет связанных вопросов^_^