[/
    Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

    Official repository: https://github.com/boostorg/beast
]

[section:timeouts 超时处理 __示例__]

网络程序必须处理各种不利的连接状况，最常见的情况就是对端意外离线。由于对端已经不在线，自然无法发送消息来告知自身已离线，因此协议本身无法可靠识别这种状态。对端离线的原因有多种可能：

[itemized_list
  [The peer experiences a power loss]
  [The peer becomes disconnected from the network]
  [The local host becomes disconnected from the network]
  [The network itself becomes unavailable]
]

为了判断对端是否已离线或处于空闲状态，程序需要实现超时算法。该算法会在满足特定条件（例如，在一段时间内未收到任何数据）时关闭连接。超时机制可用于以下场景：

[itemized_list
  [Drop malicious or poorly performing hosts]
  [Close idle connections to free up resources]
  [Determine if a peer is offline or no longer available]
]

传统做法是，程序使用 [@boost:/doc/html/boost_asio/reference/steady_timer.html `net::steady_timer`] 来检测超时，然后调用套接字的 [@boost:/doc/html/boost_asio/reference/basic_socket/close/overload2.html `close`] 方法释放资源。对于非专业人士，单独管理一个计时器所带来的复杂性，常常让人感到 [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1269r0.html#timers 困扰]。

[note
  出于可移植性考虑，网络库本身不提供超时机制。
  or cancellation features for synchronous stream operations.
]

为简化超时处理流程，以下类型对 [@boost:/doc/html/boost_asio/reference/basic_stream_socket.html `net::basic_stream_socket`] 进行封装，并提供额外功能：

[table 
[[Name][Features]]
[
  [[link beast.ref.boost__beast__tcp_stream `tcp_stream`]]
  [[itemized_list
    [Timeouts for logical operations]
    [[@boost:/doc/html/boost_asio/reference/ip__tcp.html `net::ip::tcp`] protocol]
    [[@boost:/doc/html/boost_asio/reference/executor.html `net::executor`] executor]
    [[link beast.ref.boost__beast__unlimited_rate_policy `unlimited_rate_policy`] rate limits]
  ]]
][
  [[link beast.ref.boost__beast__basic_stream `basic_stream`]]
  [[itemized_list
    [Timeouts for logical operations]
    [Configurable __Protocol__ type]
    [Configurable __Executor__ type]
    [Configurable __RatePolicy__ type]
  ]]
]]

[/-----------------------------------------------------------------------------]

[heading 构造函数]

`tcp_stream` 用于替代 [@boost:/doc/html/boost_asio/reference/ip__tcp/socket.html `net::ip::tcp::socket`]。任何当前使用套接字的程序都可以改用 `tcp_stream` 并获得上述功能（但部分接口存在差异，详见下文）。网络库现在允许 I/O 对象使用任意 __ExecutionContext__ 或 __Executor__ 实例进行构造。以下示例构造一个流，该流使用特定的 I/O 上下文来调度完成处理器：

[code_core_3_timeouts_1]

或者，也可以使用执行器来构造流：

[code_core_3_timeouts_2]

[@boost:/doc/html/boost_asio/reference/make_strand.html `make_strand`] 函数用于从执行上下文或执行器构造一个串行器。当为流的执行器选择 [@boost:/doc/html/boost_asio/reference/strand.html `net::strand`] 时，所有尚未关联执行器的完成处理器都会使用该串行器。这种方式既简化代码写法（调用点无需使用 `strand::wrap` 或 `bind_executor`），也提高安全性，因为不会再出现忘记使用串行器的情况。

[code_core_3_timeouts_3]

[/-----------------------------------------------------------------------------]

[heading 连接]

在交换数据之前，该流需要先连接到对等端。以下代码用于为异步连接操作设置超时时间。在 Beast 中，用于连接端点范围（例如由 net::ip::tcp::resolver::resolve 返回的范围）的函数属于该类的成员函数，而不是像 net::async_connect 那样的自由函数。

[code_core_3_timeouts_4]

服务器使用绑定至特定 IP 地址和端口的接收器，用于监听并接收传入的连接请求。该接收器用于返回一个普通套接字。以下代码用于展示如何基于该底层 basic_stream_socket 通过移动构造来创建 tcp_stream：

[code_core_3_timeouts_5]

[/-----------------------------------------------------------------------------]

[heading 读取与写入]

超时时间适用于整个逻辑操作（即由一系列异步调用组成的操作），而不仅仅是下一次调用。以下代码用于从流中读取一行数据并将其写回。读取和写入操作均必须在设置超时时间后的 30 秒内完成；在这两次操作之间，定时器不会被重置。

[code_core_3_timeouts_6]

由于读取和写入可以并发进行，因此可能会同时存在两个逻辑操作，且每个操作仅用于读取或仅用于写入。新读取或写入操作的开始将使用最近设置的超时时间。这不会影响已经处于未决状态的操作。

[code_core_3_timeouts_7]

当设置超时时，会取消之前尚未有未完成操作正在进行的读写超时。对于循环执行逻辑操作的算法，只需在执行逻辑操作之前设置一次超时，这种情况下无需调用 `expires_never`。以下示例实现一个持续回显行数据并带有超时机制的算法，该示例作为一个完整函数提供。

[code_core_3_timeouts_1f]

[/-----------------------------------------------------------------------------]

[heading https_get]

值得注意的是，目前所有涉及超时读写的示例，均使用了现有的网络流算法。由于这些算法采用泛型编写，适用于任何满足流（Stream）要求的对象，因此在与‘tcp_stream’配合使用时，能够透明地支持超时机制。借助这一特性，还可以为当前尚不支持超时的流包装器（Stream Wrappers）启用超时功能。

以下代码用于建立加密连接、发送 HTTP 请求、接收 HTTP 响应，并正常关闭连接。如果这些操作的总耗时超过 30 秒，就会触发超时。这段代码主要展示两件事：一是如何用 `tcp_stream` 为原本不支持超时的流算法添加上超时功能，二是如何通过异步中间操作来构建一个阻塞算法。

[code_core_3_timeouts_2f]

[endsect]

[/-----------------------------------------------------------------------------]

[section:rate_limiting 速率限制 __示例__]

[link beast.ref.boost__beast__basic_stream `basic_stream`] 类模板支持额外的 `RatePolicy` 模板参数。该类型的对象必须满足 __RatePolicy__ 概念的要求，用于实现速率限制或带宽管理。`basic_stream` 和 `tcp_stream` 的默认策略为 [link beast.ref.boost__beast__unlimited_rate_policy `unlimited_rate_policy`]，该策略对读写操作不施加任何限制。该库提供 [link beast.ref.boost__beast__simple_rate_policy `simple_rate_policy`] 策略，支持以字节/秒为单位分别控制读写速率限制。以下代码创建一个使用简单速率策略的 basic_stream 实例，并设置读写限制：

[code_core_3_timeouts_8]

更复杂的速率策略可以作为用户自定义类型来实现，只需满足 __RatePolicy__ 概念的要求即可。下面我们开发一个速率策略，用于测量读写操作的瞬时吞吐量。首先编写一个辅助类，该类对一系列离散的速率采样值应用指数平滑函数，以计算瞬时吞吐量。

[code_core_3_timeouts_3f]

接着定义速率策略对象。我们让类型 [link beast.ref.boost__beast__rate_policy_access `rate_policy_access`] 成为友元，这样可以将实现设为私有，同时仍然允许 `basic_stream` 访问所需函数。这种方式可以避免为 `basic_stream` 类模板编写繁琐的友元声明。速率策略对象的公有成员会通过调用 `rate_policy` 成为流对象接口的一部分。

[code_core_3_timeouts_4f]

要使用新策略，我们声明一个流实例，然后像往常一样配合流算法使用。任何时候都可以通过调用策略中的方法来获取当前的读速率或写速率。

[code_core_3_timeouts_9]

[endsect]

[/-----------------------------------------------------------------------------]
