Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Websocket(Jetty) : How to handle the binary data on server side which is coming in chunks

I need to setup a websocket server which can receive audio data sent by the client. I am using Jetty for this.

My handler code :

{
    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
    }

    @OnWebSocketError
    public void onError(Throwable t) {
    }

    @OnWebSocketConnect
    public void onConnect(Session session) {

    }

    @OnWebSocketMessage
    public void onMessage(String message) {
    }

    @OnWebSocketMessage
    public void onMessage(bytes [] b) {
    }

    @OnWebSocketMessage
    public void inputStream(InputStream is) {
    }
}

Since the audio file is quite large, the client is sending those in chunks. Now for every chunk, the onMessage(bytes [] b) {} method is getting invoked.

On the server side, I need to add these chunks and process the audio. How can I do that?

Also what is the difference between onMessage(bytes [] b) {} and onMessage(InputStream is) {} methods?

like image 203
shantanu Avatar asked Apr 01 '17 06:04

shantanu


People also ask

Can a single WebSocket handle text and binary?

§Request and Response Streaming WebSocket is the only transport that allows bidirectional communication over the same TCP connection (Figure 17-2): the client and server can exchange messages at will. As a result, WebSocket provides low latency delivery of text and binary application data in both directions.

Can WebSockets send binary data?

WebSockets support sending binary messages, too. To send binary data, one can use either Blob or ArrayBuffer object. Instead of calling the send method with string, you can simply pass an ArrayBuffer or a Blob .

When using WebSocket send () How do you know?

The only way to know the client received the webSocket message for sure is to have the client send your own custom message back to the server to indicate you received it and for you to wait for that message on the server. That's the ONLY end-to-end test that is guaranteed.

What is WebSocket binary?

The WebSocket protocol allows to send arbitrary binary data (not even UTF-8 or Base-64 encoded) BUT that data are encapsulated in frames whose format is defined by WebSocket protocol (see RFC6455) and has nothing to do with SSH protocol.


1 Answers

According to the onMessage javadoc, both onMessage(byte[] b) and onMessage(InputStream is) will receive the whole message:

if the method is handling binary messages:

  • byte[] or ByteBuffer to receive the whole message
  • InputStream to receive the whole message as a blocking stream

So, if you use one of these methods, Jetty will automatically reassemble the chunks and deliver the whole message to your method (as a byte[] array or an InputStream).

The maximum size of the binary message, you can receive this way, is set with the setMaxBinaryMessageSize

Also note, that you may only have one of these methods defined at your Handler class at once:

Each websocket endpoint may only have one message handling method for each of the native websocket message formats: text, binary and pong.

If you want to process the data as it goes, you should use the following method signature instead:

@OnMessage
public void processUpload(byte[] b, boolean last, Session session) {
    // process partial data here, which check on last to see if these is more on the way
}

and manually buffer your data (in memory or disk file e.t.c.)

The client, in turn, should use one of sendPartialBytes methods, to send the data chunk by chunk.


If what you observe is the "whole message" method (i.e. onMessage(byte[] b) being invoked for an each data chunk, as send by the client, this indicates that the client is not utilizing the protocol capabilities to send chunked messages, and does instead split the input data and then sends its parts as independent WS messages, effectively creating it's own application level protocol for transmitting the data in chunks.

In this case, you must either update the client (if that is an option), to make it use a regular WS protocol capabilities, or implement the same "application level protocol" the client is using.

Below is a super-simple endpoint example, which will buffer the input data until the WS socket is closed (this implies that client will close the connection once it has sent all the chunks).

@ServerEndpoint("/data")
public static class Handler {

    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    @OnMessage
    public void onMessage(byte[] message) throws IOException {
        buffer.write(message);
    }

    @OnClose
    public void onClose(Session session) throws IOException {
        System.out.println(
            buffer.toByteArray().length
        );
    }

}

This implementation also implies that a default ServerEndpointConfig.Configurator is used, so that there is exactly one Endpoint instance per connection (as documented here).

A more complex implementation might want to reuse the socket to send multiple files, and specify a mechanism to denote the start and end of each transmission (e.g. with a specially formatted message), but it all depends on how your client is implemented.

like image 72
zeppelin Avatar answered Sep 29 '22 01:09

zeppelin