I've implemented a sort of a pipe to websocket protocol. It just converts std-in/outs to websocket messages and works great. If your messages have no clear boundaries, then a direct conversion works well. However if I want some logical separation of my stdout outs, then I have a problem. For example I write in Rust -
println!("Hello, world!);
A browser can receive it as a websocket message : Hello, world!\n, however, especially on Windows, it receive it as two or more messages, like : Hello,, world!\n. Such separation brings a confusion in a message processing handler.
First idea is using Rust approach and prepend every message with a length counter, and finalize a websocket message sending only when the counter number bytes have been received from the pipe. Although such approach looks feasible, it will require to accumulate a message contents before sending it. The major drawback is that can be hardly possible in cases of a streaming.
Second approach, I use currently, is sending a complete message only when a certain byte appears in the stream. Such byte is \n currently. It works great, however if a message should include carriage return itself, or I simply send binary data, the approach won't work.
This is a fundamental problem in networking and data transfer — framing.
A pipe — or a TCP connection — has no notion of “a message” larger than a single byte. Any grouping you may observe is accidental and will break for longer messages.
WebSockets, on the other hand, does have a notion of a whole message. Therefore, when you wire up a pipe to a WebSocket connection, you have to make the choice choices:
Transfer the bytes/characters as-is, and expect the receiving side to ignore message boundaries, reconstructing the original byte-stream from arbitrary chunks. This might be appropriate if you are transporting an existing serial/TCP protocol over WebSocket — the WebSocket notion of messages is ignored because it isn't needed.
Parse the input in some fashion — collecting whole lines is one simple such parsing which is often useful for “plain text” communication, but as you note is inappropriate for binary; adding length prefixes is another.
This is a matter of protocol design. There is no single best or correct solution here; you should make the choice that benefits your actual use case — what the program on the other end of the WebSocket connection needs.