IPv6 示例程序在 connect() 上失败
IPv6 example program fails on connect()
IPv6 示例程序在 connect() 上失败
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char * es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
struct sockaddr * getadr(char * name)
{
struct addrinfo * p;
int r;
struct sockaddr_in6 * sap;
unsigned long long addrl, addrh;
printf("getadr: begin\n");
r = getaddrinfo(name, NULL, NULL, & p);
if (r) error(gai_strerror(r));
sap = NULL;
while (p && !sap) {
/* traverse the available addresses */
if (p - > ai_family == AF_INET6 && p - > ai_socktype == SOCK_STREAM) {
/* get the IPv6 address */
sap = (struct sockaddr_in6 * ) p - > ai_addr;
}
p = p - > ai_next;
}
if (!sap) error("No address found");
addrh = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[0]) << 32 |
(unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[1]);
addrl = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[2]) << 32 |
(unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[3]);
printf("Address: %llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx\n",
addrh >> 48 & 0xffff, addrh >> 32 & 0xffff, addrh >> 16 & 0xffff, addrh & 0xffff,
addrl >> 48 & 0xffff, addrl >> 32 & 0xffff, addrl >> 16 & 0xffff, addrl & 0xffff);
printf("getadr: end\n");
return ((struct sockaddr * ) sap);
}
int main(int argc, char * argv[]) {
int sockfd = 0, n = 0;
char buff[1024];
struct sockaddr_in6 serv_addr;
int r;
struct sockaddr * sap;
if (argc != 3) {
printf("Usage: socket <server> <page>\n");
exit(1);
}
if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
error("Could not create socket");
memset( & serv_addr, '0', sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(80);
printf("before address resolve\n");
if (isdigit(argv[1][0])) {
r = inet_pton(AF_INET6, argv[1], & serv_addr.sin6_addr);
if (r <= 0) error("inet_pton error occured");
} else {
sap = getadr(argv[1]);
memcpy( & serv_addr, sap, sizeof(struct sockaddr));
}
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr * ) & serv_addr, sizeof(serv_addr));
if (r < 0) error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n\r\n", "www.example.com" /*argv[1]*/ );
write(sockfd, buff, strlen(buff));
do {
r = read(sockfd, buff, sizeof(buff));
if (r > 0) {
buff[r] = 0;
printf("%s", buff);
}
} while (r);
return 0;
}
如果是数字,我安排服务器参数由 inet_pton()
评估,否则,它通过 getaddrinfo()
。 inet_pton()
设置地址并且它有效。 getaddrinfo()
不,显然,它在连接中死亡(挂断)。示例程序是一个简单的网页获取和打印(不是 https)。我使用 www.example.com 服务器进行测试。
请注意在下面的示例中 运行 我使用相同的地址 getaddrinfo()
在一个数字示例中给我,那么它工作正常。
我做错了什么?
编译就是gcc socket.c -o socket
.
$ socket6 www.example.com /
before address resolve
getadr: begin
Address: 2606:2800:220:1:248:1893:25c8:1946
getadr: end
after address resolve
^C
(hangs up in connect, CTL-C out of it)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
before address resolve
after address resolve
after connect
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
...
(prints the rest of the page)
我确实找到了一个类似的 post,他们建议将接口名称添加到服务器名称,例如 www.example.com%enp2s0,但被 getaddrinfo()
拒绝为无效。
主要问题是您没有正确填充 serv_addr
。
当调用 getadr()
时,您对 memcpy()
的调用结果没有复制足够的字节来完成 sockaddr_in6
(sizeof(sockaddr) is less than
sizeof(sockaddr_in6 )). Also, you are not asking
getaddrinfo()to output a port number, so the
sin6_port` 结果不会像您期望的那样是 80。
这就是为什么当您使用 getaddrinfo()
而不是 inet_pton()
时 connect()
失败的原因。
您的代码还有其他问题。
使用memset()
时,需要使用整数0
而不是字符'0'
。它们不是相同的值。未使用的 sockaddr_in6
字段需要正确清零。
您的 getadr()
函数会泄漏内存,并且通常效率低下。使用 getaddrinfo()
的 hints
参数来限制它输出的结果,因此您不必去寻找它们。使用完后,您需要使用 freeaddrinfo()
释放输出。
isdigit()
不是区分数字 IP 和主机名的正确方法。此外,您实际上并不需要手动执行此区分,因为 getaddrinfo()
可以解析数字 IP 字符串。但是,如果您手动区分,则无条件调用 inet_pton()
,然后在 inet_pton()
失败时调用 getaddrinfo()
。
话虽如此,请尝试更像这样的东西:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
void getadr(const char* name, struct in6_addr *addr)
{
struct addrinfo hints, *p;
int r;
struct sockaddr_in6 *sap;
char addrstr[INET6_ADDRSTRLEN];
printf("getadr: begin\n");
/*
r = inet_pton(AF_INET6, name, addr);
if (r == 1)
{
printf("getadr: end\n")
return;
}
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(name, NULL, &hints, &p);
if (r != 0)
error(gai_strerror(r));
sap = (struct sockaddr_in6*)p->ai_addr;
memcpy(addr, &(sap->sin6_addr), sizeof(*addr));
freeaddrinfo(p);
printf("Address: %s\n", inet_ntop(AF_INET6, addr, addrstr, sizeof(addrstr)));
printf("getadr: end\n");
}
int main(int argc, char *argv[])
{
int sockfd, r;
char buff[1024];
struct sockaddr_in6 serv_addr;
if (argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("Could not create socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(80);
printf("before address resolve\n");
getadr(argv[1], &(serv_addr.sin6_addr));
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
write(sockfd, buff, strlen(buff));
do
{
r = read(sockfd, buff, sizeof(buff));
if (r <= 0) break;
printf("%.*s", r, buff);
}
while (true);
close(sockfd);
return 0;
}
也就是说,getaddrinfo()
输出一个 IP 地址链表。一个主机名可以解析为多个 IP,但您的计算机可能无法访问所有 IP。您应该遍历整个列表 connect()
'ing 到每个 IP,直到其中一个成功或列表耗尽。例如:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
struct addrinfo* getadrs(const char* name, const char* port)
{
struct addrinfo hints, *p;
int r;
printf("getadr: begin\n");
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6 /*AF_UNSPEC*/;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(name, port, &hints, &p);
if (r != 0)
error(gai_strerror(r));
printf("getadr: end\n");
return p;
}
void* adrptr(struct sockaddr* addr)
{
switch (addr->sa_family)
{
case AF_INET:
return &(((struct sockaddr_in*)addr)->sin_addr);
case AF_INET6:
return &(((struct sockaddr_in6*)addr)->sin6_addr);
}
return NULL;
}
int main(int argc, char *argv[])
{
int sockfd = -1, r;
char buff[1024], addrstr[INET6_ADDRSTRLEN];
struct addrinfo *serv_addrs, *addr;
if (argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
printf("before address resolve\n");
serv_addrs = getadrs(argv[1], "80");
printf("after address resolve\n");
for(addr = serv_addrs; addr != NULL; addr = addr->ai_next)
{
sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sockfd < 0)
error("Could not create socket");
printf("Address: %s\n", inet_ntop(addr->ai_family, adrptr(addr->ai_addr), addrstr, sizeof(addrstr)));
r = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
if (r == 0) break;
close(sockfd);
sockfd = -1;
}
if (sockfd < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
write(sockfd, buff, strlen(buff));
do
{
r = read(sockfd, buff, sizeof(buff));
if (r <= 0) break;
printf("%.*s", r, buff);
}
while (true);
close(sockfd);
return 0;
}
重构代码:
/*
* Socket.c program taken from the sockets example for linux
* and refactored for IPv6.
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd = 0, n = 0;
char buff[1024];
struct sockaddr_in6 serv_addr;
int r;
struct sockaddr* sap;
struct addrinfo hints, *p;
if(argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
if((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
error("Could not create socket");
/* resolve address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(argv[1], "80", &hints, &p);
if (r != 0) error(gai_strerror(r));
memcpy(&serv_addr, (struct sockaddr_in6*)p->ai_addr, sizeof(struct sockaddr_in6));
freeaddrinfo(p);
/* connect to server */
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0) error("Connect Failed");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n\r\n", "www.example.com" /*argv[1]*/);
write(sockfd, buff, strlen(buff));
do {
r = read(sockfd, buff, sizeof(buff));
if (r > 0) {
buff[r] = 0;
printf("%s", buff);
}
} while (r);
return 0;
}
运行现在是:
$ addr www.example.com
Addresses for host: www.example.com
Address: type: AF_INET6 Socket type: SOCK_STREAM Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET6 Socket type: SOCK_DGRAM Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET6 Socket type: SOCK_RAW Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET Socket type: SOCK_STREAM Address: 93.184.216.34
Address: type: AF_INET Socket type: SOCK_DGRAM Address: 93.184.216.34
Address: type: AF_INET Socket type: SOCK_RAW Address: 93.184.216.34
$ socket6 www.example.com /
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 27 Oct 2019 17:50:10 GMT
...
(full page)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 27 Oct 2019 17:50:21 GMT
...
(full page)
我必须仔细考虑一下您所说的并非所有远程地址都有效,我当时并不知道这一点。
IPv6 示例程序在 connect() 上失败
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char * es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
struct sockaddr * getadr(char * name)
{
struct addrinfo * p;
int r;
struct sockaddr_in6 * sap;
unsigned long long addrl, addrh;
printf("getadr: begin\n");
r = getaddrinfo(name, NULL, NULL, & p);
if (r) error(gai_strerror(r));
sap = NULL;
while (p && !sap) {
/* traverse the available addresses */
if (p - > ai_family == AF_INET6 && p - > ai_socktype == SOCK_STREAM) {
/* get the IPv6 address */
sap = (struct sockaddr_in6 * ) p - > ai_addr;
}
p = p - > ai_next;
}
if (!sap) error("No address found");
addrh = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[0]) << 32 |
(unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[1]);
addrl = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[2]) << 32 |
(unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[3]);
printf("Address: %llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx\n",
addrh >> 48 & 0xffff, addrh >> 32 & 0xffff, addrh >> 16 & 0xffff, addrh & 0xffff,
addrl >> 48 & 0xffff, addrl >> 32 & 0xffff, addrl >> 16 & 0xffff, addrl & 0xffff);
printf("getadr: end\n");
return ((struct sockaddr * ) sap);
}
int main(int argc, char * argv[]) {
int sockfd = 0, n = 0;
char buff[1024];
struct sockaddr_in6 serv_addr;
int r;
struct sockaddr * sap;
if (argc != 3) {
printf("Usage: socket <server> <page>\n");
exit(1);
}
if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
error("Could not create socket");
memset( & serv_addr, '0', sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(80);
printf("before address resolve\n");
if (isdigit(argv[1][0])) {
r = inet_pton(AF_INET6, argv[1], & serv_addr.sin6_addr);
if (r <= 0) error("inet_pton error occured");
} else {
sap = getadr(argv[1]);
memcpy( & serv_addr, sap, sizeof(struct sockaddr));
}
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr * ) & serv_addr, sizeof(serv_addr));
if (r < 0) error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n\r\n", "www.example.com" /*argv[1]*/ );
write(sockfd, buff, strlen(buff));
do {
r = read(sockfd, buff, sizeof(buff));
if (r > 0) {
buff[r] = 0;
printf("%s", buff);
}
} while (r);
return 0;
}
如果是数字,我安排服务器参数由 inet_pton()
评估,否则,它通过 getaddrinfo()
。 inet_pton()
设置地址并且它有效。 getaddrinfo()
不,显然,它在连接中死亡(挂断)。示例程序是一个简单的网页获取和打印(不是 https)。我使用 www.example.com 服务器进行测试。
请注意在下面的示例中 运行 我使用相同的地址 getaddrinfo()
在一个数字示例中给我,那么它工作正常。
我做错了什么?
编译就是gcc socket.c -o socket
.
$ socket6 www.example.com /
before address resolve
getadr: begin
Address: 2606:2800:220:1:248:1893:25c8:1946
getadr: end
after address resolve
^C
(hangs up in connect, CTL-C out of it)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
before address resolve
after address resolve
after connect
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
...
(prints the rest of the page)
我确实找到了一个类似的 post,他们建议将接口名称添加到服务器名称,例如 www.example.com%enp2s0,但被 getaddrinfo()
拒绝为无效。
主要问题是您没有正确填充 serv_addr
。
当调用 getadr()
时,您对 memcpy()
的调用结果没有复制足够的字节来完成 sockaddr_in6
(sizeof(sockaddr) is less than
sizeof(sockaddr_in6 )). Also, you are not asking
getaddrinfo()to output a port number, so the
sin6_port` 结果不会像您期望的那样是 80。
这就是为什么当您使用 getaddrinfo()
而不是 inet_pton()
时 connect()
失败的原因。
您的代码还有其他问题。
使用memset()
时,需要使用整数0
而不是字符'0'
。它们不是相同的值。未使用的 sockaddr_in6
字段需要正确清零。
您的 getadr()
函数会泄漏内存,并且通常效率低下。使用 getaddrinfo()
的 hints
参数来限制它输出的结果,因此您不必去寻找它们。使用完后,您需要使用 freeaddrinfo()
释放输出。
isdigit()
不是区分数字 IP 和主机名的正确方法。此外,您实际上并不需要手动执行此区分,因为 getaddrinfo()
可以解析数字 IP 字符串。但是,如果您手动区分,则无条件调用 inet_pton()
,然后在 inet_pton()
失败时调用 getaddrinfo()
。
话虽如此,请尝试更像这样的东西:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
void getadr(const char* name, struct in6_addr *addr)
{
struct addrinfo hints, *p;
int r;
struct sockaddr_in6 *sap;
char addrstr[INET6_ADDRSTRLEN];
printf("getadr: begin\n");
/*
r = inet_pton(AF_INET6, name, addr);
if (r == 1)
{
printf("getadr: end\n")
return;
}
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(name, NULL, &hints, &p);
if (r != 0)
error(gai_strerror(r));
sap = (struct sockaddr_in6*)p->ai_addr;
memcpy(addr, &(sap->sin6_addr), sizeof(*addr));
freeaddrinfo(p);
printf("Address: %s\n", inet_ntop(AF_INET6, addr, addrstr, sizeof(addrstr)));
printf("getadr: end\n");
}
int main(int argc, char *argv[])
{
int sockfd, r;
char buff[1024];
struct sockaddr_in6 serv_addr;
if (argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("Could not create socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(80);
printf("before address resolve\n");
getadr(argv[1], &(serv_addr.sin6_addr));
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
write(sockfd, buff, strlen(buff));
do
{
r = read(sockfd, buff, sizeof(buff));
if (r <= 0) break;
printf("%.*s", r, buff);
}
while (true);
close(sockfd);
return 0;
}
也就是说,getaddrinfo()
输出一个 IP 地址链表。一个主机名可以解析为多个 IP,但您的计算机可能无法访问所有 IP。您应该遍历整个列表 connect()
'ing 到每个 IP,直到其中一个成功或列表耗尽。例如:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
struct addrinfo* getadrs(const char* name, const char* port)
{
struct addrinfo hints, *p;
int r;
printf("getadr: begin\n");
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6 /*AF_UNSPEC*/;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(name, port, &hints, &p);
if (r != 0)
error(gai_strerror(r));
printf("getadr: end\n");
return p;
}
void* adrptr(struct sockaddr* addr)
{
switch (addr->sa_family)
{
case AF_INET:
return &(((struct sockaddr_in*)addr)->sin_addr);
case AF_INET6:
return &(((struct sockaddr_in6*)addr)->sin6_addr);
}
return NULL;
}
int main(int argc, char *argv[])
{
int sockfd = -1, r;
char buff[1024], addrstr[INET6_ADDRSTRLEN];
struct addrinfo *serv_addrs, *addr;
if (argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
printf("before address resolve\n");
serv_addrs = getadrs(argv[1], "80");
printf("after address resolve\n");
for(addr = serv_addrs; addr != NULL; addr = addr->ai_next)
{
sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sockfd < 0)
error("Could not create socket");
printf("Address: %s\n", inet_ntop(addr->ai_family, adrptr(addr->ai_addr), addrstr, sizeof(addrstr)));
r = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
if (r == 0) break;
close(sockfd);
sockfd = -1;
}
if (sockfd < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
write(sockfd, buff, strlen(buff));
do
{
r = read(sockfd, buff, sizeof(buff));
if (r <= 0) break;
printf("%.*s", r, buff);
}
while (true);
close(sockfd);
return 0;
}
重构代码:
/*
* Socket.c program taken from the sockets example for linux
* and refactored for IPv6.
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>
void error(const char* es)
{
fprintf(stderr, "Error: %s\n", es);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd = 0, n = 0;
char buff[1024];
struct sockaddr_in6 serv_addr;
int r;
struct sockaddr* sap;
struct addrinfo hints, *p;
if(argc != 3)
{
printf("Usage: socket <server> <page>\n");
exit(1);
}
if((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
error("Could not create socket");
/* resolve address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
r = getaddrinfo(argv[1], "80", &hints, &p);
if (r != 0) error(gai_strerror(r));
memcpy(&serv_addr, (struct sockaddr_in6*)p->ai_addr, sizeof(struct sockaddr_in6));
freeaddrinfo(p);
/* connect to server */
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0) error("Connect Failed");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n\r\n", "www.example.com" /*argv[1]*/);
write(sockfd, buff, strlen(buff));
do {
r = read(sockfd, buff, sizeof(buff));
if (r > 0) {
buff[r] = 0;
printf("%s", buff);
}
} while (r);
return 0;
}
运行现在是:
$ addr www.example.com
Addresses for host: www.example.com
Address: type: AF_INET6 Socket type: SOCK_STREAM Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET6 Socket type: SOCK_DGRAM Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET6 Socket type: SOCK_RAW Address: 2606:2800:220:1:248:1893:25c8:1946
Address: type: AF_INET Socket type: SOCK_STREAM Address: 93.184.216.34
Address: type: AF_INET Socket type: SOCK_DGRAM Address: 93.184.216.34
Address: type: AF_INET Socket type: SOCK_RAW Address: 93.184.216.34
$ socket6 www.example.com /
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 27 Oct 2019 17:50:10 GMT
...
(full page)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 27 Oct 2019 17:50:21 GMT
...
(full page)
我必须仔细考虑一下您所说的并非所有远程地址都有效,我当时并不知道这一点。