WebSocket.
<h3>Create WebSocketFactory</h3>
<p>
WebSocketFactory is a factory class that creates
WebSocket instances. The first step is to create a
WebSocketFactory instance.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Create a WebSocketFactory instance.</span>
WebSocketFactory factory = new WebSocketFactory();</pre>
</blockquote>
<p>
By default, WebSocketFactory uses SocketFactory.getDefault() for
non-secure WebSocket connections (ws:) and SSLSocketFactory.getDefault() for secure WebSocket connections (wss:). You can change this default behavior by using
WebSocketFactory.setSocketFactory method, WebSocketFactory.setSSLSocketFactory method and WebSocketFactory.setSSLContext method. Note that you don’t have to call a setSSL* method at all if you use the default SSL configuration.
Also note that calling setSSLSocketFactory method has no
meaning if you have called setSSLContext method. See the
description of WebSocketFactory.createSocket(URI) method for
details.
</p>
<p>
The following is an example to set a custom SSL context to a
WebSocketFactory instance. (Again, you don’t have to call a
setSSL* method if you use the default SSL configuration.)
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Create a custom SSL context.</span>
SSLContext context = <a href="https://gist.github.com/TakahikoKawasaki/d07de2218b4b81bf65ac"
>NaiveSSLContext</a>.getInstance(<span style="color:darkred;">"TLS"</span>);
<span style="color: green;">// Set the custom SSL context.</span>
factory.setSSLContext(context);
<span style="color: green;">// Disable manual hostname verification for NaiveSSLContext.
factory.setVerifyHostname(false);</pre>
</blockquote>
<p>
<a href="https://gist.github.com/TakahikoKawasaki/d07de2218b4b81bf65ac"
>NaiveSSLContext</a> used in the above example is a factory class to
create an SSLContext which naively
accepts all certificates without verification. It’s enough for testing
purposes. When you see an error message
"unable to find valid certificate path to requested target" while
testing, try NaiveSSLContext.
</p>
<h3>HTTP Proxy</h3>
<p>
If a WebSocket endpoint needs to be accessed via an HTTP proxy,
information about the proxy server has to be set to a WebSocketFactory instance before creating a WebSocket
instance. Proxy settings are represented by ProxySettings
class. A WebSocketFactory instance has an associated
ProxySettings instance and it can be obtained by calling
WebSocketFactory.getProxySettings() method.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Get the associated ProxySettings instance.</span>
ProxySettings settings = factory.getProxySettings();</pre>
</blockquote>
<p>
ProxySettings class has methods to set information about
a proxy server such as setHost
method and setPort method. The
following is an example to set a secure (<code>https</code>) proxy
server.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Set a proxy server.</span>
settings.setServer(<span style="color:darkred;">"https://proxy.example.com"</span>);</pre>
</blockquote>
<p>
If credentials are required for authentication at a proxy server,
setId method and setPassword method, or
setCredentials
method can be used to set the credentials. Note that, however,
the current implementation supports only Basic Authentication.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Set credentials for authentication at a proxy server.</span>
settings.setCredentials(id, password);
</pre>
</blockquote>
<h3>Create WebSocket</h3>
<p>
WebSocket class represents a WebSocket. Its instances are
created by calling one of createSocket methods of a WebSocketFactory instance. Below is the simplest example to create
a WebSocket instance.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Create a WebSocket. The scheme part can be one of the following:
WebSocket ws = new WebSocketFactory()
.createWebSocket(<span style="color: darkred;">"ws://localhost/endpoint"</span>);</pre>
</blockquote>
<p>
There are two ways to set a timeout value for socket connection. The
first way is to call setConnectionTimeout(int timeout) method of WebSocketFactory.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Create a WebSocket factory and set 5000 milliseconds as a timeout
WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(5000);
<span style="color: green;">// Create a WebSocket. The timeout value set above is used.</span>
WebSocket ws = factory.createWebSocket(<span style="color: darkred;">"ws://localhost/endpoint"</span>);</pre>
</blockquote>
<p>
The other way is to give a timeout value to a createSocket method.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Create a WebSocket factory. The timeout value remains 0.</span>
WebSocketFactory factory = new WebSocketFactory();
<span style="color: green;">// Create a WebSocket with a socket connection timeout value.</span>
WebSocket ws = factory.createWebSocket(<span style="color: darkred;">"ws://localhost/endpoint"</span>, 5000);</pre>
</blockquote>
<p>
The timeout value is passed to connect(SocketAddress, int)
method of Socket.
</p>
<h3>Register Listener</h3>
<p>
After creating a WebSocket instance, you should call addListener(WebSocketListener) method to register a WebSocketListener that receives WebSocket events. WebSocketAdapter is an empty implementation of WebSocketListener interface.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Register a listener to receive WebSocket events.</span>
ws.addListener(new WebSocketAdapter() {
<span style="color: gray;">@Override</span>
public void onTextMessage(WebSocket websocket, String message) throws Exception {
<span style="color: green;">// Received a text message.</span>
……
}
});</pre>
</blockquote>
<p>
The table below is the list of callback methods defined in WebSocketListener
interface.
</p>
<blockquote>
<table border="1" cellpadding="5" style="border-collapse: collapse;">
<caption>WebSocketListener methods</caption>
<thead>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>handleCallbackError</td>
<td>Called when an <code>on<i>Xxx</i>()</code> method threw a Throwable.</td>
</tr>
<tr>
<td>onBinaryFrame</td>
<td>Called when a binary frame was received.</td>
</tr>
<tr>
<td>onBinaryMessage</td>
<td>Called when a binary message was received.</td>
</tr>
<tr>
<td>onCloseFrame</td>
<td>Called when a close frame was received.</td>
</tr>
<tr>
<td>onConnected</td>
<td>Called after the opening handshake succeeded.</td>
</tr>
<tr>
<td>onConnectError</td>
<td>Called when connectAsynchronously() failed.</td>
</tr>
<tr>
<td>onContinuationFrame</td>
<td>Called when a continuation frame was received.</td>
</tr>
<tr>
<td>onDisconnected</td>
<td>Called after a WebSocket connection was closed.</td>
</tr>
<tr>
<td>onError</td>
<td>Called when an error occurred.</td>
</tr>
<tr>
<td>onFrame</td>
<td>Called when a frame was received.</td>
</tr>
<tr>
<td>onFrameError</td>
<td>Called when a frame failed to be read.</td>
</tr>
<tr>
<td>onFrameSent</td>
<td>Called when a frame was sent.</td>
</tr>
<tr>
<td>onFrameUnsent</td>
<td>Called when a frame was not sent.</td>
</tr>
<tr>
<td>onMessageDecompressionError</td>
<td>Called when a message failed to be decompressed.</td>
</tr>
<tr>
<td>onMessageError</td>
<td>Called when a message failed to be constructed.</td>
</tr>
<tr>
<td>onPingFrame</td>
<td>Called when a ping frame was received.</td>
</tr>
<tr>
<td>onPongFrame</td>
<td>Called when a pong frame was received.</td>
</tr>
<tr>
<td>onSendError</td>
<td>Called when an error occurred on sending a frame.</td>
</tr>
<tr>
<td>onSendingFrame</td>
<td>Called before a frame is sent.</td>
</tr>
<tr>
<td>onSendingHandshake</td>
<td>Called before an opening handshake is sent.</td>
</tr>
<tr>
<td>onStateChanged</td>
<td>Called when the state of WebSocket changed.</td>
</tr>
<tr>
<td>onTextFrame</td>
<td>Called when a text frame was received.</td>
</tr>
<tr>
<td>onTextMessage</td>
<td>Called when a text message was received.</td>
</tr>
<tr>
<td>onTextMessageError</td>
<td>Called when a text message failed to be constructed.</td>
</tr>
<tr>
<td>onThreadCreated</td>
<td>Called after a thread was created.</td>
</tr>
<tr>
<td>onThreadStarted</td>
<td>Called at the beginning of a thread’s run() method.
</tr>
<tr>
<td>onThreadStopping</td>
<td>Called at the end of a thread’s run() method.
</tr>
<tr>
<td>onUnexpectedError</td>
<td>Called when an uncaught throwable was detected.</td>
</tr>
</tbody>
</table>
</blockquote>
<h3>Configure WebSocket</h3>
<p>
Before starting a WebSocket <a href="https://tools.ietf.org/html/rfc6455#section-4"
>opening handshake</a> with the server, you can configure the
WebSocket instance by using the following methods.
</p>
<blockquote>
<table border="1" cellpadding="5" style="border-collapse: collapse;">
<caption>Methods for Configuration</caption>
<thead>
<tr>
<th>METHOD</th>
<th>DESCRIPTION</th>
</tr>
</thead>
<tbody>
<tr>
<td>addProtocol</td>
<td>Adds an element to Sec-WebSocket-Protocol</td>
</tr>
<tr>
<td>addExtension</td>
<td>Adds an element to Sec-WebSocket-Extensions</td>
</tr>
<tr>
<td>addHeader</td>
<td>Adds an arbitrary HTTP header.</td>
</tr>
<tr>
<td>setUserInfo</td>
<td>Adds Authorization header for Basic Authentication.</td>
</tr>
<tr>
<td>getSocket</td>
<td>Gets the underlying Socket instance to configure it.</td>
</tr>
<tr>
<td>setExtended</td>
<td>Disables validity checks on RSV1/RSV2/RSV3 and opcode.</td>
</tr>
<tr>
<td>setFrameQueueSize</td>
<td>Set the size of the frame queue for <a href="#congestion_control">congestion control</a>.</td>
</tr>
<tr>
<td>setMaxPayloadSize</td>
<td>Set the <a href="#maximum_payload_size">maximum payload size</a>.</td>
</tr>
<tr>
<td>setMissingCloseFrameAllowed</td>
<td>Set whether to allow the server to close the connection without sending a close frame.</td>
</tr>
</tbody>
</table>
</blockquote>
<h3>Connect To Server</h3>
<p>
By calling connect() method, connection to the server is
established and a WebSocket opening handshake is performed
synchronously. If an error occurred during the handshake,
a WebSocketException would be thrown. Instead, when the
handshake succeeds, the connect() implementation creates
threads and starts them to read and write WebSocket frames
asynchronously.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> try
{
<span style="color: green;">// Connect to the server and perform an opening handshake.</span>
<span style="color: green;">// This method blocks until the opening handshake is finished.</span>
ws.connect();
}
catch (OpeningHandshakeException e)
{
<span style="color: green;">// A violation against the WebSocket protocol was detected</span>
<span style="color: green;">// during the opening handshake.</span>
}
catch (HostnameUnverifiedException e)
{
<span style="color: green;">// The certificate of the peer does not match the expected hostname.</span>
}
catch (WebSocketException e)
{
<span style="color: green;">// Failed to establish a WebSocket connection.</span>
}</pre>
</blockquote>
<p>
In some cases, connect() method throws OpeningHandshakeException
which is a subclass of WebSocketException (since version 1.19).
OpeningHandshakeException provides additional methods such as
getStatusLine(),
getHeaders() and
getBody() to access the
response from a server. The following snippet is an example to print
information that the exception holds.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> catch (OpeningHandshakeException e)
{
<span style="color: green;">// Status line.</span>
StatusLine sl = e.getStatusLine();
System.out.println(<span style="color:darkred;">"=== Status Line ==="</span>);
System.out.format(<span style="color:darkred;">"HTTP Version = %s\n"</span>, sl.getHttpVersion());
System.out.format(<span style="color:darkred;">"Status Code = %d\n"</span>, sl.getStatusCode());
System.out.format(<span style="color:darkred;">"Reason Phrase = %s\n"</span>, sl.getReasonPhrase());
<span style="color: green;">// HTTP headers.</span>
Map<String, List<String>> headers = e.getHeaders();
System.out.println(<span style="color:darkred;">"=== HTTP Headers ==="</span>);
for (Map.Entry<String, List<String>> entry : headers.entrySet())
{
<span style="color: green;">// Header name.</span>
String name = entry.getKey();
<span style="color: green;">// Values of the header.</span>
List<String> values = entry.getValue();
if (values == null || values.size() == 0)
{
<span style="color: green;">// Print the name only.</span>
System.out.println(name);
continue;
}
for (String value : values)
{
<span style="color: green;">// Print the name and the value.</span>
System.out.format(<span style="color:darkred;">"%s: %s\n"</span>, name, value);
}
}
}</pre>
</blockquote>
<p>
Also, connect() method throws HostnameUnverifiedException
which is a subclass of WebSocketException (since version 2.1) when
the certificate of the peer does not match the expected hostname.
</p>
<h3>Connect To Server Asynchronously</h3>
<p>
The simplest way to call connect() method asynchronously is to
use connectAsynchronously() method. The implementation of the
method creates a thread and calls connect() method in the thread.
When the connect() call failed, onConnectError() of WebSocketListener would be called. Note that
onConnectError() is called only when connectAsynchronously()
was used and the connect() call executed in the background thread
failed. Neither direct synchronous connect() nor
connect(ExecutorService) (described below) will trigger the callback method.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Connect to the server asynchronously.</span>
ws.connectAsynchronously();
</pre>
</blockquote>
<p>
Another way to call connect() method asynchronously is to use
connect(ExecutorService) method. The method performs a WebSocket
opening handshake asynchronously using the given ExecutorService.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Prepare an ExecutorService.</span>
ExecutorService es = Executors.newSingleThreadExecutor();
<span style="color: green;">// Connect to the server asynchronously.</span>
Future<WebSocket> future = ws.connect(es);
try
{
<span style="color: green;">// Wait for the opening handshake to complete.</span>
future.get();
}
catch (ExecutionException e)
{
if (e.getCause() instanceof WebSocketException)
{
……
}
}</pre>
</blockquote>
<p>
The implementation of connect(ExecutorService) method creates
a Callable<WebSocket>
instance by calling connectable() method and passes the
instance to submit(Callable)
method of the given ExecutorService. What the implementation
of call() method of the Callable
instance does is just to call the synchronous connect().
</p>
<h3>Send Frames</h3>
<p>
WebSocket frames can be sent by sendFrame(WebSocketFrame)
method. Other <code>send<i>Xxx</i></code> methods such as sendText(String) are aliases of sendFrame method. All of
the <code>send<i>Xxx</i></code> methods work asynchronously.
However, under some conditions, <code>send<i>Xxx</i></code> methods
may block. See <a href="#congestion_control">Congestion Control</a>
for details.
</p>
<p>
Below
are some examples of <code>send<i>Xxx</i></code> methods. Note that
in normal cases, you don’t have to call sendClose() method
and sendPong() (or their variants) explicitly because they
are called automatically when appropriate.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Send a text frame.</span>
ws.sendText(<span style="color: darkred;">"Hello."</span>);
<span style="color: green;">// Send a binary frame.</span>
byte[] binary = ……;
ws.sendBinary(binary);
<span style="color: green;">// Send a ping frame.</span>
ws.sendPing(<span style="color: darkred;">"Are you there?"</span>);</pre>
</blockquote>
<p>
If you want to send fragmented frames, you have to know the details
of the specification (<a href="https://tools.ietf.org/html/rfc6455#section-5.4"
>5.4. Fragmentation</a>). Below is an example to send a text message
("How are you?") which consists of 3 fragmented frames.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// The first frame must be either a text frame or a binary frame.
WebSocketFrame firstFrame = WebSocketFrame
.createTextFrame(<span style="color: darkred;">"How "</span>)
.setFin(false);
<span style="color: green;">// Subsequent frames must be continuation frames. The FIN bit of
WebSocketFrame secondFrame = WebSocketFrame
.createContinuationFrame(<span style="color: darkred;">"are "</span>);
<span style="color: green;">// The last frame must be a continuation frame with the FIN bit set.
WebSocketFrame lastFrame = WebSocketFrame
.createContinuationFrame(<span style="color: darkred;">"you?"</span>)
.setFin(true);
<span style="color: green;">// Send a text message which consists of 3 frames.</span>
ws.sendFrame(firstFrame)
.sendFrame(secondFrame)
.sendFrame(lastFrame);</pre>
</blockquote>
<p>
Alternatively, the same as above can be done like this.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Send a text message which consists of 3 frames.</span>
ws.sendText(<span style="color: darkred;">"How "</span>, false)
.sendContinuation(<span style="color: darkred;">"are "</span>)
.sendContinuation(<span style="color: darkred;">"you?"</span>, true);</pre>
</blockquote>
<h3>Send Ping/Pong Frames Periodically</h3>
<p>
You can send ping frames periodically by calling setPingInterval method with an interval in milliseconds between ping frames.
This method can be called both before and after connect() method.
Passing zero stops the periodical sending.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Send a ping per 60 seconds.</span>
ws.setPingInterval(60 * 1000);
<span style="color: green;">// Stop the periodical sending.</span>
ws.setPingInterval(0);</pre>
</blockquote>
<p>
Likewise, you can send pong frames periodically by calling setPongInterval method. "<i>A Pong frame MAY be sent
<b>unsolicited</b>."</i> (<a href="https://tools.ietf.org/html/rfc6455#section-5.5.3"
>RFC 6455, 5.5.3. Pong</a>)
</p>
<p>
You can customize payload of ping/pong frames that are sent automatically by using
setPingPayloadGenerator(PayloadGenerator) and
setPongPayloadGenerator(PayloadGenerator) methods. Both methods take an
instance of PayloadGenerator interface. The following is an example to
use the string representation of the current date as payload of ping frames.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> ws.setPingPayloadGenerator(new PayloadGenerator () {
<span style="color: gray;">@Override</span>
public byte[] generate() {
<span style="color: green;">// The string representation of the current date.</span>
return new Date().toString().getBytes();
}
});</pre>
</blockquote>
<p>
Note that the maximum payload length of control frames (e.g. ping frames) is 125.
Therefore, the length of a byte array returned from generate() method must not exceed 125.
</p>
<p>
You can change the names of the Timers that send ping/pong
frames periodically by using setPingSenderName(String) and
setPongSenderName(String) methods.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Change the Timers' names.</span>
ws.setPingSenderName(<span style="color: darkred;">"PING_SENDER"</span>);
ws.setPongSenderName(<span style="color: darkred;">"PONG_SENDER"</span>);
</blockquote>
<h3>Auto Flush</h3>
<p>
By default, a frame is automatically flushed to the server immediately after
sendFrame method is executed. This automatic
flush can be disabled by calling setAutoFlush(false).
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Disable auto-flush.</span>
ws.setAutoFlush(false);</pre>
</blockquote>
<p>
To flush frames manually, call flush() method. Note that this method
works asynchronously.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Flush frames to the server manually.</span>
ws.flush();</pre>
</blockquote>
<h3 id="congestion_control">Congestion Control</h3>
<p>
<code>send<i>Xxx</i></code> methods queue a WebSocketFrame instance to the
internal queue. By default, no upper limit is imposed on the queue size, so
<code>send<i>Xxx</i></code> methods do not block. However, this behavior may cause
a problem if your WebSocket client application sends too many WebSocket frames in
a short time for the WebSocket server to process. In such a case, you may want
<code>send<i>Xxx</i></code> methods to block when many frames are queued.
</p>
<p>
You can set an upper limit on the internal queue by calling setFrameQueueSize(int)
method. As a result, if the number of frames in the queue has reached the upper limit
when a <code>send<i>Xxx</i></code> method is called, the method blocks until the
queue gets spaces. The code snippet below is an example to set 5 as the upper limit
of the internal frame queue.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Set 5 as the frame queue size.</span>
ws.setFrameQueueSize(5);</pre>
</blockquote>
<p>
Note that under some conditions, even if the queue is full, <code>send<i>Xxx</i></code>
methods do not block. For example, in the case where the thread to send frames
(WritingThread) is going to stop or has already stopped. In addition,
method calls to send a <a href="https://tools.ietf.org/html/rfc6455#section-5.5"
>control frame</a> (e.g. sendClose() and sendPing()) do not block.
</p>
<h3 id="maximum_payload_size">Maximum Payload Size</h3>
<p>
You can set an upper limit on the payload size of WebSocket frames by calling
setMaxPayloadSize(int) method with a positive value. Text, binary and
continuation frames whose payload size is bigger than the maximum payload size
you have set will be split into multiple frames.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Set 1024 as the maximum payload size.</span>
ws.setMaxPayloadSize(1024);</pre>
</blockquote>
<p>
Control frames (close, ping and pong frames) are never split as per the specification.
</p>
<p>
If permessage-deflate extension is enabled and if the payload size of a WebSocket
frame after compression does not exceed the maximum payload size, the WebSocket
frame is not split even if the payload size before compression execeeds the
maximum payload size.
</p>
<h3 id="compression">Compression</h3>
<p>
The <strong>permessage-deflate</strong> extension (<a href=
"http://tools.ietf.org/html/rfc7692">RFC 7692</a>) has been supported
since the version 1.17. To enable the extension, call addExtension method with "permessage-deflate".
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"><span style="color: green;"> // Enable "permessage-deflate" extension (RFC 7692).</span>
ws.addExtension(WebSocketExtension.PERMESSAGE_DEFLATE);</pre>
</blockquote>
<h3>Missing Close Frame</h3>
<p>
Some server implementations close a WebSocket connection without sending a
<a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close frame</a> to
a client in some cases. Strictly speaking, this is a violation against the
specification (<a href=
"https://tools.ietf.org/html/rfc6455#section-5.5.1">RFC 6455</a>). However, this
library has allowed the behavior by default since the version 1.29. Even if the
end of the input stream of a WebSocket connection were reached without a close
frame being received, it would trigger neither onError() method nor
onFrameError() method of WebSocketListener. If you want to make a
WebSocket instance report an error in the case, pass false to
setMissingCloseFrameAllowed(boolean) method.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"><span style="color: green;"
> // Make this library report an error when the end of the input stream
ws.setMissingCloseFrameAllowed(false);</pre>
</blockquote>
<h3>Direct Text Message</h3>
<p>
When a text message was received, onTextMessage(WebSocket, String) is called. The implementation internally converts
the byte array of the text message into a String object before calling the
listener method. If you want to receive the byte array directly without the string
conversion, call setDirectTextMessage(boolean) with true, and
onTextMessage(WebSocket, byte[])
will be called instead.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"><span style="color: green;"
> // Receive text messages without string conversion.</span>
ws.setDirectTextMessage(true);</pre>
</blockquote>
<h3>Disconnect WebSocket</h3>
<p>
Before a WebSocket is closed, a closing handshake is performed. A closing handshake
is started (1) when the server sends a close frame to the client or (2) when the
client sends a close frame to the server. You can start a closing handshake by calling
disconnect() method (or by sending a close frame manually).
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Close the WebSocket connection.</span>
ws.disconnect();</pre>
</blockquote>
<p>
disconnect() method has some variants. If you want to change the close code
and the reason phrase of the close frame that this client will send to the server,
use a variant method such as disconnect(int, String). disconnect()
method itself is an alias of disconnect(WebSocketCloseCode.NORMAL, null).
</p>
<h3>Reconnection</h3>
<p>
connect() method can be called at most only once regardless of whether the
method succeeded or failed. If you want to re-connect to the WebSocket endpoint,
you have to create a new WebSocket instance again by calling one of createSocket methods of a WebSocketFactory. You may find recreate()
method useful if you want to create a new WebSocket instance that has the
same settings as the original instance. Note that, however, settings you made on
the raw socket of the original WebSocket instance are not copied.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: green;">// Create a new WebSocket instance and connect to the same endpoint.</span>
ws = ws.recreate().connect();</pre>
</blockquote>
<p>
There is a variant of recreate() method that takes a timeout value for
socket connection. If you want to use a timeout value that is different from the
one used when the existing WebSocket instance was created, use recreate(int timeout) method.
</p>
<p>
Note that you should not trigger reconnection in onError() method
because onError() may be called multiple times due to one error. Instead,
onDisconnected() is the right place to trigger reconnection.
</p>
<p>
Also note that the reason I use an expression of <i>"to trigger reconnection"</i>
instead of <i>"to call <code>recreate().connect()</code>"</i> is that I myself
won’t do it <i>synchronously</i> in <code>WebSocketListener</code> callback
methods but will just schedule reconnection or will just go to the top of a kind
of <i>application loop</i> that repeats to establish a WebSocket connection until
it succeeds.
</p>
<h3>Error Handling</h3>
<p>
WebSocketListener has some onXxxError() methods such as onFrameError() and onSendError(). Among such methods, onError() is a special
one. It is always called before any other onXxxError() is called. For
example, in the implementation of run() method of ReadingThread,
Throwable is caught and onError() and onUnexpectedError() are called in this order. The following is the implementation.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: gray;">@Override</span>
public void run()
{
try
{
main();
}
catch (Throwable t)
{
<span style="color: green;">// An uncaught throwable was detected in the reading thread.</span>
WebSocketException cause = new WebSocketException(
WebSocketError.UNEXPECTED_ERROR_IN_READING_THREAD,
<span style="color: darkred;">"An uncaught throwable was detected in the reading thread"</span>, t);
<span style="color: green;">// Notify the listeners.</span>
ListenerManager manager = mWebSocket.getListenerManager();
manager.callOnError(cause);
manager.callOnUnexpectedError(cause);
}
}</pre>
</blockquote>
<p>
So, you can handle all error cases in onError() method. However, note
that onError() may be called multiple times for one error cause, so don’t
try to trigger reconnection in onError(). Instead, onDiconnected() is the right place to trigger reconnection.
</p>
<p>
All onXxxError() methods receive a WebSocketException instance
as the second argument (the first argument is a WebSocket instance). The
exception class provides getError() method
which returns a WebSocketError enum entry. Entries in WebSocketError
enum are possible causes of errors that may occur in the implementation of this
library. The error causes are so granular that they can make it easy for you to
find the root cause when an error occurs.
</p>
<p>
Throwables thrown by implementations of onXXX() callback methods
are passed to handleCallbackError() of WebSocketListener.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: gray;">@Override</span>
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
<span style="color: green;">// Throwables thrown by onXxx() callback methods come here.</span>
}</pre>
</blockquote>
<h3>Thread Callbacks</h3>
<p>
Some threads are created internally in the implementation of WebSocket.
Known threads are as follows.
</p>
<blockquote>
<table border="1" cellpadding="5" style="border-collapse: collapse;">
<caption>Internal Threads</caption>
<thead>
<tr>
<th>THREAD TYPE</th>
<th>DESCRIPTION</th>
</tr>
</thead>
<tbody>
<tr>
<td>READING_THREAD</td>
<td>A thread which reads WebSocket frames from the server.</td>
</tr>
<tr>
<td>WRITING_THREAD</td>
<td>A thread which sends WebSocket frames to the server.</td>
</tr>
<tr>
<td>CONNECT_THREAD</td>
<td>A thread which calls connect() asynchronously.</td>
</tr>
<tr>
<td>FINISH_THREAD</td>
<td>A thread which does finalization of a WebSocket instance.</td>
</tr>
</tbody>
</table>
</blockquote>
<p>
The following callback methods of WebSocketListener are called according
to the life cycle of the threads.
</p>
<blockquote>
<table border="1" cellpadding="5" style="border-collapse: collapse;">
<caption>Thread Callbacks</caption>
<thead>
<tr>
<th>METHOD</th>
<th>DESCRIPTION</th>
</tr>
</thead>
<tbody>
<tr>
<td>onThreadCreated()</td>
<td>Called after a thread was created.</td>
</tr>
<tr>
<td>onThreadStarted()</td>
<td>Called at the beginning of the thread’s run() method.</td>
</tr>
<tr>
<td>onThreadStopping()</td>
<td>Called at the end of the thread’s run() method.</td>
</tr>
</tbody>
</table>
</blockquote>
<p>
For example, if you want to change the name of the reading thread,
implement onThreadCreated() method like below.
</p>
<blockquote>
<pre style="border-left: solid 5px lightgray;"> <span style="color: gray;">@Override</span>
public void onThreadCreated(WebSocket websocket, ThreadType type, Thread thread)
{
if (type == ThreadType.READING_THREAD)
{
thread.setName(<span style="color: darkred;">"READING_THREAD"</span>);
}
}</pre>
</blockquote>