【问题标题】:Connecting a socket, using getaddrinfo, times out使用 getaddrinfo 连接套接字超时
【发布时间】:2018-11-29 15:36:32
【问题描述】:

我花了几个小时试图找出我的问题,只是在我写问题的时候找到了解决方案(当你需要正式化你的问题并解释它时,它总是有帮助的)。我贴出来了,希望对大家有帮助。

使用 getaddrinfo,如果我尝试将套接字连接到我的服务器,执行(我认为的)正是在大量网站以及 getaddrinfo 的手册页示例代码中解释的内容,它会失败并显示“连接超时”错误信息: (简化代码更简洁)

void connect_UsingGetAddrInfo_Wrong (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    x =  getaddrinfo(hostname, NULL, &hints, &addr);
    //shall rather loop on addr linked list, but this is not the topic here.

    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}

但是,我能够使用 gethostbyname 方法将套接字连接到同一台服务器。

void connect_UsingGetHostByName_Deprecated (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    struct hostent DNS, *r;
    char buf[1024];
    x = gethostbyname_r(hostname.c_str(), & DNS, buf, sizeof(buf), & r, & err));
    socketfd = socket(domain, socketType, 0);

    //server.
    sockaddr_in server;
    memset(&server, 0x00, sizeof(server));
    server.sin_family=domain;
    server.sin_port=htons(port);
    memcpy(& server.sin_addr.s_addr, DNS.h_addr, (size_t) DNS.h_length);
    x = connect(socketfd, (struct sockaddr *) & server, sizeof(server));
}

运行代码显示两个版本都正确检索了服务器的有效 IP 地址。第一个仍然无法连接并且会超时。 为什么 ?

【问题讨论】:

  • 这些变量xdomainsocketTypehostnamefd是从哪里来的?
  • "//为简洁而简化的循环和错误处理" 我还应该添加 int 和 error var 声明。由于我想发布 4 段类似的代码,我试图只保留最低限度以了解差异并保持简洁。我本可以粘贴所有在此处没有帮助的痕迹、循环和错误。那么让我修正一些声明
  • 我终于为每个 var 添加了问题和答案函数原型和类型声明(希望我没有错过一个)。

标签: c sockets getaddrinfo gethostbyname


【解决方案1】:

它一直失败的原因是:为了检索 addrinfo,我将字段“服务”设置为 NULL。它仍然会返回成功并为您提供一个地址(您可以使用 getnameinfo 将其映射到正确的 IP 地址)。该地址仍然无法连接您的套接字!

我在这里找到了两种方法的混合版本: https://codereview.stackexchange.com/questions/17863/socket-connect-realization-gethostbyname-or-getnameinfo 这个是功能性的,但我不买铸件

void connect_UsingGetAddrInfo_HYBRID (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams. 

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    x =  getaddrinfo(host, NULL, &hints, &addr);
    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);

    //here is the hybrid part
    sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = addr->ai_family;
    servAddr.sin_addr.s_addr = *((uint32_t*) & (((sockaddr_in*)addr->ai_addr)->sin_addr));
    servAddr.sin_port        = htons(port);

    x=connect(socketfd, (struct sockaddr*) &servAddr, sizeof(servAddr));
}

最后,它帮我找到了根本原因:

void connect_UsingGetAddrInfo_FIXED (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    //Precise here the port !
    const char* service = std::to_string(port).c_str();

    x =  getaddrinfo(host, service, &hints, &addr);
    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}

希望有一天这会对某人有所帮助!

【讨论】:

    猜你喜欢
    • 2011-05-11
    • 1970-01-01
    • 2012-11-17
    • 2011-02-05
    • 2018-11-26
    • 2020-06-02
    • 2016-10-02
    • 2016-02-14
    相关资源
    最近更新 更多