【发布时间】:2021-02-19 17:26:47
【问题描述】:
问题陈述
我正在尝试使用他们的websocket API 建立与 OKEX 交易所的 websocket 连接。我正在使用Boost::Beast websockets。
问题是OKEX的服务器没有遵循正确的permessage_deflate压缩协议,发送的消息是incorrectly deflated。所以我试图自己夸大消息。问题是它不起作用......让我发疯的是我得到的行为有些不一致。
实际代码
我的代码主要是从previously linked to thread 复制和粘贴的。为简单起见,我删除了所有预处理器宏,并对套接字值进行了硬编码。
inflate 代码取自 Raj Advani's answer here。
这是main.cpp 文件:
#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 <istream>
#include "zlib.h"
#include <iostream>
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;
using stream_t = websocket::stream<ssl::stream<tcp::socket>>;
int inflate(const void *src, int srcLen, void *dst, int dstLen) {
z_stream strm = {0};
strm.total_in = strm.avail_in = srcLen;
strm.total_out = strm.avail_out = dstLen;
strm.next_in = (Bytef *) src;
strm.next_out = (Bytef *) dst;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int err = -1;
int ret = -1;
err = inflateInit2(&strm, (15 + 32)); //15 window bits, and the +32 tells zlib to to detect if using gzip or zlib
if (err == Z_OK) {
err = inflate(&strm, Z_FINISH);
if (err == Z_STREAM_END) {
ret = strm.total_out;
}
else {
inflateEnd(&strm);
return err;
}
}
else {
inflateEnd(&strm);
return err;
}
inflateEnd(&strm);
return ret;
}
int main(int argc, char** argv) {
std::string host = "real.okex.com";
auto const port = "8443";
auto const path = "/ws/v3";
net::io_context ioc;
ssl::context ctx{ ssl::context::sslv23 };
tcp::resolver resolver{ ioc };
stream_t s{ ioc, ctx };
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());
// SSL handshake
s.next_layer().handshake(ssl::stream_base::client);
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);
// auto data_it = buffer.data().begin();
// std::cout<<"Iterating over data of size:" << buffer.data().size()<<endl; // LINE 85
// int i = 0;
// while (data_it != buffer.data().end()) {
// std::cout << "buffer data["<<i++<<"] size:" << (data_it->size())<<endl;
// data_it++;
// }
net::streambuf out_buffer;
const int error_code_out = inflate(&buffer, buffer.size(), &out_buffer, 10000000);
std::cout << "received. size:"<<buffer.size()<<" data: "<< &buffer << std::endl;
std::cout << "deflated. error?"<< error_code_out << " data: " << &out_buffer << std::endl;
}
}
代码输出+问题
通货膨胀说buffer的大小是117。我认为这是合理的,但由于某种原因,我在解压时得到Z_DATA_ERROR,让我相信还有更多的数据需要解析...... .
所以我查阅了net::streambuf 的文档,发现显然有多个可以读取的缓冲区,所以也许我只使用了一个缓冲区?我运行了注释掉的代码(不包括中间的LINE 85 行)并且它从未通过循环......我认为这很奇怪。我输入那条线,然后突然间我有了几百个缓冲区? (截断的)输出类似于:
connected.
Iterating over data of size:117
buffer data[0] size:117
buffer data[1] size:72198326954657960
buffer data[2] size:140735485986592
buffer data[3] size:140618848326656
buffer data[4] size:140618848326656
.. many more lines of this...
buffer data[121] size:7089075335985461349
buffer data[122] size:3472329396561475632
buffer data[123] size:8747116609081390898
buffer data[124] size:3472329396561475632
buffer data[125] size:3472387902693336678
buffer data[126] size:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
如您所见,它崩溃了。我不知道发生了什么。我现在不知道如何解码streambuf ......而the documentation 似乎假设了很多我没有的背景知识。我尝试使用buffer.data(),将buffer 转换为char* 数组,所有这些都导致我出现完全相同的行为......
不知道该怎么办。欢迎任何帮助
供参考:Python 实现
import websockets
import asyncio
import zlib
def inflate(data):
decompress = zlib.decompressobj(-zlib.MAX_WBITS)
inflated = decompress.decompress(data)
inflated += decompress.flush()
return inflated
async def main():
client = await websockets.connect("wss://real.okex.com:8443/ws/v3")
await client.send("{'op':'subscribe', 'args':['spot/ticker:ETH-USDT']}")
r = await client.recv()
print(len(r), r)
print(inflate(r))
if __name__ == '__main__':
asyncio.run(main())
【问题讨论】:
标签: c++ boost websocket boost-beast