• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

TakahikoKawasaki/nv-websocket-client: High-quality WebSocket client implementati ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

TakahikoKawasaki/nv-websocket-client

开源软件地址:

https://github.com/TakahikoKawasaki/nv-websocket-client

开源编程语言:

Java 100.0%

开源软件介绍:

nv-websocket-client

Overview

High-quality WebSocket client implementation in Java which

  • complies with RFC 6455 (The WebSocket Protocol),
  • works on Java SE 1.5+ and Android,
  • supports all the frame types (continuation, binary, text, close, ping and pong),
  • provides a method to send a fragmented frame in addition to methods for unfragmented frames,
  • provides a method to get the underlying raw socket of a WebSocket to configure it,
  • provides a method for Basic Authentication,
  • provides a factory class which utilizes javax.net.SocketFactory interface,
  • provides a rich listener interface to hook WebSocket events,
  • has fine-grained error codes for fine-grained controllability on errors,
  • allows to disable validity checks on RSV1/RSV2/RSV3 bits and opcode of frames,
  • supports HTTP proxy, especially "Secure WebSocket" (wss) through "Secure Proxy" (https),
  • and supports RFC 7692 (Compression Extensions for WebSocket), also known as permessage-deflate (not enabled by default).

License

Apache License, Version 2.0

Maven

<dependency>
    <groupId>com.neovisionaries</groupId>
    <artifactId>nv-websocket-client</artifactId>
    <version>2.14</version>
</dependency>

Gradle

dependencies {
    compile 'com.neovisionaries:nv-websocket-client:2.14'
}

OSGi

Bundle-SymbolicName: com.neovisionaries.ws.client
Export-Package: com.neovisionaries.ws.client;version="2.14.0"

Source Code

https://github.com/TakahikoKawasaki/nv-websocket-client.git

JavaDoc

https://TakahikoKawasaki.github.io/nv-websocket-client/

Description

Create WebSocketFactory

WebSocketFactory is a factory class that creates WebSocket instances. The first step is to create a WebSocketFactory instance.

// Create a WebSocketFactory instance.
WebSocketFactory factory = new WebSocketFactory();

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.
SSLContext context = 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.
ProxySettings settings = 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.
WebSocket ws = new WebSocketFactory().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.
WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(5000);

// Create a WebSocket. The timeout value set above is used.
WebSocket ws = 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.
WebSocketFactory factory = new WebSocketFactory();

// Create a WebSocket with a socket connection timeout value.
WebSocket ws = 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(new WebSocketAdapter() {
    @Override
    public void onTextMessage(WebSocket websocket, String message) throws Exception {
        // 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 (OpeningHandshakeException e)
{
    // A violation against the WebSocket protocol was detected
    // during the opening handshake.
}
catch (HostnameUnverifiedException e)
{
    // The certificate of the peer does not match the expected hostname.
}
catch (WebSocketException e)
{
    // 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 (OpeningHandshakeException e)
{
    // Status line.
    StatusLine sl = 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.
        String name = 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 (String value : 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.
ExecutorService es = Executors.newSingleThreadExecutor();

// Connect to the server asynchronously.
Future<WebSocket> future = ws.connect(es);

try
{
    // Wait for the opening handshake to complete.
    future.get();
}
catch (ExecutionException e)
{
    if (e.getCause() instanceof WebSocketException)
    {
        ......
    }
}

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.
WebSocketFrame firstFrame = 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.
WebSocketFrame secondFrame = 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.
WebSocketFrame lastFrame = 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.

ws.sendText("How ", false)
  .sendContinuation("are ")
  .sendContinuation("you?", true);

Send Ping/Pong Frames Periodically

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(new PayloadGenerator() {
    @Override
    public byte[] generate() {
        // The string representation of the current date.
        return new Date().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.

// Enable "permessage-deflate" extension (RFC 7692).
ws.addExtension(WebSocketExtension.PERMESSAGE_DEFLATE);

Missing Close Frame

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.


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
signalapp/libsignal-service-java: A Java/Android library for communicating with ...发布时间:2022-06-24
下一篇:
Intro | Javascript Challenges发布时间:2022-06-24
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap