【发布时间】:2014-09-14 07:43:36
【问题描述】:
我正在编写一个同步客户端。其中一部分是一个Connection 对象,它负责实际发送和接收数据。整个库是使用 Boost ASIO ip::tcp::socket 类编写的。
我有一个测试,其中客户端调用服务器上的一个方法(休眠 2 秒),超时时间为 1 秒。我的代码检测到执行花费的时间超过了请求的时间,但它没有及时返回。相反,它在整整 2 秒后返回。
我已将问题缩小到receive 方法:
void Connection::receive(const mutable_buffers_1& buffers, const DurationType& timeout)
{
// to make sure it isn't 0 by mistake
auto actualTimeout = std::max(DurationType(milliseconds(1)), timeout);
SocketReceiveTimeoutOption timeoutOption(actualTimeout);
error_code ec;
_socket.set_option(timeoutOption, ec);
RPC_LOG(TRACE) << "Setting timeout " << actualTimeout << " returned: " << ec.message();
RPC_LOG(TRACE) << "Receiving...";
if (_socket.receive(buffers, MSG_WAITALL, ec) != buffer_size(buffers))
{
throw RpcCommunicationError("Did not receive the expected number of bytes from connection");
}
RPC_LOG(TRACE) << "Received! With error code: " << ec.message();
}
DurationType 只是一个方便的 typedef:
typedef boost::chrono::system_clock ClockType;
typedef ClockType::time_point::duration DurationType;
SocketReceiveTimeoutOption 是为套接字实现的选项:
template <int Name>
class SocketTimeoutOption
{
public:
#ifdef BSII_WINDOWS
SocketTimeoutOption(const DurationType& timeout) : _value(static_cast<DWORD>(boost::chrono::duration_cast<boost::chrono::milliseconds>(timeout).count())) {}
#else
SocketTimeoutOption(const DurationType& timeout) : _value(Utils::toTimeval(timeout)) {}
#endif
// Get the level of the socket option.
template <typename Protocol>
int level(const Protocol&) const
{
return SOL_SOCKET;
}
// Get the name of the socket option.
template <typename Protocol>
int name(const Protocol&) const
{
return Name;
}
// Get the address of the timeout data.
template <typename Protocol>
void* data(const Protocol&)
{
return &_value;
}
// Get the address of the timeout data.
template <typename Protocol>
const void* data(const Protocol&) const
{
return &_value;
}
// Get the size of the boolean data.
template <typename Protocol>
std::size_t size(const Protocol&) const
{
return sizeof(_value);
}
private:
#ifdef BSII_WINDOWS
DWORD _value;
#else
timeval _value;
#endif
};
typedef SocketTimeoutOption<SO_RCVTIMEO> SocketReceiveTimeoutOption;
typedef SocketTimeoutOption<SO_SNDTIMEO> SocketSendTimeoutOption;
最后
namespace Utils
{
inline
timeval toTimeval(const DurationType& duration)
{
timeval val;
auto seconds = boost::chrono::duration_cast<boost::chrono::seconds>(duration); // TODO: make sure this is truncated down in case there's fractional seconds
val.tv_sec = static_cast<long>(seconds.count());
auto micro = boost::chrono::duration_cast<boost::chrono::microseconds>(duration - seconds);
val.tv_usec = static_cast<long>(micro.count());
return val;
}
}
问题是即使我指定了 1 秒的超时时间,receive 方法仍然需要整个 2 秒。这是日志:
2014-09-14 10:27:53.348383 |跟踪 | 0x007f24e50ae7c0 |设置超时 999917107 纳秒返回:成功
2014-09-14 10:27:53.348422 |跟踪 | 0x007f24e50ae7c0 |正在接收...
2014-09-14 10:27:55.349152 |跟踪 | 0x007f24e50ae7c0 |已收到!带有错误代码:成功
如您所见,设置超时有效,但 receive 方法仍然需要 2 秒。
相同的代码在 Windows 上也能正常工作。
【问题讨论】:
-
DurationType和SocketReceiveTimeoutOption未定义。如果它们是实际 boost 类型的 typedef,请在您的示例中显示 typedef 或使用实际的 boost 类型。 -
谢谢。编辑原始问题。现在我看到在 Linux 案例中设置超时选项时可能存在错误。我会检查并更新。
-
更新:我认为将持续时间转换为 timeval 是可以的。我添加了一些日志记录并得到以下结果:Converting 999718938 nanoseconds to timeval Got: 0 seconds + 999718 useconds
标签: linux sockets boost-asio