Tommo Tommo - 1 month ago 17
Java Question

WebSocket with multiple ClientEndpoints and binary messages

Overview:

I currently have a fully functioning mobile application that communicates with a Java server running on Windows via WebSockets. The ServerEndpoint is running off of an AWS machine. The Java server is a ClientEndpoint running on a private network, and there can be multiple app users at any given time that are also ClientEndpoints.

I currently have everything working properly when it comes to sending String messages back and forth, as the ServerEndpoint is maintaining the Java server Session along with the mobile application Sessions. Almost all communication is initiated via the mobile applications at which time a JSON String is sent to the Java server session to be able to maintain which mobile application session should receive the return message.

I am now trying to implement functionality to be able to send PDF files via binary messages from the Java server to the mobile applications in the same fashion. The mobile applications are developed using Codename One, and they do not provide the ability to increase the maximum binary message size, so I seem to be limited to 8kb (8,192 bytes) per binary message. This is fine as I am sending multiple binary messages and rebuilding the file within the mobile app.

I currently have this working in the simplest case where there is only one mobile application user at a time requesting a PDF file.

Current Flow:

App A sends JSON message to ServerEndpoint which then forwards message to Java server A. Java server A sends JSON message back to ServerEndpoint which forwards message back to App A. The JSON objects contain the session id to send the messages back to. The ServerEndpoint needs to know which App Session to send messages to that come from the Java server.
When it comes to binary messages, App A sends a String message to the ServerEndpoint which then forwards message to Java server A. Java server A now needs to send multiple binary messages back to the ServerEndpoint which ALL must get forwarded to App A for the file to be rebuilt properly. This is the part that I'm not sure how to handle. It currently works fine if there is only one App session, but if multiple App sessions were to request files at the same time, it would not work properly.


Problem:

When sending String messages, it is easy enough to pass the callback mobile application session id as part of the JSON object back and forth to ensure the correct mobile application session gets the response. When it comes to sending the binary messages, what is the best way to guarantee that if users A and B request a file at the same time, that user A gets the file he/she requested and user B gets the file he/she requested?

Answer

You should be able to track the session without having to pass a session ID. For example, see this sample web service endpoint.

Notice how you can pass a Session object as the first parameter with the @OnMessage annotation.

You can use this to know who you're talking to.

One other small point/question. Your statement "The mobile applications are developed using Codename One, and they do not provide the ability to increase the maximum binary message size," seems to suggest that other platforms do allow you to set a maximum binary size on the client. AFAIK this is not part of the WebSocket spec, and isn't implemented by any browser. What other implementations do allow you to set the max size?

EDIT In response to your refinement of the question:

There are multiple ways to skin this cat. One thought is that you only need to chunk the "last mile" (between the middleman and the client). Since the middleman and the end server are JavaEE servers, you have control over the buffer size, and could potentially send the full file in one shot (within reason). Then just do the chunking in the middle server, which will know which client he is dealing with.

Another option is to create your own pseudo protocol. You're returning byte arrays, but you could choose to allocate the first n-bytes of each chunk to contain metadata, which the middleman would parse out. This metadata could be used to tell the middleman which client the chunk is destined for.