WebSocketFactory is a factory class that creates WebSocket instances. The
first step is to create a WebSocketFactory instance.
// Create a WebSocketFactory instance.WebSocketFactoryfactory = newWebSocketFactory();
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.
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.)
// Create a custom SSL context.SSLContextcontext = NaiveSSLContext.getInstance("TLS");
// Set the custom SSL context.factory.setSSLContext(context);
// Disable manual hostname verification for NaiveSSLContext.//// Manual hostname verification has been enabled since the// version 2.1. Because the verification is executed manually// after Socket.connect(SocketAddress, int) succeeds, the// hostname verification is always executed even if you has// passed an SSLContext which naively accepts any server// certificate. However, this behavior is not desirable in// some cases and you may want to disable the hostname// verification. You can disable the hostname verification// by calling WebSocketFactory.setVerifyHostname(false).factory.setVerifyHostname(false);
NaiveSSLContext
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.
SNI (Server Name Indication) is supported since version 2.4. To set up server
names, call either setServerNames(String[]) method or setServerName(String)
method. If your system has SSLParameters.setServerNames(List<SNIServerName>)
method, the method is called via reflection. Note that SSLParameters.setServerNames
is a relatively new method and it is not available before Java 1.8 and Android
7.0 (API Level 24).
// Set a server name for SNI (Server Name Indication).factory.setServerName("example.com");
HTTP Proxy
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.
// Get the associated ProxySettings instance.ProxySettingssettings = factory.getProxySettings();
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 (https) proxy server.
// Set a proxy server.settings.setServer("https://proxy.example.com");
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.
// Set credentials for authentication at a proxy server.settings.setCredentials(id, password);
SNI (Server Name Indication) is supported since version 2.4. To set up server
names, call either setServerNames(String[]) method or setServerName(String)
method. If your system has SSLParameters.setServerNames(List<SNIServerName>)
method, the method is called via reflection. Note that SSLParameters.setServerNames
is a relatively new method and it is not available before Java 1.8 and Android
7.0 (API Level 24).
// Set a server name for SNI (Server Name Indication).settings.setServerName("example.com");
Create WebSocket
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.
// Create a WebSocket. The scheme part can be one of the following:// 'ws', 'wss', 'http' and 'https' (case-insensitive). The user info// part, if any, is interpreted as expected. If a raw socket failed// to be created, an IOException is thrown.WebSocketws = newWebSocketFactory().createSocket("ws://localhost/endpoint");
There are two ways to set a timeout value for socket connection. The first way
is to call setConnectionTimeout(int timeout) method of WebSocketFactory.
// Create a WebSocket factory and set 5000 milliseconds as a timeout// value for socket connection.WebSocketFactoryfactory = newWebSocketFactory().setConnectionTimeout(5000);
// Create a WebSocket. The timeout value set above is used.WebSocketws = factory.createSocket("ws://localhost/endpoint");
The other way is to give a timeout value to a createSocket method.
// Create a WebSocket factory. The timeout value remains 0.WebSocketFactoryfactory = newWebSocketFactory();
// Create a WebSocket with a socket connection timeout value.WebSocketws = factory.createSocket("ws://localhost/endpoint", 5000);
The timeout value is passed to connect(SocketAddress, int) method of
java.net.Socket.
Register Listener
After creating a WebSocket instance, you should call addListener method
to register a WebSocketListener that receives WebSocket events.
WebSocketAdapter is an empty implementation of WebSocketListener interface.
// Register a listener to receive WebSocket events.ws.addListener(newWebSocketAdapter() {
@OverridepublicvoidonTextMessage(WebSocketwebsocket, Stringmessage) throwsException {
// Received a text message.
......
}
});
The table below is the list of callback methods defined in WebSocketListener
interface.
Method
Description
handleCallbackError
Called when an onXxx() method threw a Throwable.
onBinaryFrame
Called when a binary frame was received.
onBinaryMessage
Called when a binary message was received.
onCloseFrame
Called when a close frame was received.
onConnected
Called after the opening handshake succeeded.
onConnectError
Called when connectAsynchronously() failed.
onContinuationFrame
Called when a continuation frame was received.
onDisconnected
Called after a WebSocket connection was closed.
onError
Called when an error occurred.
onFrame
Called when a frame was received.
onFrameError
Called when a frame failed to be read.
onFrameSent
Called when a frame was sent.
onFrameUnsent
Called when a frame was not sent.
onMessageDecompressionError
Called when a message failed to be decompressed.
onMessageError
Called when a message failed to be constructed.
onPingFrame
Called when a ping frame was received.
onPongFrame
Called when a pong frame was received.
onSendError
Called when an error occurred on sending a frame.
onSendingFrame
Called before a frame is sent.
onSendingHandshake
Called before an opening handshake is sent.
onStateChanged
Called when the state of WebSocket changed.
onTextFrame
Called when a text frame was received.
onTextMessage
Called when a text message was received.
onTextMessageError
Called when a text message failed to be constructed.
onThreadCreated
Called after a thread was created.
onThreadStarted
Called at the beginning of a thread's run() method.
onThreadStopping
Called at the end of a thread's run() method.
onUnexpectedError
Called when an uncaught throwable was detected.
Configure WebSocket
Before starting a WebSocket
opening handshake
with the server, you can
configure the WebSocket instance by using the following methods.
METHOD
DESCRIPTION
addProtocol
Adds an element to Sec-WebSocket-Protocol.
addExtension
Adds an element to Sec-WebSocket-Extensions.
addHeader
Adds an arbitrary HTTP header.
setUserInfo
Adds Authorization header for Basic Authentication.
getSocket
Gets the underlying Socket instance to configure it. Note that this may return null since version 2.9. Consider using getConnectedSocket() as necessary.
getConnectedSocket
Establishes and gets the underlying Socket instance to configure it. Available since version 2.9.
setExtended
Disables validity checks on RSV1/RSV2/RSV3 and opcode.
setFrameQueueSize
Set the size of the frame queue for congestion control.
setMaxPayloadSize
Set the maximum payload size.
setMissingCloseFrameAllowed
Set to whether to allow the server to close the connection without sending a close frame.
Connect To Server
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, if
the handshake succeeds, the connect() implementation creates threads and
starts them to read and write WebSocket frames asynchronously.
try
{
// Connect to the server and perform an opening handshake.// This method blocks until the opening handshake is finished.ws.connect();
}
catch (OpeningHandshakeExceptione)
{
// A violation against the WebSocket protocol was detected// during the opening handshake.
}
catch (HostnameUnverifiedExceptione)
{
// The certificate of the peer does not match the expected hostname.
}
catch (WebSocketExceptione)
{
// Failed to establish a WebSocket connection.
}
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.
catch (OpeningHandshakeExceptione)
{
// Status line.StatusLinesl = e.getStatusLine();
System.out.println("=== Status Line ===");
System.out.format("HTTP Version = %s\n", sl.getHttpVersion());
System.out.format("Status Code = %d\n", sl.getStatusCode());
System.out.format("Reason Phrase = %s\n", sl.getReasonPhrase());
// HTTP headers.Map<String, List<String>> headers = e.getHeaders();
System.out.println("=== HTTP Headers ===");
for (Map.Entry<String, List<String>> entry : headers.entrySet())
{
// Header name.Stringname = entry.getKey();
// Values of the header.List<String> values = entry.getValue();
if (values == null || values.size() == 0)
{
// Print the name only.System.out.println(name);
continue;
}
for (Stringvalue : values)
{
// Print the name and the value.System.out.format("%s: %s\n", name, value);
}
}
}
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.
Connect To Server Asynchronously
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.
// Connect to the server asynchronously.ws.connectAsynchronously();
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.
// Prepare an ExecutorService.ExecutorServicees = Executors.newSingleThreadExecutor();
// Connect to the server asynchronously.Future<WebSocket> future = ws.connect(es);
try
{
// Wait for the opening handshake to complete.future.get();
}
catch (ExecutionExceptione)
{
if (e.getCause() instanceofWebSocketException)
{
......
}
}
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().
Send Frames
WebSocket frames can be sent by sendFrame method. Other sendXxx
methods such as sendText are aliases of sendFrame method. All of
the sendXxx methods work asynchronously. However, under some
conditions, sendXxx methods may block. See Congestion Control for
details.
Below are some examples of sendXxx methods. Note that in normal
cases, you don't have to call sendClose method and sendPong
method (or their variants) explicitly because they are called
automatically when appropriate.
// Send a text frame.ws.sendText("Hello.");
// Send a binary frame.byte[] binary = ......;
ws.sendBinary(binary);
// Send a ping frame.ws.sendPing("Are you there?");
If you want to send fragmented frames, you have to know the details of the
specification (5.4. Fragmentation).
Below is an example to send a text message ("How are you?") which consists
of 3 fragmented frames.
// The first frame must be either a text frame or a binary frame.// And its FIN bit must be cleared.WebSocketFramefirstFrame = WebSocketFrame
.createTextFrame("How ")
.setFin(false);
// Subsequent frames must be continuation frames. The FIN bit of// all continuation frames except the last one must be cleared.// Note that the FIN bit of frames returned from// WebSocketFrame.createContinuationFrame() method is cleared,// so the example below does not clear the FIN bit explicitly.WebSocketFramesecondFrame = WebSocketFrame
.createContinuationFrame("are ");
// The last frame must be a continuation frame with the FIN bit// set. Note that the FIN bit of frames returned from// WebSocketFrame.createContinuationFrame methods is cleared,// so the FIN bit of the last frame must be set explicitly.WebSocketFramelastFrame = WebSocketFrame
.createContinuationFrame("you?")
.setFin(true);
// Send a text message which consists of 3 frames.ws.sendFrame(firstFrame)
.sendFrame(secondFrame)
.sendFrame(lastFrame);
Alternatively, the same as above can be done like this.
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.
// Send a ping per 60 seconds.ws.setPingInterval(60 * 1000);
// Stop the periodical sending.ws.setPingInterval(0);
Likewise, you can send pong frames periodically by calling setPongInterval
method. "A Pong frame MAY be sent unsolicited."
(RFC 6455, 5.5.3. Pong)
You can customize payload of ping/pong frames that are sent automatically
by using setPingPayloadGenerator() and setPongPayloadGenerator() 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.
ws.setPingPayloadGenerator(newPayloadGenerator() {
@Overridepublicbyte[] generate() {
// The string representation of the current date.returnnewDate().toString().getBytes();
}
});
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.
You can change the names of the Timers that send ping/pong frames
periodically by using setPingSenderName() and setPongSenderName() methods.
// Change the Timers' names.ws.setPingSenderName("PING_SENDER");
ws.setPongSenderName("PONG_SENDER");
Auto Flush
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).
// Disable auto-flush.ws.setAutoFlush(false);
To flush frames manually, call flush() method. Note that this method
works asynchronously.
// Flush frames to the server manually.ws.flush();
Congestion Control
sendXxx methods queue a WebSocketFrame instance to the internal queue.
By default, no upper limit is imposed on the queue size, so sendXxx
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
sendXxx methods to block when many frames are queued.
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 sendXxx 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.
// Set 5 as the frame queue size.ws.setFrameQueueSize(5);
Note that under some conditions, even if the queue is full, sendXxx
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
control frame (e.g.
sendClose() and sendPing()) do not block.
Maximum Payload Size
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.
// Set 1024 as the maximum payload size.ws.setMaxPayloadSize(1024);
Control frames (close, ping and pong frames) are never split as per the
specification.
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
exceeds the maximum payload size.
Compression
The permessage-deflate extension (RFC 7692)
has been supported since the version 1.17. To enable the extension, call
addExtension method with permessage-deflate.
Some server implementations close a WebSocket connection without sending a
close frame to a client in some cases. Strictly speaking, this is a violation
against the specification (RFC 6455).
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 method.
// Make this library report an error when the end of the input stream// of the WebSocket connection is reached before a close frame is read.ws.setMissingCloseFrameAllowed(false);
Direct Text Message
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.
请发表评论