连接到套接字时似乎无法实现超时

2023-12-11

我正在尝试为 connect() 提供超时。我四处搜寻,发现了几篇与此相关的文章。我已经编写了我认为应该有效的代码,但不幸的是我没有收到 getsockopt() 报告的错误。但是当我执行 write() 时,它失败了,错误号为 107 - ENOTCONN。

有几点。我在 Fedora 23 上运行。 connect() 的文档说,对于尚未完成的连接,它应该返回失败,错误号为 EINPROGRESS,但我遇到了 EAGAIN,所以我将其添加到我的检查中。目前,我的套接字服务器正在listen() 调用中将积压设置为零。许多调用都会成功,但失败的调用都会失败,并出现我在 write() 调用中提到的 107 - ENOTCONN。

我希望我只是错过了一些东西,但到目前为止还不知道是什么。

int domain_socket_send(const char* socket_name, unsigned char* buffer,
        unsigned int length, unsigned int timeout)
{
    struct sockaddr_un addr;
    int fd = -1;
    int result = 0;

    // Create socket.

    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd == -1)
        {
        result = -1;
        goto done;
        }

    if (timeout != 0)
        {

        // Enabled non-blocking.

        int flags;
        flags = fcntl(fd, F_GETFL);
        fcntl(fd, F_SETFL, flags | O_NONBLOCK);
        }

    // Set socket name.

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);

    // Connect.

    result = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
    if (result == -1)
        {

        // If some error then we're done.

        if ((errno != EINPROGRESS) && (errno != EAGAIN))
            goto done;

        fd_set write_set;
        struct timeval tv;

        // Set timeout.

        tv.tv_sec = timeout / 1000000;
        tv.tv_usec = timeout % 1000000;

        unsigned int iterations = 0;
        while (1)
            {
            FD_ZERO(&write_set);
            FD_SET(fd, &write_set);

            result = select(fd + 1, NULL, &write_set, NULL, &tv);
            if (result == -1)
                goto done;
            else if (result == 0)
                {
                result = -1;
                errno = ETIMEDOUT;
                goto done;
                }
            else
                {
                if (FD_ISSET(fd, &write_set))
                    {
                    socklen_t len;
                    int socket_error;
                    len = sizeof(socket_error);

                    // Get the result of the connect() call.

                    result = getsockopt(fd, SOL_SOCKET, SO_ERROR,
                            &socket_error, &len);
                    if (result == -1)
                        goto done;

                    // I think SO_ERROR will be zero for a successful
                    // result and errno otherwise.

                    if (socket_error != 0)
                        {
                        result = -1;
                        errno = socket_error;
                        goto done;
                        }

                    // Now that the socket is writable issue another connect.

                    result = connect(fd, (struct sockaddr*) &addr,
                            sizeof(addr));
                    if (result == 0)
                        {
                        if (iterations > 1)
                            {
                            printf("connect() succeeded on iteration %d\n",
                                    iterations);
                            }
                        break;
                        }
                    else
                        {
                        if ((errno != EAGAIN) && (errno != EINPROGRESS))
                            {
                            int err = errno;
                            printf("second connect() failed, errno = %d\n",
                                    errno);
                            errno = err;
                            goto done;
                            }
                        iterations++;
                        }
                    }
                }
            }
        }

    // If we put the socket in non-blocking mode then put it back
    // to blocking mode.

    if (timeout != 0)
        {

        // Turn off non-blocking.

        int flags;
        flags = fcntl(fd, F_GETFL);
        fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
        }

    // Write buffer.

    result = write(fd, buffer, length);
    if (result == -1)
        {
        int err = errno;
        printf("write() failed, errno = %d\n", err);
        errno = err;
        goto done;
        }

done:
    if (result == -1)
        result = errno;
    else
        result = 0;
    if (fd != -1)
        {
        shutdown(fd, SHUT_RDWR);
        close(fd);
        }
    return result;
}

2016 年 4 月 5 日更新:

我突然意识到,也许我需要多次调用 connect() 直到成功,毕竟这是非阻塞 io 而不是异步 io。就像在 read() 上遇到 EAGAIN 后有数据要读取时必须再次调用 read() 一样。另外,我发现了以下SO问题:

使用 select() 进行非阻塞套接字连接始终返回 1

