我发现以下代码在C中打开一个连接:
int OpenConnection(const char *hostname, int port)
{
    int sd;
    struct hostent *host;
    struct sockaddr_in addr = {0};
    if ((host = gethostbyname(hostname)) == NULL)
    {
        perror(hostname);
        abort();
    }
    sd = socket(PF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = *(long *)(host->h_addr_list[0]);
    if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
    {
        close(sd);
        perror(hostname);
        abort();
    }
    return sd;
}
Run Code Online (Sandbox Code Playgroud)
当我用C++重写这段代码时,我发现我应该使用getaddrinfo而不是gethostbyname,根据这篇文章中的评论:C  - 什么是*(long*)(host-> h_addr); 做?.
我正在查看Beej的套接字编程指南,并找到了以下示例:
int status;
struct addrinfo hints;
struct addrinfo *servinfo;  // will point to the results
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me
if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    exit(1);
}
// servinfo now points to a linked list of 1 or more struct addrinfos
// ... do everything until you don't need servinfo anymore ....
freeaddrinfo(servinfo); // free the linked-list
Run Code Online (Sandbox Code Playgroud)
但是,此示例适用于创建服务器,这让我很奇怪
A] getaddrinfo应该用于客户端
和
B]如何修改提示结构和函数参数以使此代码适合创建客户端?
目前,我有以下代码,但我
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *client_info;
const int status = getaddrinfo(hostname, port, &hints, &client_info); //don't know if its correct
Run Code Online (Sandbox Code Playgroud)
更新:
对于任何可能发现它有用的人,在阅读接受的答案后,这是我的最终代码:
int OpenConnection(const char *hostname, const char *port)
{
    struct hostent *host;
    if ((host = gethostbyname(hostname)) == nullptr)
    {
        //More descriptive error message?
        perror(hostname);
        exit(EXIT_FAILURE);
    }
    struct addrinfo hints = {0}, *addrs;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    const int status = getaddrinfo(hostname, port, &hints, &addrs);
    if (status != 0)
    {
        fprintf(stderr, "%s: %s\n", hostname, gai_strerror(status));
        exit(EXIT_FAILURE);
    }
    int sfd, err;
    for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next)
    {
        sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
        if (sfd == ERROR_STATUS)
        {
            err = errno;
            continue;
        }
        if (connect(sfd, addr->ai_addr, addr->ai_addrlen) == 0)
        {
            break;
        }
        err = errno;
        sfd = ERROR_STATUS;
        close(sfd);
    }
    freeaddrinfo(addrs);
    if (sfd == ERROR_STATUS)
    {
        fprintf(stderr, "%s: %s\n", hostname, strerror(err));
        exit(EXIT_FAILURE);
    }
    return sfd;
}
Run Code Online (Sandbox Code Playgroud)
    这些gethostbyname()和gethostbyaddr()函数在大多数平台上都已弃用,并且它们不实现对IPv6的支持.IPv4已达到极限,现在世界已经转向IPv6一段时间了.分别使用getaddrinfo()和getnameinfo()代替.
回答你的问题:
A. getaddrinfo()并且getnameinfo()可用于客户端和服务器一样,就像gethostbyname()和gethostbyaddr()即可.它们只是主机/地址解析功能,如何使用已解析的值取决于调用应用程序来决定.
B.客户端代码使用getaddrinfo()看起来像这样:
int OpenConnection(const char *hostname, int port)
{
    int sd, err;
    struct addrinfo hints = {}, *addrs;
    char port_str[16] = {};
    hints.ai_family = AF_INET; // Since your original code was using sockaddr_in and
                               // PF_INET, I'm using AF_INET here to match.  Use
                               // AF_UNSPEC instead if you want to allow getaddrinfo()
                               // to find both IPv4 and IPv6 addresses for the hostname.
                               // Just make sure the rest of your code is equally family-
                               // agnostic when dealing with the IP addresses associated
                               // with this connection. For instance, make sure any uses
                               // of sockaddr_in are changed to sockaddr_storage,
                               // and pay attention to its ss_family field, etc...
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    sprintf(port_str, "%d", port);
    err = getaddrinfo(hostname, port_str, &hints, &addrs);
    if (err != 0)
    {
        fprintf(stderr, "%s: %s\n", hostname, gai_strerror(err));
        abort();
    }
    for(struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next)
    {
        sd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (sd == -1)
        {
            err = errno;
            break; // if using AF_UNSPEC above instead of AF_INET/6 specifically,
                   // replace this 'break' with 'continue' instead, as the 'ai_family'
                   // may be different on the next iteration...
        }
        if (connect(sd, addr->ai_addr, addr->ai_addrlen) == 0)
            break;
        err = errno;
        close(sd);
        sd = -1;
    }
    freeaddrinfo(addrs);
    if (sd == -1)
    {
        fprintf(stderr, "%s: %s\n", hostname, strerror(err));
        abort();
    }
    return sd;
}
Run Code Online (Sandbox Code Playgroud)
        我总是使用gethostbyname(),因为"永远".它始终有效,它继续工作,而且"更简单".
getaddrinfo()是更新的函数:
http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
getaddrinfo()函数将gethostbyname(3)和getservbyname(3)函数提供的功能组合到一个接口中,但与后面的函数不同,getaddrinfo()是可重入的,并允许程序消除IPv4与IPv6的依赖关系.
我知道getaddrinfo()更强大,更高效,更安全:你不应该使用gethostbyname()
附录:
在回答您的具体问题时:
A] getaddrinfo()优先gethostbyname()于查找主机名的IP地址; "客户"或"服务器".
B]问:我如何修改提示结构和函数参数?
答:"提示"看起来不错,但我可能会将端口修改为NULL.
这是一个完整的例子:
https://www.kutukupret.com/2009/09/28/gethostbyname-vs-getaddrinfo/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
    struct addrinfo hints, *res, *p;
    int status;
    char ipstr[INET6_ADDRSTRLEN];
    if (argc != 2) {
       fprintf(stderr, "Usage: %s hostname\n", argv[0]);
       return 1;
    }
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
    hints.ai_socktype = SOCK_STREAM;
    if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        return 2;
    }
    for(p = res;p != NULL; p = p->ai_next) {
        void *addr;
        if (p->ai_family == AF_INET) {
            return 1;  
        } else {
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr = &(ipv6->sin6_addr);
            /* convert the IP to a string and print it: */
            inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);             
            printf("Hostname: %s\n", argv[1]);
            printf("IP Address: %s\n", ipstr);
        }
    }
    freeaddrinfo(res); // free the linked list     
    return 0;
}
Run Code Online (Sandbox Code Playgroud)