C Socket编程连接被拒绝问题排查及代码优化咨询
问题描述
执行命令 ./client www.google.com 80 时收到错误:
connect() failed: Connection refused
这段代码用于获取客户端与任意输入URL服务器之间的RTT(往返时间),作为编程新手,希望了解代码中需要注意或修改的地方,同时确认问题出在代码还是环境设置,未修改过代码配置。
代码内容
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <netdb.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/time.h> #define RCVBUFSIZE 32 void DieWithError(char *errorMessage); int getAddressInfo(const char* host, unsigned short* port, struct sockaddr_in* sock_addr); int main(int argc, char *argv[]) { int sock; struct sockaddr_in httpServAddr; unsigned short httpServPort; char *httpRequest = "GET/ HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n"; char httpBuffer[RCVBUFSIZE]; int bytesRcvd, totalBytesRcvd; if (argc < 2 || argc > 3) { fprintf(stderr, "Usage: %s <Server IP/URL> [<HTTP Port>]\n", argv[0]); exit(1); } const char* host = argv[1]; if (getAddressInfo(host, &httpServPort, &httpServAddr) != 0) { DieWithError("Failed to get server address information"); } if (argc == 3) { httpServPort = atoi(argv[2]); } else { httpServPort = 80; } if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { DieWithError("socket() failed"); } memset(&httpServAddr, 0, sizeof(httpServAddr)); httpServAddr.sin_family = AF_INET; httpServAddr.sin_port = htons(httpServPort); if (connect(sock, (struct sockaddr *) &httpServAddr, sizeof(httpServAddr)) < 0) { DieWithError("connect() failed"); } char request[strlen(httpRequest) + strlen(host) + 1]; sprintf(request, httpRequest, host); if (send(sock, request, strlen(request), 0) != strlen(request)) { DieWithError("send() sent a different number of bytes than expected"); } totalBytesRcvd = 0; printf("Received: "); struct timeval start, end; gettimeofday(&start, NULL); while ((bytesRcvd = recv(sock, httpBuffer, RCVBUFSIZE - 1, 0)) > 0) { totalBytesRcvd += bytesRcvd; httpBuffer[bytesRcvd] = '\0'; printf("%s", httpBuffer); } gettimeofday(&end, NULL); long rtt = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; if (bytesRcvd < 0) { DieWithError("recv() failed"); } printf("\n\nRound-trip time: %ld ms\n", rtt); close(sock); exit(0); } int getAddressInfo(const char* host, unsigned short* port, struct sockaddr_in* httpServAddr) { struct addrinfo hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; struct addrinfo* result; int ret = getaddrinfo(host, NULL, &hints, &result); if (ret != 0) { fprintf(stderr, "getaddrinfo() failed: %s\n", gai_strerror(ret)); return -1; } // Copy the server address information to the httpServAddr struct memcpy(httpServAddr, (struct sockaddr_in*)result->ai_addr, sizeof(struct sockaddr_in)); *port = ntohs(httpServAddr->sin_port); freeaddrinfo(result); return 0; } void DieWithError(char *errorMessage) { perror(errorMessage); exit(1); }
核心问题排查与代码修改点
1. 地址被清空导致连接失败(Connection refused的直接原因)
调用getAddressInfo已经正确解析了服务器IP到httpServAddr,但后续执行的memset(&httpServAddr, 0, sizeof(httpServAddr))把解析好的IP地址清空了,只设置端口和协议族的话,相当于连接无效IP,必然失败。
修复:删除memset这行代码,仅更新端口即可:
// 删掉 memset(&httpServAddr, 0, sizeof(httpServAddr)); httpServAddr.sin_port = htons(httpServPort); // 仅修改端口
2. HTTP请求格式错误
请求行GET/ HTTP/1.1缺少空格,不符合HTTP协议规范,服务器会拒绝处理。
修复:修正请求行格式:
char *httpRequest = "GET / HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n";
3. RTT计时逻辑错误
当前代码在开始接收响应时才启动计时,仅测量了接收响应的时间,不是完整的往返时间(从发送请求到收到最后一个响应字节的时间)。
修复:在发送请求前启动计时:
struct timeval start, end; gettimeofday(&start, NULL); // 发送请求前开始计时 if (send(sock, request, strlen(request), 0) != strlen(request)) { DieWithError("send() sent a different number of bytes than expected"); } // 接收响应代码... gettimeofday(&end, NULL); // 接收完成后结束计时 long rtt = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000;
4. 端口处理逻辑冗余
getAddressInfo中传入NULL作为端口参数,返回的端口无意义,且后续会被命令行参数覆盖,可简化函数:
修复:修改getAddressInfo去掉端口返回:
int getAddressInfo(const char* host, struct sockaddr_in* httpServAddr) { struct addrinfo hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; struct addrinfo* result; int ret = getaddrinfo(host, NULL, &hints, &result); if (ret != 0) { fprintf(stderr, "getaddrinfo() failed: %s\n", gai_strerror(ret)); return -1; } memcpy(httpServAddr, (struct sockaddr_in*)result->ai_addr, sizeof(struct sockaddr_in)); freeaddrinfo(result); return 0; } // main函数中调用改为: if (getAddressInfo(host, &httpServAddr) != 0) { DieWithError("Failed to get server address information"); }
5. 栈上动态数组的安全性问题
char request[strlen(httpRequest) + strlen(host) + 1];属于C语言扩展特性,存在栈溢出风险。
修复:改用malloc分配内存:
size_t requestLen = strlen(httpRequest) + strlen(host) + 1; char *request = malloc(requestLen); if (request == NULL) { DieWithError("malloc() failed"); } sprintf(request, httpRequest, host); // 使用完成后释放内存 free(request);
6. 其他细节优化
- 增大
RCVBUFSIZE(比如设为1024),减少recv调用次数,提升输出流畅度; - 测试时优先使用国内站点(如
www.baidu.com 80),排除网络墙导致的连接失败。
内容的提问来源于stack exchange,提问作者PikaKirbyBro
相关产品推荐
相关产品推荐