其中 EJP 的回答说您需要发出多个 connect() 。另外,来自 EJP 参考文献:

它似乎表明您需要发出多个 connect() 。我已经修改了这个问题中的代码片段以调用 connect() 直到成功。我可能仍然需要对可能更新传递给 select() 的超时值进行更改,但这不是我直接的问题。

多次调用 connect() 似乎已经解决了我原来的问题,即我在调用 write() 时收到 ENOTCONN,我猜是因为套接字未连接。但是,您可以从代码中看到我正在跟踪 select 循环的次数,直到 connect() 成功。我见过这个数字达到数千。这让我担心我正处于繁忙的等待循环中。为什么套接字可写,即使它没有处于 connect() 会成功的状态?调用 connect() 是否清除了可写状态,并且由于某种原因它被操作系统再次设置,或者我真的处于繁忙的等待循环中?

谢谢, 缺口


From http://lxr.free-electrons.com/source/net/unix/af_unix.c:

441 static int unix_writable(const struct sock *sk)
442 {
443         return sk->sk_state != TCP_LISTEN &&
444                (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
445 }

我不确定正在比较的这些缓冲区是什么,但显然没有检查套接字的连接状态。因此,除非在套接字连接时修改这些缓冲区,否则我的 unix 套接字将始终被标记为可写,因此我无法使用 select() 来确定非阻塞 connect() 何时完成。

并基于此片段http://lxr.free-electrons.com/source/net/unix/af_unix.c:

1206 static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
1207                                int addr_len, int flags)
.
.
.
1230         timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
.
.
.
1271         if (unix_recvq_full(other)) {
1272                 err = -EAGAIN;
1273                 if (!timeo)
1274                         goto out_unlock;
1275 
1276                 timeo = unix_wait_for_peer(other, timeo);
.
.
.

看来设置发送超时可能能够使连接超时。这也与 SO_SNDTIMEO 的文档匹配http://man7.org/linux/man-pages/man7/socket.7.html.

谢谢, 缺口

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

连接到套接字时似乎无法实现超时 的相关文章

随机推荐

  • 从 k8s 入口动态添加/删除命名主机

    我正在 GKE 上设置 k8s 集群 通配符 DNS server com将指向入口控制器 在集群内部 将有网络服务器 Pod 每个 Pod 都公开一个独特的服务 Ingress 控制器将使用服务器名称来路由到各种服务 服务器几乎每天都会被
  • 当泄漏工具未显示内存泄漏时,如何调试内存泄漏?

    我有一个用 Swift 编写的 iOS 应用程序 该应用程序正在泄漏内存 在某些情况下 一些对象应该被释放 但它们没有 我通过简单地添加了解了这个问题deinit调试消息如下 deinit println DEINIT KeysProvid
  • 贪心算法无法完成 0-1 背包 p‌r‌o‌b‌l‌e‌m 的情况

    我正在寻找一种情况 其中选择重量 考虑容量为 4 的背包以及具有以下重量和价值的物品 Item Weight Value value Weight A 3 1 65 0 55 B 2 1 0 5 C 2 1 0 5 基于每权重价值的贪婪算法
  • 有没有办法在 Dart 中通过引用传递原始参数?

    我想通过引用传递一个原语 int bool 我在这里找到了关于它的讨论 通过引用传递值类型 段落 Dart 中的值类型 但我仍然想知道是否有办法在 Dart 中做到这一点 除了使用对象包装器 有什么发展吗 Dart 语言不支持这一点 我怀疑
  • 将 Service Broker 与 Sql Server Express 2008 结合使用

    是否可以在 sql Express 上使用外部激活而不通过 sql enterprise standard 我想向 sql Express 服务代理发送一条消息 然后让它通知在同一盒子上运行的外部应用程序服务 以便启动控制台应用程序来拾取消
  • 使用 jQuery.ajax 和 JSONP 设置标头?

    我正在尝试使用 jQuery 访问 google 文档 这是我到目前为止所拥有的 var token my auth token ajax url http docs google com feeds documents private f
  • 当 COM 事件发生时,如何更新自定义图形项(在 pyqtgraph 中)?

    我做了一个程序 实时接收原油期货的每笔交易信息 基本上 OnReceiveRealData当事务执行并调用时执行real get方法 在该方法中 收集当前时间 价格和数量数据并用它们制作字典 有更多方法可以从实时流数据中制作 OHLC 格式
  • 如何获取当前shell脚本的完整路径名?

    有没有更简单的方法来做到这一点 bin ksh THIS SCRIPT usr bin readlink f echo 0 bin sed s PWD echo THIS SCRIPT 我被困在使用ksh但更喜欢一个适用于的解决方案bash
  • 闹钟没有停止

    我试图停止警报并检查它是否停止 但它总是返回 true 意味着警报正在工作 我尝试根据链接中的答案停止警报https stackoverflow com a 17616299 1226882但这对我不起作用 请参考下面的代码 启动警报 pu
  • 为什么 Spark Mongo 连接器不下推过滤器?

    我有一个大型 Mongo 集合 想在我的 Spark 应用程序中使用 Spark Mongo 连接器 该集合相当大 gt 10 GB 并且包含每日数据 索引为original item CreatedDate场地 在 Mongo 中选择几天
  • ReadProcessMemory 在某些页面上失败 (GetLastError()=299)

    我尝试读取进程 Win7 64 的所有提交页面 在大多数页面上它可以工作 但在少数页面上会失败 我无法解释为什么 这是我的测试程序 x32编译 在Win7 64中测试 include
  • HTML5 视频自动播放在 Chrome 中不起作用

    我正在尝试使用 html5 视频在滑块中显示视频 这适用于controls参数 显示控件 但是当我添加autoplay它不会自动播放 所以我搜索了原因 发现 chome 只允许静音视频自动播放 所以我添加了muted参数 但是当我这样做时
  • Microsoft 应用程序 ID 和密码有何用途?

    我正在尝试部署我的 Azure Bot 服务 但我有点难以理解 Microsoft 应用程序 ID 和密码的用途是什么 有人能给我解释一下这些术语吗 我试图在网上寻找答案但没有成功 感谢您的任何帮助 这是机器人服务的标识 你会注意到在应用程
  • 如何在 Swift 中将“Index”转换为“Int”类型?

    我想将字符串中包含的字母的索引转换为整数值 尝试读取头文件 但找不到类型Index 虽然它看起来符合协议ForwardIndexType使用方法 例如distanceTo var letters abcdefg let index lett
  • 排序/过滤 MVC

    我正在尝试按作者姓氏 出版日期 最新和最旧 标题 最受欢迎和评分最高对搜索结果进行排序 我使用了一个例子 几乎逐字复制 但它对我不起作用 对我做错了什么有什么建议吗 这是我的控制器代码 public ActionResult Index s
  • 如何将 Ncurses 添加到 Visual Studio 2017 中的项目?

    我正在寻找详细的分步指南 以将 Ncurses 添加到 Visual Studio 2017 中的项目中 我下载了源代码 但我不知道如何将其添加到我的项目中 我知道这个问题对你来说很愚蠢 但我是 C 的初学者 没有在网上找到将 Ncurse
  • javax.validation.ValidationException:HV000183:无法加载“javax.el.E​​xpressionFactory”

    我尝试使用休眠验证器编写非常简单的应用程序 我的步骤 在 pom xml 中添加了以下依赖项
  • 如何将 Blue Imp 文件上传集成到 CakePHP?

    Wa正在使用CakePHP 需要集成Blue Imp文件上传库 有人成功整合了这些吗 如果是 您是否设置上传以将文件存储在数据库或文件系统上 您能否分享此类集成的示例 非常感谢您的帮助和指导 是的 我在 php 中集成了 BlueImp 文
  • 如何使用 SQL 从字符串末尾删除数字

    拜托 你能回答我的问题吗 如何使用 SQL 从字符串末尾删除数字 例如 字符串 2Ga4la2009 必须转换为 2Ga4la 问题是我们无法修剪它们 因为我们不知道字符串末尾有多少位数字 最好的问候 加琳娜 这似乎有效 select le
  • 连接到套接字时似乎无法实现超时

    我正在尝试为 connect 提供超时 我四处搜寻 发现了几篇与此相关的文章 我已经编写了我认为应该有效的代码 但不幸的是我没有收到 getsockopt 报告的错误 但是当我执行 write 时 它失败了 错误号为 107 ENOTCON