【问题标题】:sockaddr value changes unexpectedly after calling getaddrinfo()调用 getaddrinfo() 后 sockaddr 值意外更改
【发布时间】:2015-09-30 07:34:12
【问题描述】:

我正在编写一个 UDP 客户端。我想将套接字绑定到客户端计算机上的给定端口,以便始终将同一端口用于所有发送。我使用 getaddrinfo 获取服务器的 sockaddr,并执行相同的操作来获取传递给 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 中的套接字编程相当陌生,并且很确定我犯了一些愚蠢的错误,但是在谷歌搜索并尝试了很多事情之后,我仍然坚持这一点。任何的想法?

【问题讨论】:

  • 当然它会改变。这就是该功能应该做的事情。在获得新地址之前,您应该与旧地址绑定。或者使用两个地址结构。这肯定很明显吗?
  • 我怀疑p-&gt;ai_addr(又名destination_addr)是freeaddrinfo之后的有效指针。
  • @EJP 我使用了两个地址结构,destination_addrsource_addr_info-&gt;ai_addr。在source_addr_info 上调用getaddrinfo 应该没有理由改变destination_addr 的值。除非,正如@molbdnilo 所指出的,freeaddrinfo 释放的内存被getaddrinfo 的调用覆盖。我会按照这条路来解决问题。
  • 我真的不明白为什么这个问题被否决了。

标签: c sockets getaddrinfo sockaddr-in


【解决方案1】:

解决了。正如@molbdnilo 指出的那样,错误是由对freeaddrinfo 的调用引起的。为了修复它,我现在复制p-&gt;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);
}

它成功了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    相关资源
    最近更新 更多