c++ - C++ 中的客户端,使用 gethostbyname 或 getaddrinfo

标签 c++ sockets

我发现下面的代码在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;
}

当我用 C++ 重写这段代码时,我发现我应该使用 getaddrinfo 而不是 gethostbyname,根据这篇文章中的评论:C - What does *(long *)(host->h_addr); do? .

我在看 Beej's guide to socket programming , 并找到以下示例:

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

但是,这个例子适用于创建服务器,这让我很奇怪

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

更新:

对于任何可能觉得它有用的人,这是我在阅读接受的答案后的最终代码:

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;
}

最佳答案

gethostbyname()gethostbyaddr() 函数在大多数平台上已被弃用,并且它们不实现对 IPv6 的支持。 IPv4 已达到极限,世界转向 IPv6 已有一段时间了。分别使用 getaddrinfo()getnameinfo()

回答您的问题:

一个。 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;
}

关于c++ - C++ 中的客户端,使用 gethostbyname 或 getaddrinfo,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52727565/

相关文章:

c++ - 如何在 iOS 应用程序中锁定代码执行

c++ - WSARecv 有时会为与 IOCP 端口关联的套接字返回 "invalid handle (error no 6)"。 (C++)

C# void ReceiveData(IAsyncResult iar)

c - 非阻塞 TCP 接受器不从套接字读取

Java/线程优先级

c++ - int 到自定义结构 c++ 的转换

java - Java中c++的goto在特定场景下的等价物

c++ - 将整数左移 32 位

c++ - 为每个连接手动分配端口号

linux - super 终端使用的TCP/IP通信协议(protocol)?