连接套接字时使用getaddrinfo,超时了

4
我花了好几个小时来寻找问题的解决方法,当我写问题描述并且需要说明时,终于找到了解决方案。我把解决方案发布出来,希望能够帮助到其他人。
使用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 是从哪里来的? - Tormund Giantsbane
"//简化循环和错误处理以增加简洁性。我应该还添加int和error变量声明。由于我想要发布4个类似的代码片段,我尝试只保留最少的内容以了解差异并保持简洁。我本可以粘贴带有跟踪、循环和错误的所有内容,但这对此处没有帮助。让我先修复一些声明。" - Pellekrino
1
我最终为每个变量添加了问题和答案函数原型和类型声明(希望我没有漏掉任何一个)。 - Pellekrino
1个回答

5
它一直失败的原因是:为了检索addrinfo,我将字段“service”保留为空。它仍然会返回成功并提供一个地址(您可以使用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);
}

希望这对某个人有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接