c - select() 在未连接的套接字上返回 1(不一致)
问题描述
我正在使用非阻塞套接字连接到服务器。
在一个特定的测试场景中,服务器宕机,这意味着一个 TCP SYN 出去了,但是没有响应,永远不可能建立连接。
在此设置中,通常会select
在 2 秒后超时,返回 0。这是大多数情况下的行为,而且看起来是正确的。
然而,在大约 5% 的情况下,select
立即返回 1(表示套接字在掩码中是可读的)。
但是当我read(2)
从套接字-1
中返回时,返回的是' Network is unreachable
'
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// sockfd checked and > 0
// set non-blocking
struct timeval tv{};
tv.tv_sec = 2;
int ret = connect(sockfd, addr, addrlen ); // addr set elsewhere
if (ret < 0 && errno == EINPROGRESS)
{
fd_set cset;
FD_ZERO(&cset);
FD_SET(sockfd, &cset);
ret = select(sockfd + 1, &cset, nullptr, nullptr, &tv);
// returns 1 sometimes
}
在第一篇文章中,我错误地指出,在错误情况下,网络上只有一个 TCP SYN(没有重试)。
这不是真的; 在错误和非错误情况下,网络上都有一个 TCP SYN 在 1 秒后重新发送。
什么可能导致这种情况,有没有办法获得一致的行为select
?
解决方案
确定非阻塞是否完成的正确connect()
方法是要求select()
可写性而不是可读性。connect()
这在文档中明确说明:
EINPROGRESS
套接字是非阻塞的,连接不能立即完成。(UNIX 域套接字改为失败EAGAIN
。) 可以通过选择用于写入的套接字来完成select(2)
或poll(2)
完成。在select(2)
指示可写性之后,用于getsockopt(2)
读取SO_ERROR
级别的选项SOL_SOCKET
以确定是connect()
成功完成(SO_ERROR
为零)还是未成功完成(是SO_ERROR
此处列出的常见错误代码之一,解释失败的原因)。
在您知道连接实际上已首先建立之前,使用/测试套接字的可读性是未定义的行为。select()
poll()
试试这个:
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// sockfd checked and > 0
// set non-blocking
int ret = connect(sockfd, addr, addrlen); // addr set elsewhere
if (ret < 0)
{
if (errno != EINPROGRESS)
{
close(sockfd);
sockfd = -1;
}
else
{
fd_set cset;
FD_ZERO(&cset);
FD_SET(sockfd, &cset);
struct timeval tv{};
tv.tv_sec = 2;
ret = select(sockfd + 1, nullptr, &cset, nullptr, &tv);
if (ret <= 0)
{
close(sockfd);
sockfd = -1;
}
else
{
int errCode = 0;
socklen_t len = sizeof(errCode);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &errCode, &len);
if (errCode != 0)
{
close(sockfd);
sockfd = -1;
}
}
}
}
if (sockfd != -1)
{
// use sockfd as needed (read(), etc) ...
close(sockfd);
}
推荐阅读
- javascript - Outlook AddIn GetAsync 成功但不返回任何内容
- python - 如何正则表达式替换其中替换是搜索关键字/标签的功能
- javascript - 如何按照这种模式将 Javascript 对象的所有函数复制到不同的函数?
- c# - 如何在 C# 中使用 drawString 更新日期
- image - FFmpeg - 将两个图像 (.png) 合并到视频 (.mp4) 不起作用
- amazon-redshift - 在 Redshift 中是否有相当于 PostgreSQL 的横向连接?
- selenium - Selenium xpath 找不到元素(其他 xpath 工具证明它在那里)
- php - 卷发需要很多时间
- r - 将不同列的大数据文件合并为一个大文件
- git - 如何合并来自两个 git 存储库的代码在分支中具有相同的代码,但历史记录不同?