【问题标题】:can you set SO_RCVTIMEO and SO_SNDTIMEO socket options in boost asio?你可以在 boost asio 中设置 SO_RCVTIMEO 和 SO_SNDTIMEO 套接字选项吗?
【发布时间】:2008-11-15 20:13:09
【问题描述】:

你能在 boost asio 中设置 SO_RCVTIMEO 和 SO_SNDTIMEO 套接字选项吗?

如果有怎么办?

请注意,我知道您可以改用计时器,但我想特别了解这些套接字选项。

【问题讨论】:

    标签: c++ boost boost-asio


    【解决方案1】:

    绝对! Boost ASIO 允许您访问本机/底层数据,在这种情况下是 SOCKET 本身。所以,假设你有:

    boost::asio::ip::tcp::socket my_socket;
    

    假设您已经调用了openbind 或一些实际上使my_socket 可用的成员函数。然后,要获取底层 SOCKET 值,请调用:

    SOCKET native_sock = my_socket.native();
    int result = SOCKET_ERROR;
    
    if (INVALID_SOCKET != native_sock)
    {
        result = setsockopt(native_sock, SOL_SOCKET, <the pertinent params you want to use>);
    }
    

    所以你有它! Boost 的 ASIO 让您可以比其他方式更快地完成许多事情,但是您仍然需要正常的套接字库调用很多事情。这恰好是其中之一。

    【讨论】:

    • 除了最底层的 send/recv 函数外,所有的函数都有 for (;;) 循环围绕明确捕获 EAGAIN 的调用。这有效地使 SO_{SND,RCV}TIMEO 选项无用,除非您丢弃 Boost 中 95% 的发送/接收功能。因此,它们允许您设置选项是没有实际意义的,因为利用它的唯一方法是不使用库的其余部分......
    【解决方案2】:

    它似乎没有内置在 Boost.Asio 中(截至当前的 Boost SVN),但是,如果您愿意编写自己的类来模拟 boost::asio::detail::socket_option 类,您可以随时按照boost/asio/socket_base.hpp 并执行以下操作:

    typedef boost::asio::detail::socket_option::timeval<SOL_SOCKET, SO_SNDTIMEO>
        send_timeout;
    typedef boost::asio::detail::socket_option::timeval<SOL_SOCKET, SO_RCVTIMEO>
        receive_timeout;
    

    (显然,我不建议您将timeval 类注入boost::asio::detail::socket_option 命名空间,但我目前想不出一个好用的。:-P)

    编辑:我的socket_option::timeval 示例实现,基于socket_option::integer

    // Helper template for implementing timeval options.
    template <int Level, int Name>
    class timeval
    {
    public:
      // Default constructor.
      timeval()
        : value_(zero_timeval())
      {
      }
    
      // Construct with a specific option value.
      explicit timeval(::timeval v)
        : value_(v)
      {
      }
    
      // Set the value of the timeval option.
      timeval& operator=(::timeval v)
      {
        value_ = v;
        return *this;
      }
    
      // Get the current value of the timeval option.
      ::timeval value() const
      {
        return value_;
      }
    
      // Get the level of the socket option.
      template <typename Protocol>
      int level(const Protocol&) const
      {
        return Level;
      }
    
      // Get the name of the socket option.
      template <typename Protocol>
      int name(const Protocol&) const
      {
        return Name;
      }
    
      // Get the address of the timeval data.
      template <typename Protocol>
      ::timeval* data(const Protocol&)
      {
        return &value_;
      }
    
      // Get the address of the timeval data.
      template <typename Protocol>
      const ::timeval* data(const Protocol&) const
      {
        return &value_;
      }
    
      // Get the size of the timeval data.
      template <typename Protocol>
      std::size_t size(const Protocol&) const
      {
        return sizeof(value_);
      }
    
      // Set the size of the timeval data.
      template <typename Protocol>
      void resize(const Protocol&, std::size_t s)
      {
        if (s != sizeof(value_))
          throw std::length_error("timeval socket option resize");
      }
    
    private:
      static ::timeval zero_timeval()
      {
        ::timeval result = {};
        return result;
      }
    
      ::timeval value_;
    };
    

    【讨论】:

    • 我不能这样做:typedef boost::asio::detail::socket_option::integer send_timeout; recv 超时也一样?为什么还需要 timeval 结构?
    • 哈哈哈,你会在我的答案的第一个版本中看到,这也是我的想法。但是,阅读opengroup.org/onlinepubs/009695399/functions/setsockopt.html,您会发现超时值使用的是 timeval,而不是 int。
    • 注意:在 Win32 上,SO_SNDTIMEO 采用 DWORD,而不是 timeval
    【解决方案3】:

    解决此问题的一个简单方法是使用本机读写函数。

    对于 1 秒超时写入:

    struct timeval tv = { 1, 0 };
    setsockopt(socket.native_handle(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
    ssize_t nsent = ::write(socket->native_handle(), buff, size);
    if (nsent > 0) {
      BOOST_LOG_TRIVIAL(debug) << "Sent " << nsent << " bytes to remote client " << ep;
    } else if (nsent == 0) {
     BOOST_LOG_TRIVIAL(info) <<  "Client " << ep << " closed connection";
    } else if (errno != EAGAIN) {
      BOOST_LOG_TRIVIAL(info) <<  "Client " << ep << " error: " <<     strerror(errno);
    }
    

    对于 1 秒超时的读取:

    struct timeval tv = { 1, 0 };
    setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    ssize_t nread = ::read(socket.native_handle(), buff, audio_buff_size);
    if (nread > 0) {
    } else if (nread == 0) {
      BOOST_LOG_TRIVIAL(info) <<  "Source " << source << " server " << host << " closed connection";
      break;
    } else if (errno != EAGAIN) {
      BOOST_LOG_TRIVIAL(info) <<  "Source " << source << " server " << host << " error: " << strerror(errno);
      break;
    }
    

    这对我来说很好。

    【讨论】:

      猜你喜欢
      • 2014-12-01
      • 2012-04-08
      • 1970-01-01
      • 1970-01-01
      • 2019-03-11
      • 2012-01-07
      • 1970-01-01
      • 2015-03-30
      • 1970-01-01
      相关资源
      最近更新 更多