希望我的客户端程序通过IPv4或IPv6连接
目前,以下程序仅使用IPv4地址进行连接。 我希望将它修改为使用服务器的任何IPv6或IPv4地址连接到服务器(兼容接受IPv4和IPv6客户端)。希望我的客户端程序通过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;
}
您需要使用支持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);
除了fluter的答案,你需要改变你的代码的结构位。
除了创建套接字,然后查找地址,您需要调用getaddrinfo
来获取给定主机名的所有地址。主机名通常是双重堆叠的,所以你会得到多个IPv4和/或IPv6地址。他们通常按您应该尝试的顺序排序。所以循环所有的地址一个接一个,直到连接成功。
由于某些地址是IPv4,有些地址是IPv6,因此无法预先创建套接字。对于您尝试的每个地址,应该创建一个属于您尝试连接的地址的地址系列的新套接字。弗莱特已经告诉你如何。
一旦连接成功,您将断开循环并使用已建立的连接。
某些平台将允许您使用IPv6套接字在两种协议上进行通信。但我不确定这样做的代码可以变得便携。然而,为每个地址创建一个单独的套接字还有其他优点,因为这样可以并行地尝试多个套接字。 – kasperd
程序中需要做什么修改? 我是否需要编写一个单独的客户端? – ramnarayanan
@ramnarayanan否,目的是使用一个客户端来支持这两个版本。 – fluter
我想分享一些错误。 @fluter – ramnarayanan