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

2w3rbyxf  于 2023-11-17  发布在  Linux
关注(0)|答案(1)|浏览(94)

**已关闭。**此问题为not reproducible or was caused by typos。目前不接受回答。

这个问题是由错字或无法再重现的问题引起的。虽然类似的问题在这里可能是on-topic,但这个问题的解决方式不太可能帮助未来的读者。
3天前关闭。
Improve this question
我尝试使用主机名和端口创建一个套接字。但是,::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;
}

字符串

uubf1zoe

uubf1zoe1#

您错误地调用了select()
你需要改变这一点:

FD_SET(0, &rfds);

字符串
对此:

FD_SET(sockfd, &rfds);


改变这个:

::select(sockfd, ...);


对此:

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


也就是说,在你所展示的代码中,在connect()之后使用isSocketReady()是没有意义的。HTTP和WebSocket协议都要求你在读取任何数据之前发送一个请求,但是你没有这样做,所以套接字永远不会报告为可读。
如果你试图使用isSocketReady()来检查connect()是否成功,那么这也是错误的,原因有两个:

  • 你必须测试套接字的 * 可写性 * 而不是 * 可读性 *。
  • 你没有将套接字设置为非阻塞模式(TCP_NODELAY不是为了这个目的),所以connect()将阻塞调用线程,直到连接完全建立或失败。所以你不能使用select()来检查这种情况。要将套接字设置为非阻塞模式,你必须执行以下操作之一(取决于你的代码运行在哪个平台上):
  • socket()调用中创建带有SOCK_NONBLOCK标志的套接字。
  • 使用fctrl(F_SETFL)在套接字上启用O_NONBLOCK标志。
  • 使用ioctl()(posix)或ioctlsocket()(Windows)在套接字上启用FIONBIO选项。

相关问题