提问人:intrigued_66 提问时间:11/13/2023 更新时间:11/13/2023 访问量:53
从主机名和端口创建套接字,但 ::select() 表示没有数据 [关闭]
Create socket from hostname and port but ::select() indicates no data [closed]
问:
我正在尝试使用主机名和端口创建套接字。但是,这意味着套接字没有数据。::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;
}
答:
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_NODELAY
connect()
select()
- 在调用上创建带有标志的套接字。
SOCK_NONBLOCK
socket()
- 用于启用套接字上的标志。
fctrl(F_SETFL)
O_NONBLOCK
- 使用 (posix) 或 (Windows) 在套接字上启用该选项。
ioctl()
ioctlsocket()
FIONBIO
- 在调用上创建带有标志的套接字。
评论
FD_SET(0, &rfds); select(sockfd, &rfds, NULL, NULL, &tv);
- 这是一个不正确的代码。请仔细阅读 man select(2)。connect
FD_SET(sockfd, &rfds)
select()