You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:15:00