How to get TcpStream to work in WASM?

Hello! I have been trying for some time to open a TCP (or UDP, WebSocket,… ) connection from Rust compiled to Web Assembly without any luck. These are the two approaches I have tried:

  1. Use std: use the built-in implementations available in the standard library. Unfortunately, I always get OS error 115 (Operation in progress) when I do unwrap after TcpStream::connect. In the Firefox console, this appears as a failing WebSocket.
  2. Use websocket or some other crate for WebSockets: tried websocket, ws and some others, but I could not get any of these to compile.

Since there is not yet any good documentation on wasm in general, I have come here to you to look for the solution to this problem. Should I just use JavaScript and be happy with it? Is it my lack of knowledge of the front end? How does the standard library handle TCP/UDP in wasm, where it seems there is no true access to a pure transport layer connection?

Help!

It sounds like the TcpStream is running in a nonblocking mode. It looks like emscriptn/wasm support epoll, so mio and tokio might be ways to go there.

Thanks! I will give this a shot. Though I have taken a look at mio and it currently does not support emscripten. It does not complie at all. I will have to tinker with the source code to make it work, most probably… maybe add some cfg(target_os = "emscripten") here and there.

Well, at least that is what Github exists for…

Were u able to make it work? I was planning to write WASM node js module for Websocket, currently researching about if it is possible to run WS in WASM.

@tokahuke: did you got a success to make it work?

I had to refresh my Emscripten+websockets knowledge recently, so some notes:

  • Emcripten emulates the Linux syscall interface (e.g. with things like open returning file descriptors such that when you call write it knows what to do).
  • Emscripten wraps TCP streams inside websocket streams.
  • Emscripten always returns EINPROGRESS for the Linux connect syscall, as websocket connects do not immediately return an error.
  • Assuming the connect will succeed and the connection won’t break, you can just ignore the EINPROGRESS and start using the TCP socket as normal.
  • On the other end this will look like a websocket connection, so I recommend using https://github.com/novnc/websockify to translate it to look like a normal TCP connection.

For Rust you should be using the Emscripten target to benefit from any of this (IIRC it defers to the Linux implementation in most cases, which is what you want). It’s not the end of the world to implement your own interface accessible from the WASM target, but it is extra effort.

Onto the issues - TcpStream::connect won’t work (as you’ve noticed) because of EINPROGRESS. You may end up having success with TcpStream::connect_timeout, as it connects in nonblocking mode and then uses poll to check if you’re successful.

Alternatively, use the libc crate to do your socket + connect dance, ignore EINPROGRESS and then use FromRawFd to create your new TcpStream from the file descriptor.

Once you have the TcpStream it should work pretty well in non-blocking mode.