boost库实现websocket分别传输二进制与字符串的心得

 一、背景

        最近的工作有一项任务就是 websocket 兼容 json 字符串和音视频流,之前都没有接触过音视频方向,网上找了找也没找到合适的博客,大部分都是 js 的代码实现,就干脆自己看 boost 库源码找找方法,正好记录一下这一次的学习过程。

二、过程

        因为我的 websocket 协议是用 boost 库实现的,所以就从 boost 库中找对应的实现方法。

1.寻找分辨二进制与字符串类型的函数:

        在 boost 的 stream.hpp 头文件中,我找到了设置 opcode 值的函数:

template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
binary(bool value)
{
    impl_->wr_opcode = value ?
        detail::opcode::binary :
        detail::opcode::text;
}

        该函数可以设置传输二进制流与字符串,与之对应的还有一个 text 函数:

template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
text(bool value)
{
    impl_->wr_opcode = value ?
        detail::opcode::text :
        detail::opcode::binary;
}

        虽然使用一个函数传入 true or false 就可以实现切换,但估计 boost 库开发者想更有可读性就分别实现了 binary 和 text 的函数供大家使用吧。

        除此之外还有获取该 opcode 值的函数:

template<class NextLayer, bool deflateSupported>
bool
stream<NextLayer, deflateSupported>::
binary() const
{
    return impl_->wr_opcode == detail::opcode::binary;
}

template<class NextLayer, bool deflateSupported>
bool
stream<NextLayer, deflateSupported>::
text() const
{
    return impl_->wr_opcode == detail::opcode::text;
}

        在这里博主踩了一个坑,详细后面再讲。

2.尝试实现验证

        在找到对应的函数之后,博主就准备实现一个 demo 测试一下,看看功能满不满足需求。

        博主先是在客户端发送数据时进行对应的切换操作:

if (msg.type == 0) //字符串数据
{
    ws_->text(true); //将websocket opcode设置为text模式
    //对应的数据操作
}
else if (msg.type == 1) //二进制数据 
{
    ws_->binary(true); //将websocket opcode设置为binary模式
    //对应的数据操作
}

        然后在服务端里使用 binary() 与 text() 函数获取对应的 opcode 进行报文的解析:

if (ws_.text()) // 处理字符串数据
{
    std::cout << "text() result is true..." << std::endl;
    std::string str = boost::beast::buffers_to_string(buffer_.data());

    // demo中只是将数据打印出来查看是否正确
    std::cout << "receive text data : " << str << std::endl;
    buffer_.consume(buffer_.size());
}
if (ws_.binary()) // 处理二进制数据
{
    std::cout << "binary() result is true..." << std::endl;
    std::string str = boost::beast::buffers_to_string(buffer_.data());
    uint8_t *binaryData = (uint8_t *)str.c_str();
    int length = str.size();
    std::cout << "receive binary data : " << std::endl;
    for (int i = 0; i < length; i++)
    {
        std::cout << binaryData[i] << " ";
    }
    std::cout << std::endl;
    buffer_.consume(buffer_.size());
}

        实现之后运行结果如下:

        二进制数据被识别成字符串了。通过进一步排查,发现无论是发送二进制数据还是字符串数据,都走到 ws_text() 分支里,如图所示:

         显然, text() 函数与 binary() 函数并不能获取到报文的 opcode 进行操作,而是统一默认为字符串了,这不符合我的需求。

3.解决办法

        既然客户端可以通过 binary(bool) 与 text(bool) 函数修改发送数据的模式,服务端也能获取对应的模式才对,所以我又重新看了看源码,发送在 stream.hpp 头文件中还有一个函数 got_binary() :

template<class NextLayer, bool deflateSupported>
bool
stream<NextLayer, deflateSupported>::
got_binary() const noexcept
{
    return impl_->rd_op == detail::opcode::binary;
}

        将该函数与 binary() 函数比较了一下,发现 binary() 函数是 return wr_opcode 的,而 got_binary() return 的是 rd_op ,即 binary() 函数是查看当前端发送的模式,而 got_binary() 是获取当前端接收的模式。那么,一切也就水落石出了。

if (ws_.got_binary()) // 接收的二进制数据
{
    std::cout << "got_binary() result is true..." << std::endl;
    std::string str = boost::beast::buffers_to_string(buffer_.data());
    uint8_t *binaryData = (uint8_t *)str.c_str();
    int length = str.size();
    std::cout << "receive binary data : " << std::endl;
    for (int i = 0; i < length; i++)
    {
        printf("%#x ", binaryData[i]);
    }
    std::cout << std::endl;
    buffer_.consume(buffer_.size());
}
else // 接收的字符串数据
{
    std::cout << "text() result is true..." << std::endl;
    std::string str = boost::beast::buffers_to_string(buffer_.data());
    std::cout << "receive text data : " << str << std::endl;
    buffer_.consume(buffer_.size());
}

        修改代码后,运行结果也如我所期望的:

        分别对应处理二进制与字符串数据,满足我的需求。

三、心得

        看 boost 库源码让我受益良多,我所学习的只是 boost 库的冰山一角,前进的路还很长,就以此篇博客作为我的学习之路的开端吧。

        这篇博客只是我的一点小小的感悟和体会,做一个抛砖引玉之用,如果有不同的见解,可以一起交流探讨,互相学习进步。

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WebSocket 协议可以传输二进制数据和文本数据。在处理二进制数据时,我们需要使用 BinaryWebSocketHandler 类来处理二进制数据。 具体步骤如下: 1. 创建 BinaryWebSocketHandler 类并重写 handleBinaryMessage 方法来处理二进制数据。 ```java public class MyBinaryWebSocketHandler extends BinaryWebSocketHandler { @Override public void handleBinaryMessage(WebSocketSession session, BinaryMessage message) { // 处理二进制消息 } } ``` 2. 修改 WebSocketConfig 类中注册 WebSocket 处理器的方法,将处理器修改为 BinaryWebSocketHandler 类型,并指定 WebSocket 路径。 ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(binaryWebSocketHandler(), "/ws").setAllowedOrigins("*"); } @Bean public WebSocketHandler binaryWebSocketHandler() { return new MyBinaryWebSocketHandler(); } } ``` 3. 在前端页面中使用 WebSocket 发送二进制数据。 ```javascript var socket = new WebSocket("ws://localhost:8080/ws"); socket.binaryType = "arraybuffer"; socket.onopen = function() { // 连接成功 }; socket.onmessage = function(event) { var data = new Uint8Array(event.data); // 处理二进制消息 }; socket.onclose = function() { // 连接关闭 }; var data = new Uint8Array([1, 2, 3, 4]); socket.send(data.buffer); ``` 在前端页面中,我们需要通过设置 socket.binaryType = "arraybuffer" 来告诉 WebSocket 接收二进制数据。在接收到二进制数据时,我们需要将 event.data 转换为 Uint8Array 类型来处理二进制数据。 注意:在发送二进制数据时,需要通过 data.buffer 来获取 ArrayBuffer 类型的数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值