如何解决connect()失败错误:Address family not supported by protocol family
问题与解决方案
问题详情
执行终端命令 ./client www.google.com 80 时触发错误:
connect() failed: Address family not supported by protocol family
怀疑问题出在getAddressInfo函数中,附上相关C语言代码:
#include <stdio.h> /* for printf() and fprintf() */ #include <sys/socket.h> /* for socket(), connect(), send(), and recv() */ #include <arpa/inet.h> /* for sockaddr_in and inet_addr() */ #include <stdlib.h> /* for atoi() and exit() */ #include <string.h> /* for memset() */ #include <unistd.h> /* for close() */ #include <netdb.h> #include <sys/time.h> #define RCVBUFSIZE 32 /* Size of receive buffer */ void DieWithError(char *errorMessage); /* Error handling function */ int getAddressInfo(const char* host, unsigned short* port) { struct addrinfo hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; struct addrinfo* result; int ret = getaddrinfo(host, "http", &hints, &result); if (ret != 0) { fprintf(stderr, "getaddrinfo() failed: %s\n", gai_strerror(ret)); return -1; } struct sockaddr_in* sock_addr = (struct sockaddr_in*)result->ai_addr; char* servIP = inet_ntoa(sock_addr->sin_addr); *port = ntohs(sock_addr->sin_port); freeaddrinfo(result); return 0; } int main(int argc, char *argv[]) { int sock; /* Socket descriptor */ struct sockaddr_in httpServAddr; /* HTTP server address */ unsigned short httpServPort; /* HTTP server port */ /* Server IP address (dotted quad) */ char *httpRequest = "GET /index.html HTTP/1.1\r\n" "Host: www.example.com\r\n" "Connection: close\r\n\r\n"; /* HTTP request to send to the server */ char httpBuffer[RCVBUFSIZE]; /* Buffer for HTTP response */ int bytesRcvd, totalBytesRcvd; /* Bytes read in single recv() and total bytes read */ if (argc < 2 || argc > 3) { /* Test for correct number of arguments */ fprintf(stderr, "Usage: %s <Server IP> [<HTTP Port>]\n", argv[0]); exit(1); } const char* host = argv[1]; if (getAddressInfo(host, &httpServPort) != 0) { DieWithError("Failed to get server address information"); } if (argc == 3) { httpServPort = atoi(argv[2]); /* Use given port, if any */ } else { httpServPort = 80; /* Default HTTP port */ } /* Create a reliable, stream socket using TCP */ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { DieWithError("socket() failed"); } /* Construct the server address structure */ memset(&httpServAddr, 0, sizeof(httpServAddr)); /* Zero out structure */ httpServAddr.sin_family = AF_INET; /* Internet address family */ httpServAddr.sin_addr.s_addr = inet_addr(host); /* Server IP address */ httpServAddr.sin_port = htons(httpServPort); /* Server port */ /* Establish the connection to the HTTP server */ if (connect(sock, (struct sockaddr *) &httpServAddr, sizeof(httpServAddr)) < 0) { DieWithError("connect() failed"); } /* Send the HTTP request to the server */ if (send(sock, httpRequest, strlen(httpRequest), 0) != strlen(httpRequest)) { DieWithError("send() sent a different number of bytes than expected"); } /* Receive the HTTP response from the server */ totalBytesRcvd = 0; printf("Received: "); /* Setup to print the HTTP response */ while ((bytesRcvd = recv(sock, httpBuffer, RCVBUFSIZE - 1, 0)) > 0) { totalBytesRcvd += bytesRcvd; httpBuffer[bytesRcvd] = '\0'; printf("%s", httpBuffer); } if (bytesRcvd < 0) { DieWithError("recv() failed"); } printf("\n"); /* Print a final linefeed */ close(sock); exit(0); } /* void calcRTT(char* host, char* servport, int trials) { struct addrinfo* servinfo = buildAddrInfo(url, port); int i; float sum = 0; for(i=0; i < trials; i++) { struct timeval start; struct timeval end; } } long timediff(struct timeval t1, struct timeval t2) { long elapsed = t2.tv_sec - t1.tv_sec; return elapsed * 1000; } */ void DieWithError(char *errorMessage) { perror(errorMessage); exit(1); }
同时希望实现终端输入URL的信息显示功能。
错误原因分析
inet_addr无法解析域名:inet_addr(host)仅能处理点分十进制格式的IP地址,无法解析www.google.com这类域名,调用后会返回INADDR_NONE,导致sockaddr_in结构中的IP地址无效,最终触发connect错误。getAddressInfo功能被浪费:该函数调用getaddrinfo成功解析了域名和端口,但后续代码完全未使用解析出的IP地址,反而用inet_addr重新处理原域名,且后面的代码还覆盖了该函数获取到的端口值。
修复方案与功能实现
以下是修改后的代码,同时实现了URL解析信息显示功能:
#include <stdio.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/time.h> #define RCVBUFSIZE 1024 /* 增大接收缓冲区,避免截断响应 */ void DieWithError(char *errorMessage); // 解析地址并返回addrinfo链表,同时打印解析信息 struct addrinfo* getAddressInfo(const char* host, const char* port_str) { struct addrinfo hints = {0}; hints.ai_family = AF_UNSPEC; // 同时支持IPv4和IPv6 hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo* result; int ret = getaddrinfo(host, port_str, &hints, &result); if (ret != 0) { fprintf(stderr, "getaddrinfo() failed: %s\n", gai_strerror(ret)); return NULL; } // 打印解析到的地址信息 printf("=== 解析URL信息 ===\n"); printf("目标主机: %s\n", host); printf("目标端口: %s\n", port_str); struct addrinfo* ptr; char ip_str[INET6_ADDRSTRLEN]; for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { void* addr; char* ip_version; if (ptr->ai_family == AF_INET) { struct sockaddr_in* ipv4 = (struct sockaddr_in*)ptr->ai_addr; addr = &(ipv4->sin_addr); ip_version = "IPv4"; } else { struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)ptr->ai_addr; addr = &(ipv6->sin6_addr); ip_version = "IPv6"; } inet_ntop(ptr->ai_family, addr, ip_str, sizeof(ip_str)); printf("解析到%s地址: %s\n", ip_version, ip_str); } printf("===================\n\n"); return result; } int main(int argc, char *argv[]) { int sock; struct addrinfo* servinfo; char *httpRequest; char httpBuffer[RCVBUFSIZE]; int bytesRcvd, totalBytesRcvd; if (argc < 2 || argc > 3) { fprintf(stderr, "Usage: %s <Server Hostname/IP> [<HTTP Port>]\n", argv[0]); exit(1); } const char* host = argv[1]; const char* port_str = (argc == 3) ? argv[2] : "80"; // 解析地址并获取addrinfo servinfo = getAddressInfo(host, port_str); if (servinfo == NULL) { DieWithError("Failed to get server address information"); } // 遍历addrinfo链表,尝试创建socket并连接 struct addrinfo* ptr; for (ptr = servinfo; ptr != NULL; ptr = ptr->ai_next) { if ((sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) < 0) { perror("socket() failed"); continue; } if (connect(sock, ptr->ai_addr, ptr->ai_addrlen) == 0) { break; // 连接成功,跳出循环 } close(sock); perror("connect() failed"); } if (ptr == NULL) { fprintf(stderr, "Failed to connect to any address\n"); freeaddrinfo(servinfo); exit(1); } // 动态生成HTTP请求,替换Host字段为输入的主机名 char request_template[] = "GET /index.html HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n"; httpRequest = malloc(strlen(request_template) + strlen(host) - 2); // 减去%s的长度 if (httpRequest == NULL) { DieWithError("malloc() failed"); } sprintf(httpRequest, request_template, host); // 发送HTTP请求 if (send(sock, httpRequest, strlen(httpRequest), 0) != strlen(httpRequest)) { DieWithError("send() sent unexpected number of bytes"); } // 接收并打印响应 totalBytesRcvd = 0; printf("=== 服务器响应 ===\n"); while ((bytesRcvd = recv(sock, httpBuffer, RCVBUFSIZE - 1, 0)) > 0) { totalBytesRcvd += bytesRcvd; httpBuffer[bytesRcvd] = '\0'; printf("%s", httpBuffer); } if (bytesRcvd < 0) { DieWithError("recv() failed"); } printf("\n=== 接收完成 ===\n"); printf("总接收字节数: %d\n", totalBytesRcvd); free(httpRequest); close(sock); freeaddrinfo(servinfo); exit(0); } void DieWithError(char *errorMessage) { perror(errorMessage); exit(1); }
关键修改点
- 修复连接逻辑:直接使用
getaddrinfo返回的addrinfo结构创建socket和发起连接,不再手动构造sockaddr_in,同时支持IPv4和IPv6。 - 实现URL信息显示:在
getAddressInfo函数中遍历解析结果,打印目标主机、端口以及所有解析到的IP地址(IPv4/IPv6)。 - 修复HTTP请求:动态生成
Host字段,替换为用户输入的主机名,符合HTTP规范。 - 优化缓冲区:增大接收缓冲区,避免截断长响应。
内容的提问来源于stack exchange,提问作者PikaKirbyBro
相关产品推荐
相关产品推荐

