【问题标题】:Speeding up non-blocking Unix Sockets (C++)加速非阻塞 Unix 套接字 (C++)
【发布时间】:2009-06-25 03:02:50
【问题描述】:

我已经实现了一个简单的套接字包装类。它包括一个非阻塞功能:

void Socket::set_non_blocking(const bool b) {
    mNonBlocking = b; // class member for reference elsewhere
    int opts = fcntl(m_sock, F_GETFL);
    if(opts < 0) return;
    if(b)
        opts |= O_NONBLOCK;
    else
        opts &= ~O_NONBLOCK;

    fcntl(m_sock, F_SETFL, opts);
}

该类还包含一个简单的接收函数:

int Socket::recv(std::string& s) const {
    char buffer[MAXRECV + 1];
    s = "";
    memset(buffer,0,MAXRECV+1);
    int status = ::recv(m_sock, buffer, MAXRECV,0);

    if(status == -1) {
    if(!mNonBlocking)
        std::cout << "Socket, error receiving data\n";

        return 0;
    } else if (status == 0) {
        return 0;
    } else {
        s = buffer;
        return status;
    }
}

实际上,调用 Socket::recv() 时似乎有大约 15 毫秒的延迟。这种延迟是可以避免的吗?我看过一些使用 select() 的非阻塞示例,但不明白这有什么帮助。

【问题讨论】:

    标签: c++ select sockets delay nonblocking


    【解决方案1】:

    这取决于您如何使用套接字。如果您有多个套接字并且循环遍历所有套接字,以检查可能导致延迟的数据。

    使用非阻塞recv,您依赖于那里的数据。如果您的应用程序需要使用多个套接字,您将不得不不断地对每个套接字进行轮流池化,以确定其中是否有可用的数据。

    这对系统资源不利,因为这意味着即使无事可做,您的应用程序也会不断运行。

    您可以通过选择来避免这种情况。您基本上设置了套接字,将它们添加到组并在组中选择。当任何选定的套接字发生任何事情时,选择返回指定发生了什么以及发生在哪个套接字上。

    有关如何使用选择的一些代码请查看beej's guide to network programming

    【讨论】:

    • 感谢您的建议,但我的问题实际上是 1 个且只有 1 个套接字,其中对 recv() 的调用呈现不可接受的 15 毫秒延迟。由于它仅轮询 1 个套接字,我猜测问题出在其他地方是否不正确?
    • 可能还有其他因素,根据少量代码无法判断发生了什么。如果只有一个套接字并且我假设您正在等待数据,为什么要使用非阻塞?这是否意味着您在等待数据时正在做其他事情?是否有可能其他工作使您无法在数据到达后立即接收数据?
    • @duckworthd 你是在 recv() 周围开始/停止你的计时器,还是你在测量中包括更多?此外,您确实没有进行错误检查;那 -1 可能不是您假设的 EAGAIN 。您是否获得了预期的数据?
    • @duckworthd:您如何测量 recv 需要大约 15 毫秒的时间来接收
    【解决方案2】:

    select 会让你指定一个超时时间,并且可以测试套接字是否准备好被读取。所以你可以使用小于 15ms 的东西。顺便说一句,您需要小心您拥有的代码,如果网络上的数据可以包含嵌入的 NUL s,则不会包含所有读取的数据。你应该使用s.assign(buffer, status);之类的东西。

    【讨论】:

      【解决方案3】:

      除了 stefanB,我看到您每次都将缓冲区归零。何苦? recv 返回实际读取的字节数。只需将 ( buffer[status+1]=NULL ) 之后的一个字节清零

      【讨论】:

      • 虽然在几乎任何实现中都有效,但我不确定 NULL 常量(用于指针)是否保证始终与字符 '\0' 相同。
      【解决方案4】:

      您的MAXRECV 有多大?可能只是您在堆栈增长时遇到了页面错误。其他人已经提到完全没有必要将接收缓冲区清零。当您从收到的字符数据中创建 std::string 时,您还会进行内存分配和复制命中。

      【讨论】:

        猜你喜欢
        • 2011-12-27
        • 2012-06-13
        • 1970-01-01
        • 1970-01-01
        • 2010-10-31
        • 2013-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多