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