求可运行的C/C++ WebSocket客户端示例,对接雅虎金融推送接口
问题排查
- 缺少雅虎服务端必填的
Origin请求头:雅虎金融WebSocket端点会校验Origin字段合法性,原代码仅设置了User-Agent,未携带Origin: https://finance.yahoo.com,服务端校验失败直接主动关闭连接,即报错信息中提到的WebSocket流优雅关闭问题。 - 公共测试服务
echo.websocket.org已正式下线,因此访问无响应属于正常情况,无需排查代码问题。 - 原代码逻辑仅读取1次消息就主动关闭连接,不符合雅虎金融持续推送行情流的使用场景。
- 测试请求内容错误:向雅虎端点发送了
Hello, world!字符串,该服务仅接受指定格式的订阅JSON报文,非法报文也会触发服务端主动断连。
修改后的可运行代码
#include "example/common/root_certificates.hpp" #include <boost/beast/core.hpp> #include <boost/beast/ssl.hpp> #include <boost/beast/websocket.hpp> #include <boost/beast/websocket/ssl.hpp> #include <boost/asio/connect.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/ssl/stream.hpp> #include <cstdlib> #include <iostream> #include <string> namespace beast = boost::beast; // from <boost/beast.hpp> namespace http = beast::http; // from <boost/beast/http.hpp> namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp> namespace net = boost::asio; // from <boost/asio.hpp> namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> int main(int argc, char** argv) { try { if(argc != 4) { std::cerr << "Usage: websocket-client-sync-ssl <host> <port> <subscribe_json>\n" << "Example:\n" << " websocket-client-sync-ssl streamer.finance.yahoo.com 443 " "'{\"subscribe\":[\"INTC\"]}'\n"; return EXIT_FAILURE; } std::string host = argv[1]; auto const port = argv[2]; auto const sub_msg = argv[3]; net::io_context ioc; ssl::context ctx{ssl::context::tlsv12_client}; load_root_certificates(ctx); // 测试用可临时关闭SSL校验,生产环境请删除下行 // ctx.set_verify_mode(ssl::verify_none); tcp::resolver resolver{ioc}; websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx}; auto const results = resolver.resolve(host, port); auto ep = net::connect(get_lowest_layer(ws), results); if(! SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str())) throw beast::system_error( beast::error_code( static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()), "Failed to set SNI Hostname"); host += ':' + std::to_string(ep.port()); ws.next_layer().handshake(ssl::stream_base::client); ws.set_option(websocket::stream_base::decorator( [](websocket::request_type& req) { req.set(http::field::user_agent, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-client-sync-ssl"); // 新增必填Origin头 req.set(http::field::origin, "https://finance.yahoo.com"); })); ws.handshake(host, "/"); ws.write(net::buffer(std::string(sub_msg))); // 循环读取推送流 beast::flat_buffer buffer; int msg_count = 0; while(true) { buffer.consume(buffer.size()); ws.read(buffer); msg_count++; std::cout << msg_count << " -- " << beast::buffers_to_string(buffer.data()) << std::endl; } } catch(std::exception const& e) { std::cerr << "Error: " << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }
编译运行方法
- 先将Boost官方示例目录中的
example/common/root_certificates.hpp文件放到当前代码同目录下 - 执行编译命令:
g++ boost_test.cpp -o websocket-client-sync-ssl -lboost_system -pthread -lssl -lcrypto
- 执行运行命令:
./websocket-client-sync-ssl streamer.finance.yahoo.com 443 '{"subscribe":["INTC"]}'
运行后即可看到持续输出的雅虎行情推送数据,按Ctrl+C即可停止程序。
关于Firefox WebSocket模块的问题
Firefox源码中的WebSocket模块与Firefox自身的netwerk网络栈、XPCOM组件框架深度绑定,无法独立剥离使用,也没有公开的成熟剥离实践案例,不建议尝试复用该模块实现C++ WebSocket客户端,使用Boost.Beast、websocketpp等成熟第三方库的开发成本远低于剥离Firefox模块的成本。
内容的提问来源于stack exchange,提问作者Michael Auten
相关产品推荐
相关产品推荐

