Sending Messages
This chapter will dive into the details of how to send messages to WebSocket endpoints.
The API for sending messages is the same for annotated as well as programmatic endpoints which in contrast to receiving messages (next chapter) which are handled differently for different endpoints
As already stated, the Java WebSocket API supports binary, text, custom Java objects and ping-pong message payloads. These message types can be sent using various styles/modes
Asynchronous
Synchronous
Partial, and
Streaming
As you might guess, this leads to a lot of possible permutations and combinations for sending messages (and can be quite confusing at times). Hopefully, things should be clear by the end of this chapter
Send modes: a quick primer
Before we dive into the nitty gritty, it'll be good to have an overview of the different message transmission modes
Mode
Description
Synchronous
the client sending the message is blocked until the process is completed (or an error occurs)
Asynchronous
client thread is released immediately and it can track the process using a Future object or a callback implementation
Partial
the message is delivered in parts, the client needs to keep track of them and tell the API when its done sending all the parts
Streaming
makes use of Java character/output stream to send messages
Sending text messages
Synchronous
This is the most easy-to-understand method. Just make used of the public void sendText(String msg)
in RemoteEndpoint.Basic
interface
Asynchronous
This mode is handled by the RemoteEndpoint.Async
interface which exposes two methods
public Future<Void> sendText(String msg)
: returns ajava.util.concurrent.Future
objectpublic void sendText(String msg, SendHandler handler)
: allows the user to provide a callback handler implementation
Partial
Sending messages in part can be done by using an overloaded version of the sendText
method in the RemoteEndpoint.Basic
interface. The process is synchronous in nature
Streaming
One can stream textual (character) data to a java.io.Writer
provided by the public void getSendWriter()
in RemoteEndpoint.Basic
. Any of the overloaded write
methods in Writer
can be used
Summary
Here is a table summarizing possible message sending combinations for text data
Sending style for text messages
Method signature
Synchronous
public void sendText(String msg)
Asynchronous
public Future<Void> sendText(String msg)
, void sendText(String msg, SendHandler callback)
Partial
public void sendText(String part, boolean isLast)
Streaming
public void getSendWriter().write(String msg)
Sending binary data
Handling (sending) Binary data is similar to String as far as the API is concerned. The only (obvious) difference being the data type - ByteBuffer
in case of binary data as opposed to String
for textual data. The supported modes are also the same (as for text data)
Synchronous
Asynchronous
Partial
Streaming
Use the getSendStream()
method on RemoteEndpoint.Basic
to get an OutputStream
and use any of the overloaded write methods to transmit binary data
Summary
Here is the gist
Sending style for binary messages
Method signature
Synchronous
public void sendBinary(ByteBuffer data)
Asynchronous
public Future<Void> sendBinary(ByteBuffer data)
,public void sendBinary(ByteBuffer data, SendHandler callback)
Partial
public void sendBinary(ByteBuffer part, boolean isLast)
Streaming
public void getSendStream().write(byte[] data)
Sending Java objects
More often than not, your business logic will internally deal with objects rather than their raw binary or textual representations. As mentioned earlier, WebSocket supports text and binary data as it on-wire format. Thus, Java objects within your code would need to be transformed into their text or binary forms for them to be sent over a WebSocket connection.
Similar to what we saw in case of native (binary and text) messages, the RemoteEndpoint.Basic
and RemoteEndpoint.Async
interfaces contain appropriate methods which support Java object transmission - both synchronously and asynchronously.
Here is a quick peek
Sending style for (Java) objects
Method signature
Synchronous
public void sendObject(Object obj)
Asynchronous
public Future<Void> sendObject(Object obj)
, public void sendObject(Object obj, SendHandler callback)
The question is -
how does a
java.lang.Object
representation get converted into text or binary format ?
This is done with the help of an Encoder. A javax.websocket.Encoder
encapsulates the logic to convert a message from a Java object into an on-wire format supported by the WebSocket protocol (i.e. text or binary). Before diving into Encoders, let's look at a code sample for sending stock prices (represents as a StockQuote
java object) asynchronously
The synchronous counterpart is very simple - you just need to use the
sendObject
method onRemoteEndpoint.Basic
WebSocket Encoders: the details
Take a look at the diagram below to visualize how Encoders work at runtime. It should be relatively easy to grasp what's going on
A summary of Encoders and their types
Encoder Type
Description
Encoder
The top level interface for different types of Encoders
Encoder.Text<T>
Transforms a custom Java object (of type T) to a textual (java.lang.String
) format
Encoder.Binary<T>
Transforms to transform a custom Java object (of type T) to a binary (java.nio.ByteBuffer
) format
A sample to demonstrate how an Encoder
implementation would look like for a Java object which represents stock prices
Streaming your Java objects
Native text and binary messages can be sent as streams - as explained in the previous sections. Java objects can also be transmitted in a streaming style. The respective methods are not directly exposed via RemoteEndpoint.Basic
or RemoteEndpoint.Async
. It's actually handled via the Encoder
implementation
Encoder Type
Description
Encoder.TextStream<T>
Converts a custom Java object (of type T) and transmit it as a character stream (using java.io.Writer
)
Encoder.BinaryStream<T>
Converts to transform a custom Java object (of type T) and transmit it as a binary stream (using java.io.OutputStream
)
This example should help
Thus, if you need to send your Java objects as a binary or character stream, you would need implement and register an appropriate Encoder corresponding to your Java type and the rest will be handled by the WebSocket runtime (your Encoder will be automatically invoked)
What about Java primitives ?A WebSocket implementation provides default encoders for Java primitive (int-Integer, long-Long, double-Double etc.) data types. It is possible to write a custom encoder for any of these in order to override the default ones
Exchanging health status (ping-pong) messages
Exchaning ping-pong messages is a way to check up on the health status of the connection b/w a pair of WebSocket peers
Ping/Pong
Description
Ping
A health check request message. The API does not provide an object corresponding to this message (its a byte buffer)
Pong
Response to a health check status, represented by javax.websocket.PongMessage
. It can also be used as a one-way heartbeat message (without the ping message being involved)
Common semantics for Ping-Pong messages
They are nothing but binary data and take the form of a
ByteBuffer
(as explained earlier) as far the API is concernedThey cannot be larger than 125 bytes (as is the case with WebSocket Control Frames in general) - these are just health-check messages and not meant for core/application level data exchange b/w WebSocket endpoints
Capability to send ping and pong messages are defined by the top level javax.websocket.RemoteEndpoint
interface which means that they are inherited by its RemoteEndpoint.Basic
and RemoteEndpoint.Async
This means that both these messages can be sent in a sync and async manner
Sending style for Ping and Pong messages
Method signature (ping)
Method signature (pong)
Synchronous & Asynchronous
void sendPing(ByteBuffer ping)
void sendPong(ByteBuffer pong)
Additional notes
A Ping message is only meant to be sent (not recieved) as opposed to Pong, which can be sent and recieved
One does not need to write logic to explicitly return a pong message in response to a ping - the Java WebSocket API implementation will do that for you automatically
A Pong message can also be used as a self inititated heart beat message (not just in response to ping)
Asynchronous timeouts
Throughout this lesson, we have seen strategies of being able to send messages in an asynchronous manner which avoid blocking the sending thread. This is a great where your solution needs to scale in order to support a large number of clients.
But, is there limit on how long can we wait for the asynchronous process to complete ?
The answer is yes
Timeout support in the API
first and foremost, there is a notion of a timeout and this can be configured using the
setSendTimeout
method in theRemoteEndpoint.Async
interfacesecondly, the failure result manifests itself using the
Future
object orSendResult
How do timeouts manifest ?
In case you are using the SendHandler
i.e. the callback handler route, the timeout exception details will be available via SendResult.getException()
If you chose to use Future
to track the completion, calling it's get
method will result in a java.util.concurrent.ExecutionException
Before we proceed
.. a quick refresher. The below table provides a quick preview of which mode is supported for which message type
Sending style
Text
Binary
Java object
Pong
Ping
Synchronous
y
y
y
y
y
Asynchronous
y
y
y
y
y
Partial
y
y
n
n
n
Streaming
y
y
y
n
n
The next lesson ...
... will dive into the other half of the message exchange process i.e. Receiving Messages
Last updated