【问题标题】:Want my client program to connect to over IPv4 or IPv6希望我的客户端程序通过 IPv4 或 IPv6 连接
【发布时间】:2016-04-24 04:22:49
【问题描述】:

目前,以下程序仅使用 IPv4 地址进行连接。 我希望修改它以使用服务器的任何 IPv6 或 IPv4 地址连接到服务器(兼容接受 IPv4 和 IPv6 客户端)。

            #include <stdio.h>
            #include <stdlib.h>
            #include <string.h>
            #include <sys/types.h>
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>

            #include "common.h"
            #include "client.h"



            int
            CreateClientTCP(const char *svrHost,   
                            unsigned short svrPort,
                            char *svrName, 
                            int svrNameLen)
            {
                int sock;
                struct sockaddr_in svrAddr;

                sock = socket(AF_INET, SOCK_STREAM, 0);
                if (sock < 0) {
                    perror("Failed to allocate the client socket");
                    exit(EXIT_FAILURE);
                }

                memset(&svrAddr, 0, sizeof(svrAddr));
                svrAddr.sin_family = AF_INET;
                svrAddr.sin_port   = htons(svrPort);

                if (inet_pton(AF_INET, svrHost, &svrAddr.sin_addr.s_addr) <= 0) {
                    perror("Failed to convert IP address\n");
                    exit(EXIT_FAILURE);
                }

                SocketAddrToString(&svrAddr, svrName, svrNameLen);
                Log("Attempting %s\n", svrName);

                if (connect(sock, (struct sockaddr *)&svrAddr, sizeof(svrAddr)) < 0) {
                    perror("Failed to connect to the server");
                    exit(EXIT_FAILURE);
                }

                return sock;
            }



            int
            main(int argc, char *argv[])
            {
                int sock;
                ClientArgs cliArgs;
                char svrName[INET_ADDRSTRLEN + PORT_STRLEN];

                ParseArgs(argc, argv, &cliArgs);

                sock = CreateClientTCP(cliArgs.svrHost, cliArgs.svrPort,
                                       svrName, sizeof svrName);

                Log("Connected to server at %s\n", svrName);

                Client(sock, &cliArgs);

                close(sock);
                Log("\nDisconnected from server at %s\n", svrName);
                return 0;
            }

【问题讨论】:

    标签: c client-server ipv6 ipv4 getaddrinfo


    【解决方案1】:

    除了颤振的答案,您还需要稍微更改代码的结构。

    您需要调用getaddrinfo 来获取给定主机名的所有地址,而不是创建一个套接字然后查找地址。主机名通常是双重堆叠的,因此您将获得多个 IPv4 和/或 IPv6 地址。它们通常按照您应该尝试的顺序排序。所以将所有地址一一循环,直到连接成功。

    因为有些地址是 IPv4,有些是 IPv6,所以您不能预先创建套接字。对于您尝试的每个地址,您应该创建一个属于您尝试连接的地址的地址系列的新套接字。 Fluter 已经向您展示了如何操作。

    一旦你的连接成功,你就打破循环并使用已建立的连接。

    【讨论】:

    • 某些平台允许您使用 IPv6 套接字在两种协议上进行通信。但我不确定这样做的代码是否可以移植。然而,为每个地址创建一个单独的套接字还有其他优点,因为这将允许并行尝试多个。
    【解决方案2】:

    您将需要使用同时支持 IPV6 和 IPV4 的函数,例如,不要使用inet_pton,而是使用getaddrinfo,它可以解析两个协议版本的地址,并告诉您正确的家庭使用。

    在所有后续网络调用中,您应该使用getaddrinfo 返回的族,而不是将其硬编码为AF_INET,例如sock = socket(addr-&gt;ai_family, ...).

    另外,阅读这个 IPV6 介绍,这是一个很好的开始。

    https://www.akkadia.org/drepper/userapi-ipv6.html

    对于代码中的特定更改,我认为您需要首先更改这三个地方:

    // struct sockaddr_in svrAddr;  <-- sockaddr_in is for ipv4 address
    struct sockaddr_storage svrAddr;
    
    // inet_pton(AF_INET, svrHost, &svrAddr.sin_addr.s_addr) <-- IPV4 only
    struct addrinfo *res;
    getaddrinfo(svrHost, NULL, &hint, &res);
    
    // sock = socket(AF_INET, SOCK_STREAM, 0); <-- IPV4 only
    sock = socket(result->ai_family, SOCK_STREAM, 0);
    

    【讨论】:

    • 程序中需要进行哪些更改?我必须编写一个单独的客户端吗?
    • @ramnarayanan 不,目的是使用一个客户端来支持两个版本。
    • 我想分享一系列错误。 @fluter
    猜你喜欢
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 2017-10-21
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多