Boost膨胀算法解压缩
Boost inflate algorithm decompress
我正在使用 C++ boost 使用 Websocket 客户端开发一个简单的测试代码。我得到响应的服务器说我需要使用膨胀算法解压缩消息。我发现在 boost Websocket 库中有 deflate 选项,但它没有用。请告诉我如何将数据转换为解压缩的字符串。
#include <iostream>
#include <string>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/ssl.hpp>
#include <chrono>
using tcp = boost::asio::ip::tcp;
namespace websocket = boost::beast::websocket;
int main()
{
std::ostringstream stream;
std::string host = "real.okex.com";
auto const port = "8443";
auto const path = "/ws/v3";
boost::beast::multi_buffer buffer;
boost::asio::io_context ioc;
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
tcp::resolver resolver{ioc};
websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> wss{ioc, ctx};
ctx.set_verify_mode(boost::asio::ssl::verify_none);
tcp::resolver::results_type results = resolver.resolve(host, port);
boost::asio::connect(wss.next_layer().next_layer(), results.begin(), results.end());
// SSL handshake
wss.next_layer().handshake(boost::asio::ssl::stream_base::client);
// websocket handshake
wss.handshake(host, path);
std::cout << "connected" << std::endl;
// send request to the websocket
wss.write(boost::asio::buffer("{'op':'subscribe', 'args':['spot/ticker:ETH-USDT']}"));
// read message
wss.read(buffer);
std::cout << buffer.size() << std::endl;
buffer.consume(buffer.size());
/*
stream << boost::beast::buffers(buffer.data());
buffer.consume(buffer.size());
std::string incoming = stream.str();
std::cout << incoming << std::endl;
*/
}
谢谢!
我纠结了很久,然后我想,如果我换个服务器试试呢?
很有帮助。我拍了 echo_compressed/server.py
from Autobahn:
wget 'https://github.com/crossbario/autobahn-python/raw/master/examples/twisted/websocket/echo_compressed/server.py'
virtualenv venv && . venv/bin/activate && pip install autobahn twisted
python server.py
这会在端口 9000 上启动一个 WS 服务器。虽然它没有使用 SSL,所以我在代码中禁用了它(参见下面的 #ifdef SSL
)。
现在关键是设置permessage_deflate
扩展选项beforeWS handshake:
websocket::permessage_deflate opt;
opt.client_enable = true; // for clients
opt.server_enable = true; // for servers
s.set_option(opt);
另请注意,某些服务器要求端口名称出现在主机 header 中,而不是 运行 在标准端口上:
s.handshake(host + ":" + port, path);
现在阅读效果很好,并且如您所料缩小,例如写到 response.txt
:
beast::multi_buffer buffer;
s.read(buffer);
{
std::ofstream ofs("response.txt", std::ios::binary);
std::copy(
net::buffers_begin(buffer.data()),
net::buffers_end(buffer.data()),
std::ostreambuf_iterator<char>(ofs));
}
或者,当用 Asio streambuf 替换 multi_buffer 时,流式传输它很容易:
net::streambuf buffer;
s.read(buffer);
std::cout << &buffer;
它正在放气的证据
使用 tcpdump/Wireshark 检查流量显示了这一点。此外,高速公路记录证实了这一点:
2020-06-22 02:12:05+0200 [-] Log opened.
2020-06-22 02:12:05+0200 [-] WebSocketServerFactory starting on 9000
2020-06-22 02:12:05+0200 [-] Starting factory <autobahn.twisted.websocket.WebSocketServerFactory object at 0x7f7af3fa5710>
2020-06-22 02:12:05+0200 [-] Site starting on 8080
2020-06-22 02:12:05+0200 [-] Starting factory <twisted.web.server.Site instance at 0x7f7af3850910>
2020-06-22 02:12:11+0200 [-] WebSocket connection request by tcp4:127.0.0.1:48658
2020-06-22 02:12:11+0200 [-] WebSocket extensions in use: [PerMessageDeflate(is_server = True, server_no_context_takeover = False, client_no_context_takeover = False, server_max_window_bits = 15, client_max_window_bits = 15, mem_level = 8)]
该服务器的问题 (real.okex.com)
我真的不知道怎么回事,但服务器似乎没有发送标准响应。也许其他人可以告诉。将响应写入文件不会导致文件看起来像 zlib
压缩。
尝试过的其他工具也无法解码数据:
zlib-flate - 解压缩 < response.txt
与 python oneliner 相同:
python -c 'import zlib; import sys; sys.stdout.write(zlib.decompress(sys.stdin.read()))' < response.txt
完整列表
正如我测试的那样:
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <iostream>
#include <string>
#include <fstream>
namespace net = boost::asio;
namespace ssl = net::ssl;
namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
using tcp = net::ip::tcp;
//#define SSL
#ifdef SSL
using stream_t = websocket::stream<ssl::stream<tcp::socket>>;
#else
using stream_t = websocket::stream<tcp::socket/*, true*/>;
#endif
int main(int argc, char** argv) {
if (argc<4) {
std::cerr << "Usage: " << argv[0] << " host port path\n";
return 1;
}
std::string host = argc>=2? argv[1] : "real.okex.com";
auto const port = argc>=3? argv[2] : "8443";
auto const path = argc>=3? argv[3] : "/ws/v3";
net::io_context ioc;
ssl::context ctx{ ssl::context::sslv23 };
tcp::resolver resolver{ ioc };
#ifdef SSL
stream_t s{ ioc, ctx };
#else
stream_t s{ ioc };
#endif
ctx.set_verify_mode(ssl::verify_none);
tcp::resolver::results_type results = resolver.resolve(host, port);
net::connect(
beast::get_lowest_layer(s),
//s.next_layer().next_layer(),
results.begin());
#ifdef SSL
// SSL handshake
s.next_layer().handshake(ssl::stream_base::client);
#endif
// websocket handshake
websocket::permessage_deflate opt;
opt.client_enable = true; // for clients
opt.server_enable = true; // for servers
s.set_option(opt);
s.handshake(host + ":" + port, path);
std::cout << "connected" << std::endl;
// send request to the websocket
s.write(net::buffer("{'op':'subscribe', 'args':['spot/ticker:ETH-USDT']}"));
{
net::streambuf buffer;
s.read(buffer);
std::cout << &buffer << std::endl;
}
}
然后我运行和
在协议升级响应中,websocket 服务器应该包含一个字段“Sec-WebSocket-Extensions”,告诉客户端使用 Compression Extensions for WebSocket.
但是许多像 okex/huobi 这样的加密货币交易所的 websocket 服务器不这样做。您必须在应用程序代码中缩小消息。
您可以认为这是将 deflate/inflate 从协议层移动到应用层。
我正在使用 C++ boost 使用 Websocket 客户端开发一个简单的测试代码。我得到响应的服务器说我需要使用膨胀算法解压缩消息。我发现在 boost Websocket 库中有 deflate 选项,但它没有用。请告诉我如何将数据转换为解压缩的字符串。
#include <iostream>
#include <string>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/ssl.hpp>
#include <chrono>
using tcp = boost::asio::ip::tcp;
namespace websocket = boost::beast::websocket;
int main()
{
std::ostringstream stream;
std::string host = "real.okex.com";
auto const port = "8443";
auto const path = "/ws/v3";
boost::beast::multi_buffer buffer;
boost::asio::io_context ioc;
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
tcp::resolver resolver{ioc};
websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> wss{ioc, ctx};
ctx.set_verify_mode(boost::asio::ssl::verify_none);
tcp::resolver::results_type results = resolver.resolve(host, port);
boost::asio::connect(wss.next_layer().next_layer(), results.begin(), results.end());
// SSL handshake
wss.next_layer().handshake(boost::asio::ssl::stream_base::client);
// websocket handshake
wss.handshake(host, path);
std::cout << "connected" << std::endl;
// send request to the websocket
wss.write(boost::asio::buffer("{'op':'subscribe', 'args':['spot/ticker:ETH-USDT']}"));
// read message
wss.read(buffer);
std::cout << buffer.size() << std::endl;
buffer.consume(buffer.size());
/*
stream << boost::beast::buffers(buffer.data());
buffer.consume(buffer.size());
std::string incoming = stream.str();
std::cout << incoming << std::endl;
*/
}
谢谢!
我纠结了很久,然后我想,如果我换个服务器试试呢?
很有帮助。我拍了 echo_compressed/server.py
from Autobahn:
wget 'https://github.com/crossbario/autobahn-python/raw/master/examples/twisted/websocket/echo_compressed/server.py'
virtualenv venv && . venv/bin/activate && pip install autobahn twisted
python server.py
这会在端口 9000 上启动一个 WS 服务器。虽然它没有使用 SSL,所以我在代码中禁用了它(参见下面的 #ifdef SSL
)。
现在关键是设置permessage_deflate
扩展选项beforeWS handshake:
websocket::permessage_deflate opt;
opt.client_enable = true; // for clients
opt.server_enable = true; // for servers
s.set_option(opt);
另请注意,某些服务器要求端口名称出现在主机 header 中,而不是 运行 在标准端口上:
s.handshake(host + ":" + port, path);
现在阅读效果很好,并且如您所料缩小,例如写到 response.txt
:
beast::multi_buffer buffer;
s.read(buffer);
{
std::ofstream ofs("response.txt", std::ios::binary);
std::copy(
net::buffers_begin(buffer.data()),
net::buffers_end(buffer.data()),
std::ostreambuf_iterator<char>(ofs));
}
或者,当用 Asio streambuf 替换 multi_buffer 时,流式传输它很容易:
net::streambuf buffer;
s.read(buffer);
std::cout << &buffer;
它正在放气的证据
使用 tcpdump/Wireshark 检查流量显示了这一点。此外,高速公路记录证实了这一点:
2020-06-22 02:12:05+0200 [-] Log opened.
2020-06-22 02:12:05+0200 [-] WebSocketServerFactory starting on 9000
2020-06-22 02:12:05+0200 [-] Starting factory <autobahn.twisted.websocket.WebSocketServerFactory object at 0x7f7af3fa5710>
2020-06-22 02:12:05+0200 [-] Site starting on 8080
2020-06-22 02:12:05+0200 [-] Starting factory <twisted.web.server.Site instance at 0x7f7af3850910>
2020-06-22 02:12:11+0200 [-] WebSocket connection request by tcp4:127.0.0.1:48658
2020-06-22 02:12:11+0200 [-] WebSocket extensions in use: [PerMessageDeflate(is_server = True, server_no_context_takeover = False, client_no_context_takeover = False, server_max_window_bits = 15, client_max_window_bits = 15, mem_level = 8)]
该服务器的问题 (real.okex.com)
我真的不知道怎么回事,但服务器似乎没有发送标准响应。也许其他人可以告诉。将响应写入文件不会导致文件看起来像 zlib
压缩。
尝试过的其他工具也无法解码数据:
zlib-flate - 解压缩 < response.txt
与 python oneliner 相同:
python -c 'import zlib; import sys; sys.stdout.write(zlib.decompress(sys.stdin.read()))' < response.txt
完整列表
正如我测试的那样:
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <iostream>
#include <string>
#include <fstream>
namespace net = boost::asio;
namespace ssl = net::ssl;
namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
using tcp = net::ip::tcp;
//#define SSL
#ifdef SSL
using stream_t = websocket::stream<ssl::stream<tcp::socket>>;
#else
using stream_t = websocket::stream<tcp::socket/*, true*/>;
#endif
int main(int argc, char** argv) {
if (argc<4) {
std::cerr << "Usage: " << argv[0] << " host port path\n";
return 1;
}
std::string host = argc>=2? argv[1] : "real.okex.com";
auto const port = argc>=3? argv[2] : "8443";
auto const path = argc>=3? argv[3] : "/ws/v3";
net::io_context ioc;
ssl::context ctx{ ssl::context::sslv23 };
tcp::resolver resolver{ ioc };
#ifdef SSL
stream_t s{ ioc, ctx };
#else
stream_t s{ ioc };
#endif
ctx.set_verify_mode(ssl::verify_none);
tcp::resolver::results_type results = resolver.resolve(host, port);
net::connect(
beast::get_lowest_layer(s),
//s.next_layer().next_layer(),
results.begin());
#ifdef SSL
// SSL handshake
s.next_layer().handshake(ssl::stream_base::client);
#endif
// websocket handshake
websocket::permessage_deflate opt;
opt.client_enable = true; // for clients
opt.server_enable = true; // for servers
s.set_option(opt);
s.handshake(host + ":" + port, path);
std::cout << "connected" << std::endl;
// send request to the websocket
s.write(net::buffer("{'op':'subscribe', 'args':['spot/ticker:ETH-USDT']}"));
{
net::streambuf buffer;
s.read(buffer);
std::cout << &buffer << std::endl;
}
}
然后我运行和
在协议升级响应中,websocket 服务器应该包含一个字段“Sec-WebSocket-Extensions”,告诉客户端使用 Compression Extensions for WebSocket.
但是许多像 okex/huobi 这样的加密货币交易所的 websocket 服务器不这样做。您必须在应用程序代码中缩小消息。
您可以认为这是将 deflate/inflate 从协议层移动到应用层。