从主机名和端口创建套接字,但 ::select() 表示没有数据 [关闭]

Create socket from hostname and port but ::select() indicates no data [closed]

提问人:intrigued_66 提问时间:11/13/2023 更新时间:11/13/2023 访问量:53

问:


这个问题是由错别字或无法再现的问题引起的。虽然类似的问题可能在这里是主题,但这个问题的解决方式不太可能帮助未来的读者。

7天前关闭。

我正在尝试使用主机名和端口创建套接字。但是,这意味着套接字没有数据。::select()

有人可以帮忙吗?

下面的独立示例(和 Godbolt 链接)。

https://godbolt.org/z/r7G3aczdK

#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(0, &rfds);

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

    const int retval = ::select(sockfd, &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)
        {
            continue;
        }
        else if(isSocketReady(sockfd))
        {
            break;
        }            
    }

    freeaddrinfo(res);
    return sockfd;
}

int main()
{
    int sock = connectByHostName("example.com", 80);
    std::cout << sock << std::endl;
}
C++ Linux TCP 网络编程

评论

1赞 Marco Bonelli 11/13/2023
为什么你首先需要数据?您正在连接到 HTTP 服务器,除非您首先发送 HTTP 请求,否则您将不会得到任何响应。
1赞 Marco Bonelli 11/13/2023
啊,那么经典的XY问题。无论如何,websocket 也是一样的。Websocket 建立在 HTTP 之上。除非您首先发送请求,否则您将无法从服务器获得任何内容。您真的在发送请求吗?
3赞 273K 11/13/2023
FD_SET(0, &rfds); select(sockfd, &rfds, NULL, NULL, &tv);- 这是一个不正确的代码。请仔细阅读 man select(2)。
1赞 Marco Bonelli 11/13/2023
@intrigued_66如果没有返回错误,则说明它已正确连接...connect
2赞 user207421 11/13/2023
你说错了。您需要 start,并且第一个参数 to 也不正确。正如您期望的那样,套接字将立即可读。FD_SET(sockfd, &rfds)select()

答:

2赞 Remy Lebeau 11/13/2023 #1

您呼叫不正确。select()

您需要更改此设置:

FD_SET(0, &rfds);

对此:

FD_SET(sockfd, &rfds);

并更改此设置:

::select(sockfd, ...);

对此:

::select(sockfd+1, ...); // or 0 on Windows

话虽如此,在您显示的代码中使用 after 是没有意义的。HTTP 和 WebSocket 协议都要求您先发送请求,然后才能读取任何数据,但您没有这样做,因此套接字永远不会报告为可读。isSocketReady()connect()

如果您尝试使用来检查是否成功,那么这也是错误的,原因有 2 个:isSocketReady()connect()

  • 您必须测试套接字的可性而不是可读性

  • 您没有将套接字置于非阻塞模式(不是用于此目的),因此将阻塞调用线程,直到连接完全建立或失败。因此,您不能用于检查该条件。若要将套接字置于非阻塞模式,必须执行下列操作之一(具体取决于代码运行的平台):TCP_NODELAYconnect()select()

    • 在调用上创建带有标志的套接字。SOCK_NONBLOCKsocket()
    • 用于启用套接字上的标志。fctrl(F_SETFL)O_NONBLOCK
    • 使用 (posix) 或 (Windows) 在套接字上启用该选项。ioctl()ioctlsocket()FIONBIO