<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Asio on Mer's Blog</title><link>https://merisky.top/tags/asio/</link><description>Recent content in Asio on Mer's Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 29 May 2023 11:38:32 +0800</lastBuildDate><atom:link href="https://merisky.top/tags/asio/index.xml" rel="self" type="application/rss+xml"/><item><title>C++20 Asio With Boost 获取 B 站徽章（直播间牌子）</title><link>https://merisky.top/posts/173569173/</link><pubDate>Sat, 25 Feb 2023 14:17:24 +0000</pubDate><guid>https://merisky.top/posts/173569173/</guid><description>&lt;p&gt;本文介绍了如何使用 C++20 with Asio (Boost 版本) 完成一个简单的客户端用于获取 B 站徽章。&lt;/p&gt;&#10;&lt;p&gt;强烈推荐昨天发现的一个视频 《Why C++20 is the Awesomest Language for Network Programming》，可以去油管上搜一下，总时长一个小时，比较长但是讲的很好，听的巨舒服，上次有这种感觉还是听那个 c10k 问题的视频。&lt;/p&gt;&#10;&lt;p&gt;总之先看一下程序执行的效果：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;➜ bili-medal git:(master) ./build/main &amp;#123;my_cookie&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;当前登录账号的 mid 为 29248492&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;当前登陆账号所佩戴的直播间徽章为 &lt;span class="string"&gt;&amp;quot;ASAKI&amp;quot;&lt;/span&gt;, 等级为 15 级&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;贴一下主要逻辑：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;awaitable&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;&amp;gt; &lt;span class="title"&gt;start&lt;/span&gt;&lt;span class="params"&gt;(asio::io_context &amp;amp;ctx, &lt;span class="type"&gt;const&lt;/span&gt; std::string &amp;amp;cookie)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;const&lt;/span&gt; std::string host = &lt;span class="string"&gt;&amp;quot;api.bilibili.com&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 解析域名&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; [e1, endpoint] = &lt;span class="keyword"&gt;co_await&lt;/span&gt; tcp::&lt;span class="built_in"&gt;resolver&lt;/span&gt;(ctx).&lt;span class="built_in"&gt;async_resolve&lt;/span&gt;(host, &lt;span class="string"&gt;&amp;quot;https&amp;quot;&lt;/span&gt;, use_nothrow_awaitable);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// ssl_ctx 用于建立 https 连接&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ssl::context ssl_ctx &amp;#123;ssl::context::sslv23&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ssl_socket socket &amp;#123;ctx, ssl_ctx&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;static_assert&lt;/span&gt;(std::is_same_v&amp;lt;std::&lt;span class="type"&gt;decay_t&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;decltype&lt;/span&gt;(socket.&lt;span class="built_in"&gt;next_layer&lt;/span&gt;())&amp;gt;, tcp::socket&amp;gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 建立 tcp 连接&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ssl_ctx.&lt;span class="built_in"&gt;set_default_verify_paths&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; [e2, _] = &lt;span class="keyword"&gt;co_await&lt;/span&gt; asio::&lt;span class="built_in"&gt;async_connect&lt;/span&gt;(socket.&lt;span class="built_in"&gt;lowest_layer&lt;/span&gt;(), endpoint, use_nothrow_awaitable);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// https 握手，建立 https 连接&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; [e3] = &lt;span class="keyword"&gt;co_await&lt;/span&gt; socket.&lt;span class="built_in"&gt;async_handshake&lt;/span&gt;(ssl::stream_base::client, use_nothrow_awaitable);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::string mid;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 创建并发送一个 api 请求到 B 站服务器，获得返回结果并解析 mid 字段&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; request = &lt;span class="built_in"&gt;make_request&lt;/span&gt;(http::verb::get, host, &lt;span class="string"&gt;&amp;quot;/x/web-interface/nav&amp;quot;&lt;/span&gt;, cookie);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; response = &lt;span class="keyword"&gt;co_await&lt;/span&gt; &lt;span class="built_in"&gt;async_send_request&lt;/span&gt;(socket, request);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; data = json::&lt;span class="built_in"&gt;parse&lt;/span&gt;(beast::&lt;span class="built_in"&gt;buffers_to_string&lt;/span&gt;(response.&lt;span class="built_in"&gt;body&lt;/span&gt;().&lt;span class="built_in"&gt;data&lt;/span&gt;()));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; mid = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;mid&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;当前登录账号的 mid 为 &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; mid &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::string medal_id, medal_name, medal_level;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 创建并发送一个 api 请求到 B 站服务器，获得返回结果并解析直播间徽章&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; request = &lt;span class="built_in"&gt;make_request&lt;/span&gt;(http::verb::get, host, &lt;span class="string"&gt;&amp;quot;/x/space/acc/info?mid=&amp;quot;&lt;/span&gt;+mid, cookie);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; response = &lt;span class="keyword"&gt;co_await&lt;/span&gt; &lt;span class="built_in"&gt;async_send_request&lt;/span&gt;(socket, request);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; data = json::&lt;span class="built_in"&gt;parse&lt;/span&gt;(beast::&lt;span class="built_in"&gt;buffers_to_string&lt;/span&gt;(response.&lt;span class="built_in"&gt;body&lt;/span&gt;().&lt;span class="built_in"&gt;data&lt;/span&gt;()));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; medal_id = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;fans_medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal_id&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; medal_level = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;fans_medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; medal_name = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;fans_medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal_name&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;当前登陆账号所佩戴的直播间徽章为 &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; medal_name &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;, 等级为 &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; medal_level &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot; 级&amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;整体流程我觉得是比较清晰的，基本上对 https 请求过程有所了解的话应该能容易能理解这里做了什么。&lt;/p&gt;</description></item></channel></rss>