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

通过主机名和端口创建Socket,::select()显示无数据求协助

问题分析与解决

你遇到的问题核心有两处代码错误,加上对TCP连接逻辑的误解,导致select()返回无数据:

错误点1:FD_SET未添加目标Socket

在isSocketReady函数中,你把**标准输入(文件描述符0)**加入了读集合,而不是要检查的sockfd。这会导致select()一直监听键盘输入,而非目标Socket的状态。

错误点2:select()第一个参数使用错误

select()的第一个参数需要传入最大文件描述符值 + 1,而非目标Socket的描述符本身。如果传入sockfd,当sockfd大于当前监听的fd时,select()无法正确处理。

逻辑误解:TCP连接成功不代表有数据

连接到example.com的80端口后,HTTP服务器不会主动发送数据,需要你先发送HTTP请求(比如GET / HTTP/1.1\r\nHost: example.com\r\n\r\n),服务器才会返回响应。所以即使Socket连接成功,select()也会提示无数据,因为服务器还没有数据要发送。


修正后的代码

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h>

#include <string>
#include <iostream>

bool isSocketReady(int sockfd)
{
    fd_set rfds;
    struct timeval tv;

    FD_ZERO(&rfds);
    FD_SET(sockfd, &rfds); // 修正:加入目标Socket而非标准输入

    tv.tv_sec = 2;
    tv.tv_usec = 0;

    // 修正:第一个参数用最大fd+1
    const int retval = ::select(sockfd + 1, &rfds, NULL, NULL, &tv);

    if (retval == -1)
    {
        printf("select() error");
        return false;
    }
    else if (retval)
    {
        printf("Data is available now.\n");
        return true;
    }
    else
    {
        printf("No data within timeout.\n");
        return false;
    }
}

int connectByHostName(const std::string& host, int port)
{
    int sockfd = -1;

    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    const std::string port_str = std::to_string(port);

    struct addrinfo* res{nullptr};
    const int r =  getaddrinfo(host.c_str(), port_str.c_str(), &hints, &res);
    
    if(r < 0)
    {
        return -1;
    }

    for (struct addrinfo* address = res; address != nullptr; address = address->ai_next)
    {
        sockfd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
        
        if (sockfd < 0)
        {
            continue;
        }

        int flag = 1;
        setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag));

        if(::connect(sockfd, address->ai_addr, address->ai_addrlen) < 0)
        {
            close(sockfd); // 修正:连接失败时关闭Socket,避免资源泄漏
            sockfd = -1;
            continue;
        }
        else
        {
            // 发送HTTP请求,触发服务器响应
            const std::string request = "GET / HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n";
            send(sockfd, request.c_str(), request.size(), 0);
            
            if(isSocketReady(sockfd))
            {
                break;
            }
            else
            {
                close(sockfd);
                sockfd = -1;
            }            
        }
    }

    freeaddrinfo(res);
    return sockfd;
}

int main()
{
    int sock = connectByHostName("example.com", 80);
    std::cout << sock << std::endl;
    
    // 如果连接成功,读取响应
    if(sock != -1)
    {
        char buffer[1024];
        ssize_t bytes_read = recv(sock, buffer, sizeof(buffer)-1, 0);
        if(bytes_read > 0)
        {
            buffer[bytes_read] = '\0';
            std::cout << "Received response:\n" << buffer << std::endl;
        }
        close(sock);
    }
    return 0;
}

额外修正点

  • 连接失败时关闭Socket,避免文件描述符泄漏
  • 添加HTTP请求发送逻辑,确保服务器会返回数据供select()检测

内容的提问来源于stack exchange,提问作者intrigued_66

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:10:20