在 Android 应用程序中使用哪个 WebSocket 库?

我想给我的 Android 应用程序添加一个 服务,它在后台运行,保持 WebSocket连接(可能超过几个小时甚至几天) ,并定期向服务器发送一些数据。

现在似乎有很多用于 Java 的 WebSocket 库,我不确定我应该使用哪一个:

此外,Android 还有一个原生的 Socket.io客户端库:

  • Nkzawa/socket.io-client. java 来自 GitHub 的描述: Java 的全功能 Socket.IO 客户端库,它与 Socket.IO v1.0及更高版本兼容。

使用 socket.io Android 客户端对我来说会很方便,因为无论如何我计划在 web 前端使用 nodejs/socket.io。但是本地客户机非常年轻,并且存在一些未解决的问题。除此之外,我的理解是,Android 应用程序没有使用 socket.io 客户端库的任何好处(除了与 socket.io 1.0服务器兼容之外) ,因为 WebSocket 支持可以在客户端得到保证。

我的要求如下:

  • 与 Android API 9及更高版本的兼容性
  • 通过 SSL 连接的可能性
  • 保持长时间的连接,而不必持有一个永久的醒发
  • 与可用 nodejs websocket 服务器实现或 socket.io 的兼容性

对于这些需求哪个库是正确的有什么建议吗?

99634 次浏览

一些笔记。

  • Koush/AndroidAsync 不执行 RFC 6455所要求的 握手结束

  • Tyrus 项目 可以在 Android 上运行,但是要确保它的许可证(具有 CPE 的 CDDL 1.1和 GPL 2)和它的大小(用 ProGuard 减小 WebSocket 客户端 jar 大小)满足您的需求。还要注意的是,当文本大小较大时,Tyrus 可能会抛出异常(这可能是一个 bug)。详情请参阅 这个

  • Jetty : Jetty 用户邮件列表中的一个两年前的 电子邮件说,”我们目前没有兼容 Android 的 Jetty 9 WebSocket 客户端。有计划尝试将 Jetty WebSocket Client 从 JDK 7支持到 JDK 5/6,用于安卓系统,但是它的优先级比我们完成 JSR-356 Java WebSocket API (javax.WebSocket)的实现要低。” Jetty 目前关于 WebSocket 客户端 API 的 文件没有提到任何关于 Android 的内容。

  • Codebutler/android-websocket 不执行 RFC 6455所需的 握手结束,并且可能在关闭时抛出异常。

  • Atmosphere/wasync 使用 AsyncHttpClient/< a href = “ https://github.com/AsyncHttpClient/sync-http-client”rel = “ nofollow noReferrer”> sync-http-client 作为其 WebSocket 实现。

  • Firebase/TubeSock 不验证 Sec-WebSocket-Accept。这是对 RFC 6455的侵犯。而且,TubeSock 在构建文本消息时有一个 bug。如果对文本消息使用多字节 UTF-8字符,您迟早会遇到 bug。有关 TubeSock 问题的详细列表,请参阅 Happy-im/Android-DDP中的 第三期

考虑因素

选择用 Java 编写的 WebSocket 客户端实现时的考虑要点:

  1. 符合 。不少实现没有实现 RFC 6455所要求的 握手结束。(如果没有实现结束握手会发生什么?见 这个)
  2. 需要的 Java 版本 。 Java SE 5,6,7,8还是 Java EE? 在 Android 上也能工作吗?
  3. 大小 。一些实现有许多依赖项。
  4. Wss 支持。
  5. HTTP 代理 支持。
  6. 对 wss over HTTP 代理 的支持。请参见 HTML5WebSockets 如何与代理服务器交互中的图2,了解 WebSocket 客户端库必须做些什么才能支持 wss over HTTP 代理。
  7. 在 SSL 配置上的灵活性 . SSLSocketFactorySSLContext应该能够在没有不必要限制的情况下得到利用。
  8. 开始握手 中定制 HTTP 头,包括基本身份验证。
  9. HTTP 代理协商 中的自定义 HTTP 头,包括代理服务器上的身份验证。
  10. 能够发送所有帧类型 (连续、二进制、文本、关闭、 ping 和 pong)或不。大多数实现都没有为开发人员提供手动发送 支离破碎的画面不请自来乒乓球框架的方法。
  11. 侦听器接口 接收各种 WebSocket 事件。糟糕的界面让开发人员感到沮丧。丰富的界面可以帮助开发人员编写健壮的应用程序。
  12. 能否查询 WebSocket 状态 RFC 6455定义了连接、打开、关闭和关闭状态,但很少有实现以定义的方式维护其内部状态转换。
  13. 能够为套接字连接设置超时值 (等效于 Socket.connect(SocketAddress endpoint, int timeout)方法的第二个参数)
  14. 能够访问底层的原始套接字
  15. 直观易用的 API 与否。
  16. 是否有充分的文件证明。
  17. RFC 7692 (WebSocket 的压缩扩展) 支持(又名 permessage-flate)。
  18. 重定向 (3xx)支持。
  19. 摘要认证 支持。

Nv-websocket-client 涵盖了以上所有内容,除了最后两个。此外,它的一个小而方便的特点是定期发送乒乓球框架。它可以通过调用 setPingInterval/setPongInterval方法来实现(参见 JavaDoc)。

免责声明: Takahiko Kawasaki 是 nv-websocket-client 的作者。

其他一些考虑:

Tyrus 在 Android 上工作。但是,它在 Android 5.0中使用的 SSL 库存在 bug 和 SSL 握手失败。这个问题在新版本的安卓系统中应该是可以解决的,但是由于安卓系统在很多设备上都没有更新,这对你来说可能是个问题。

这可能也是一个问题,具体取决于其他 websocket 实现是如何实现 SSL 的。

AndroidAsync 没有这个 SSL 问题,它有其他问题,比如 无法设置超时

A)将此文件添加到分级文件中

compile 'com.github.nkzawa:socket.io-client:0.3.0'

B)在申请活动中加入以下项目:

    public class MyApplication extends Application {
private Socket mSocket;
{
try {
mSocket = IO.socket(Config.getBaseURL());


} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}


public Socket getSocket() {
return mSocket;
}
}

C)将这个函数添加到您的活动中,您在其中调用了 WebSocket:

     private void websocketConnection() {
//Get websocket from application
MyApplication app = (MyApplication ) getApplication();
mSocket = app.getSocket();
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.on("messageFromServer", onNewLocation);
mSocket.connect();
}




private Emitter.Listener onConnect = new Emitter.Listener() {
@Override
public void call(Object... args) {
runOnUiThread(() -> {
if (!isConnected) {


RequestSocket mRequestSocket = new RequestSocket();


mRequestSocket.setToken("anil_singhania");
/* your parameter */
mSocket.emit("messageFromClient", new Gson().toJson(mRequestSocket));
Log.i("Socket Data", new Gson().toJson(mRequestSocket));
isConnected = true;
}
});
}
};


private Emitter.Listener onDisconnect = args -> runOnUiThread(() -> {
isConnected = false;
/* Toast.makeText(getApplicationContext(),
R.string.disconnect, Toast.LENGTH_LONG).show();*/
});


private Emitter.Listener onConnectError = args -> runOnUiThread(() -> {
/*   Toast.makeText(getApplicationContext(),
R.string.error_connect, Toast.LENGTH_LONG).show()*/
});


private Emitter.Listener onNewLocation = new Emitter.Listener() {
@Override
public void call(final Object... args) {
runOnUiThread(() -> {




});
}
};