希望我的客户端程序连接到IPv4或IPv6

时间:2016-04-24 04:22:49

标签: c client-server ipv6 ipv4 getaddrinfo

目前,以下程序仅使用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;
            }

2 个答案:

答案 0 :(得分:2)

除了fluter的回答,您还需要稍微改变代码的结构。

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

由于某些地址是IPv4,而某些地址是IPv6,因此您无法预先创建套接字。对于您尝试的每个地址,您应该创建一个属于您尝试连接的地址的地址系列的新套接字。 Fluter已经告诉你如何。

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

答案 1 :(得分:1)

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

在所有后续网络通话中,您应使用getaddrinfo返回的系列,而不是将其硬编码到AF_INET,例如sock = socket(addr->ai_family, ...)

此外,请阅读此IPV6简介,这是一个非常好的开始。

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

对于代码中的特定更改,我认为您需要更改这3个位置才能开始:

// 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);