javascriptgoogle-chromeboostbeast-websockets

How to resolve could not decode a text frame as UTF 8 though boost websocket


I write a websocket though boost, and I receive the message though the client in chrome. When I use ws, it works well, I can receive correct msg. but when I use wss, it works bad, and said could not decode a text frame as UTF 8.

the picture is what's wrong is ssl mode. wrong msg in ssl mode

c++ send msg code

  Json::Value jsonMsg;
        jsonMsg["msgType"] = Json::Value("MspServiceStartUp");
        jsonMsg["version"] = Json::Value(std::string(MSP_VERSION));
        ws_->async_write(boost::asio::buffer((void *) jsonMsg.toStyledString().data(), jsonMsg.toStyledString().size()),
                         boost::asio::bind_executor(*strand_, [&, sp](boost::system::error_code ec1,
                                                                      std::size_t bytes_transferred1) {
                             boost::ignore_unused(bytes_transferred1);
                             if (ec1) {
                                 LOG_ERR << "async write failed, ec = " << ec1 << ", msg = "
                                         << ec1.message();
                                 return;
                             }
                             // Clear the buffer
                             buffer_->consume(buffer_->size());
                             task();
                         }));
    }

js code

var ws=new WebSocket("wss://localhost.com:17801/");
ws.onopen=()=>{console.log('ws open')};
ws.onclose=()=>{console.log('ws close')};
ws.onmessage=(msg)=>{console.log('ws onMessage');console.log(msg)};

Where does this odd character come from? What is the problem? How to fix this ?


Solution

  • The problem is with sending data. async_write() ends immediately, it doesn't make a copy of data buffer, you have to ensure that data passed into boost::asio::buffer lives until the full message will be sent.

    Even if we added some delay code between async_write and ending brace {:

    async_write(boost::asio::buffer((void *) jsonMsg.toStyledString().data(), ..));
    ... some code waiting until write is completed
    }
    

    this code will not work also, because toStyledString returns string by value. So temporary string is created, string::data() is called, async_write() ends, and you have dangling pointer inside task initiated by async_write().

    Quick solution, prolong lifetime of string for example by using smart pointer:

    std::shared_ptr<std::string> buf(std::make_shared<std::string>(jsonMsg.toStyledString()));
    ws_->async_write(boost::asio::buffer(*buf),
                     boost::asio::bind_executor(*strand_, 
                     [&, sp, buf](boost::system::error_code ec1,
                                 std::size_t bytes_transferred1) 
                     {
                             boost::ignore_unused(bytes_transferred1);
                             if (ec1) {
                                 LOG_ERR << "async write failed, ec = " << ec1 << ", msg = "
                                         << ec1.message();
                                 return;
                             }
                             // Clear the buffer
                             buffer_->consume(buffer_->size());
                             task();
                     }));
    

    pass buf by boost::asio::buffer(*buf), and capture it by value inside lambda: [&,sp,buf].