123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- [/
- 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 Timeouts __example__]
- Network programs must handle adverse connection conditions; the most common
- is that a connected peer goes offline unexpectedly. Protocols have no way of
- identifying this reliably: the peer is offline after all, and unable to send
- a message announcing the absence. A peer can go offline for various reasons:
- [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]
- ]
- To determine when a peer is offline or idle, a program will implement a
- [@https://en.wikipedia.org/wiki/Timeout_(computing) timeout]
- algorithm, which closes the connection after a specified amount of time if
- some condition is met. For example, if no data is received for the duration.
- A timeout may be used to:
- [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]
- ]
- Traditionally, programs use a
- [@boost:/doc/html/boost_asio/reference/steady_timer.html `net::steady_timer`]
- to determine when a timeout occurs, and then call
- [@boost:/doc/html/boost_asio/reference/basic_socket/close/overload2.html `close`]
- on the socket to release the resources. The complexity of managing a separate
- timer is often a source of
- [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1269r0.html#timers frustration]
- for non-experts.
- [note
- For portability reasons, networking does not provide timeouts
- or cancellation features for synchronous stream operations.
- ]
- To simplify the handling of timeouts, these provided types wrap a
- [@boost:/doc/html/boost_asio/reference/basic_stream_socket.html `net::basic_stream_socket`]
- to provide additional features:
- [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 Construction]
- The `tcp_stream` is designed as a replacement for
- [@boost:/doc/html/boost_asio/reference/ip__tcp/socket.html `net::ip::tcp::socket`].
- Any program which currently uses a socket, can switch to a `tcp_stream` and achieve
- the features above (although some interfaces are different, see below).
- Networking now allows I/O objects to construct with any instance of
- __ExecutionContext__ or __Executor__ objects. Here we construct a stream which
- uses a particular I/O context to dispatch completion handlers:
- [code_core_3_timeouts_1]
- Alternatively, we can construct the stream from an executor:
- [code_core_3_timeouts_2]
- The function
- [@boost:/doc/html/boost_asio/reference/make_strand.html `make_strand`]
- returns a strand constructed from an execution context or executor. When a
- [@boost:/doc/html/boost_asio/reference/strand.html `net::strand`]
- is chosen for the stream's executor, all completion handlers which do not
- already have an associated executor will use the strand. This is both a
- notational convenience (no need for `strand::wrap` or `bind_executor` at
- call sites) and a measure of safety, as it is no longer possible to forget
- to use the strand.
- [code_core_3_timeouts_3]
- [/-----------------------------------------------------------------------------]
- [heading Connecting]
- Before data can be exchanged, the stream needs to be connected to a peer.
- The following code sets a timeout for an asynchronous connect operation.
- In Beast, functions to connect to a range of endpoints (such as the range
- returned by
- [@boost:/doc/html/boost_asio/reference/ip__basic_resolver/resolve/overload3.html `net::ip::tcp::resolver::resolve`])
- are members of the class rather than free functions such as
- [@boost:/doc/html/boost_asio/reference/async_connect.html `net::async_connect`].
- [code_core_3_timeouts_4]
- A server will use an acceptor bound to a particular IP address and port to
- listen to and receive incoming connection requests. The acceptor returns
- an ordinary socket. A `tcp_stream` can be move-constructed from the
- underlying `basic_stream_socket` thusly:
- [code_core_3_timeouts_5]
- [/-----------------------------------------------------------------------------]
- [heading Reading and Writing]
- Timeouts apply to the logical operation, expressed as a series of asynchronous
- calls, rather than just the next call. This code reads a line from the stream
- and writes it back. Both the read and the write must complete within 30 seconds
- from when the timeout was set; the timer is not reset between operations.
- [code_core_3_timeouts_6]
- Since reads and writes can take place concurrently, it is possible to have
- two simultaneous logical operations where each operation either only reads,
- or only writes. The beginning of a new read or write operation will use
- the most recently set timeout. This will not affect operations that are
- already outstanding.
- [code_core_3_timeouts_7]
- When a timeout is set, it cancels any previous read or write timeout for which
- no outstanding operation is in progress. Algorithms which loop over logical
- operations simply need to set the timeout once before the logical operation,
- it is not necessary to call `expires_never` in this case. Here we implement
- an algorithm which continuously echoes lines back, with a timeout. This example
- is implemented as a complete function.
- [code_core_3_timeouts_1f]
- [/-----------------------------------------------------------------------------]
- [heading https_get]
- It is important to note that all of the examples thus far which perform
- reads and writes with a timeout, make use of the existing networking stream
- algorithms. As these algorithms are written generically to work with any
- object meeting the stream requirements, they transparently support timeouts
- when used with `tcp_stream`. This can be used to enable timeouts for stream
- wrappers that do not currently support timeouts.
- The following code establishes an encrypted connection, writes an HTTP
- request, reads the HTTP response, and closes the connection gracefully.
- If these operations take longer than 30 seconds total, a timeout occurs.
- This code is intended to show how `tcp_stream` can be used to enable
- timeouts across unmodified stream algorithms which were not originally
- written to support timing out, and how a blocking algorithm may be written
- from asynchronous intermediate operations.
- [code_core_3_timeouts_2f]
- [endsect]
- [/-----------------------------------------------------------------------------]
- [section:rate_limiting Rate Limiting __example__]
- The
- [link beast.ref.boost__beast__basic_stream `basic_stream`]
- class template supports an additional `RatePolicy` template parameter. Objects
- of this type must meet the requirements of __RatePolicy__. They are used to
- implement rate limiting or bandwidth management. The default policy for
- `basic_stream` and `tcp_stream` is
- [link beast.ref.boost__beast__unlimited_rate_policy `unlimited_rate_policy`],
- which places no limits on reading and writing. The library comes with the
- [link beast.ref.boost__beast__simple_rate_policy `simple_rate_policy`],
- allowing for independent control of read and write limits expressed in terms
- of bytes per second. The follow code creates an instance of the basic stream
- with a simple rate policy, and sets the read and write limits:
- [code_core_3_timeouts_8]
- More sophisticated rate policies can be implemented as user-defined types which
- meet the requirements of __RatePolicy__. Here, we develop a rate policy that
- measures the instantaneous throughput of reads and writes. First we write a
- small utility class that applies an exponential smoothing function to a series
- of discrete rate samples, to calculate instantaneous throughput.
- [code_core_3_timeouts_3f]
- Then we define our rate policy object. We friend the type
- [link beast.ref.boost__beast__rate_policy_access `rate_policy_access`] to
- allow our implementation to be private, but still allow the `basic_stream`
- access to call the required functions. This lets us avoid having to write
- a cumbersome friend declaration for the `basic_stream` class template.
- Public members of rate policy objects become part of the stream object's
- interface, through a call to `rate_policy`.
- [code_core_3_timeouts_4f]
- To use our new policy we declare an instance of the stream, and then use it
- with stream algorithms as usual. At any time, we can determine the current
- read or write rates by calling into the policy.
- [code_core_3_timeouts_9]
- [endsect]
- [/-----------------------------------------------------------------------------]
|