[/
    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 握手]

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

[heading 客户端角色]

WebSocket 会话的建立过程如下：客户端在已建立的连接上发送 HTTP/1.1 [@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade] 请求，服务器则返回相应响应，表示接受该升级请求。该 Upgrade 请求必须包含 [@https://tools.ietf.org/html/rfc7230#section-5.4 Host] 字段以及所请求资源的 [@https://tools.ietf.org/html/rfc7230#section-5.3 目标]。由实现创建并发送的典型 HTTP Upgrade 请求如下所示：

[table WebSocket HTTP 升级请求
[[线路格式][描述]]
[[
```
    GET / HTTP/1.1
    Host: www.example.com
    Upgrade: websocket
    Connection: upgrade
    Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
    Sec-WebSocket-Version: 13
    User-Agent: Boost.Beast/216
```
][主机和目标参数会分别填入所生成的 HTTP 请求中，分别对应 Host 字段和请求目标。密钥由实现自行生成。如果调用方需要添加、修改或检查字段，可以在流上设置修饰器选项（详见下文）。
]]]

[link beast.ref.boost__beast__websocket__stream `websocket::stream`] 的成员函数 [link beast.ref.boost__beast__websocket__stream.handshake `handshake`] 和 [link beast.ref.boost__beast__websocket__stream.async_handshake `async_handshake`] 用于发送包含所需主机和目标字符串的请求。以下代码先连接到通过主机名解析获得的 IP 地址，然后以客户端角色执行 WebSocket 握手。

[code_websocket_2_1]

当客户端收到服务器返回的表示升级成功的 HTTP Upgrade 响应时，调用方可能希望对收到的 HTTP 响应消息进行额外验证。例如，检查基本认证质询的响应是否有效。为此，handshake 成员函数的重载版本允许调用方将收到的 HTTP 消息存储到一个输出引用参数中，该参数类型为 [link beast.ref.boost__beast__websocket__response_type `response_type`]，用法如下：

[code_websocket_2_2]

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

[heading 服务器角色]

对于接受入站连接的服务器，[link beast.ref.boost__beast__websocket__stream `websocket::stream`] 可以读取入站升级请求并自动回复。如果握手满足要求，流会返回一个状态码为 `101 Switching Protocols` 的升级响应。如果握手不符合要求，或超出调用方先前设置的流选项所允许的参数范围，流会返回一个带有错误状态码的 HTTP 响应。根据 keep-alive 设置，连接可能会保持打开状态，以便后续再次尝试握手。当接收到升级请求握手时，实现创建并发送的典型 HTTP 升级响应如下所示：

[table WebSocket 升级 HTTP 响应
[[线路格式][描述]]
[[
    ```
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    Server: Boost.Beast
    ```
][[@https://tools.ietf.org/html/rfc6455#section-11.3.3 `Sec-WebSocket-Accept`] 字段值按照 WebSocket 协议规定的方式根据请求生成。
]]]

[link beast.ref.boost__beast__websocket__stream `stream`] 的成员函数 [link beast.ref.boost__beast__websocket__stream.accept `accept`] 和 [link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`] 用于从已连接入站对端的流中读取 WebSocket HTTP 升级请求握手，然后发送 WebSocket HTTP 升级响应，用法如下：

[code_websocket_2_3]

[heading 握手缓冲机制]

服务器可能会先读取流中的数据，稍后才决定将这些已缓冲的字节作为 WebSocket 升级请求来处理。为此，该库提供了 [link beast.ref.boost__beast__websocket__stream.accept `accept`] 和 [link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`] 的重载版本，它们接受一个额外的缓冲区序列参数。

在本示例中，服务器先将初始 HTTP 请求头部读入动态缓冲区，随后使用缓冲数据尝试进行 WebSocket 升级。

[code_websocket_2_4]

[heading 检查 HTTP 请求内容]

当实现同时支持 WebSocket 的 HTTP 服务器时，服务器通常需要先读取客户端发来的 HTTP 请求。若要判断该请求是否为 WebSocket 升级请求，可以使用 [link beast.ref.boost__beast__websocket__is_upgrade `is_upgrade`] 函数。

一旦确认 HTTP 请求是 WebSocket 升级请求，就可以使用 [link beast.ref.boost__beast__websocket__stream.accept `accept`] 和 [link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`] 的额外重载版本，这些重载接受整个 HTTP 请求头部对象来完成握手。通过手动读取请求，程序既能处理普通 HTTP 请求，也能处理升级请求。同时，程序还可以基于 HTTP 字段（例如基本认证）来实施策略控制。以下示例先使用 HTTP 算法读取请求，然后将其传递给新构造的流：

[code_websocket_2_5]

[heading 子协议]

WebSocket 协议支持子协议的概念。如果客户端请求使用某个子协议，它会在初始 WebSocket 升级 HTTP 请求中设置 [@https://tools.ietf.org/html/rfc6455#section-11.3.4 Sec-WebSocket-Protocol] 头部。服务器需要解析该头部，并从中选择一个协议接受。服务器通过在响应头部中设置 [@https://tools.ietf.org/html/rfc6455#section-11.3.4 Sec-WebSocket-Protocol] 头部来指示所选协议。

这可以通过 [link beast.ref.boost__beast__websocket__stream_base__decorator `decorator`] 来实现。

以下代码演示服务器如何读取 HTTP 请求，识别其为 WebSocket 升级请求，并在执行 WebSocket 握手之前检查是否存在匹配的优先子协议：

[code_websocket_2_6]


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

[endsect]
