Search code examples
c++boostemscriptenwebassembly

Problem connecting websocket from c++ compiled with emscripten


Trying to connect to websocket (poco-1.9.0 samples\WebSocketServer) using c++ code compiled with emscripten. Using compiled boost 1.69 and one of common examples to connect to socket.

boost::asio::ssl::context ctxt(context::sslv23_client); 
ctxt.set_verify_mode(boost::asio::ssl::verify_none);

boost::asio::io_service svc;
tcp::resolver resolver(svc);
tcp::resolver::query query("127.0.0.1", "9980", 
    boost::asio::ip::resolver_query_base::numeric_service);

tcp::resolver::iterator i = resolver.resolve(query, ec);

boost::asio::ssl::stream<tcp::socket> s(svc, ctxt);
s.lowest_layer().connect(*i, ec); 
s.handshake(boost::asio::ssl::stream<tcp::socket>::client, ec);

Server output is as following

Request from 127.0.0.1:58152: GET / HTTP/1.1
Host: 127.0.0.1:9980
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://127.0.0.1:8887
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: binary
WebSocket connection established.
Frame received (length=0, flags=0x0).
WebSocket connection closed.

However, this code hangs after handshake. Can it be used this way or it's necessary to use async calls from asio?

Also, if there is any similar example that you know of, please share.


Solution

  • I always tell people who try WebAssembly that...

    WebAssembly (in a browser context) is JavaScript.

    Even though you code in C/C++ using Emscripten, compiled WebAssembly bytecode is run in a browser's JavaScript engine such as V8. This means that WASM code does not have any special low-level APIs beyond JavaScript APIs. Every system-level functions are emulated using JavaScript.

    What does it mean? Low-level socket control such as setting SSL versions and SSL handshaking does not make sense because your WASM code can only make use of JavaScript WebSocket API for networking, thus sockets are handled by the browser, not your WASM code.

    Instead, you can use plain BSD sockets. Emscripten will convert the BSD sockets into JavaScript WebSocket. In other words, you can't use Poco library at all.

    Like this:

    struct sockaddr_in addr;
    ing res;
    int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    unsigned long nonblocking = 1;
    fcntl(fd, F_SETFL, O_NONBLOCK);
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9980);
    if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) {
      perror("inet_pton failed");
      finish(EXIT_FAILURE);
    }
    res = connect(server.fd, (struct sockaddr *)&addr, sizeof(addr));
    

    BTW you are forced to use asynchronous (nonblocking) socket operations only, since it is JS websockets.

    Related references: