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

时间:2015-05-30 14:47:06

标签: android node.js websocket

我想在我的Android应用中添加Service,该应用在后台运行,持有WebSocket连接(可能持续数小时甚至数天)并定期向服务器发送一些数据。

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

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

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

使用socket.io Android客户端对我来说很方便,因为我打算使用nodejs / socket.io作为web前端。但本土客户很年轻,有几个未解决的问题。除此之外,我的理解是Android应用程序没有使用socket.io客户端库的任何好处(除了与socket.io 1.0服务器兼容),因为可以在客户端确保WebSocket支持

我的要求如下:

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

对于这些要求,哪一个是正确的库?

3 个答案:

答案 0 :(得分:117)

一些注释。

  • koush / AndroidAsync 无法执行closing handshake所需的RFC 6455。有关详细信息,请参阅this

  • Project Tyrus 适用于Android,但要确保其许可证(CDDL 1.1 and GPL 2 with CPE)及其大小(Reducing WebSocket client jar size with ProGuard)符合您的要求。另请注意,当文本大小很大时,Tyrus可能会抛出异常(这可能是一个错误)。有关详细信息,请参阅this

  • Jetty :2年前{1}}在jetty-users邮件列表中说“我们目前没有Android兼容的Jetty 9 WebSocket客户端。计划尝试将Jetty WebSocket客户端从JDK 7反向移植到JDK 5/6以供Android使用,但它的优先级低于完成JSR-356 Java WebSocket API(javax.websocket)的实现。“ Jetty的当前版本关于其WebSocket Client API的email thread没有提及有关Android的任何内容。

  • codebutler / android-websocket 不会执行document所需的closing handshake,并且可能会在关闭时抛出异常。请参阅RFC 6455

  • Atmosphere / wasync 使用 AsyncHttpClient / this 作为其WebSocket实现。所以,应该提到AsyncHttpClient / async-http-client。

  • firebase / TubeSock 无法验证Sec-WebSocket-Accept。这违反了async-http-client。此外,TubeSock在构建短信时存在错误。如果您对文本消息使用多字节UTF-8字符,您迟早会遇到该错误。有关TubeSock问题的详细列表,请参阅RFC 6455中的Issue 3

考虑点

选择用Java编写的WebSocket客户端实现的注意事项:

  1. 合规即可。不少实现不会实现delight-im/Android-DDP所需的closing handshake。 (如果未实施结束握手会怎样?请参阅RFC 6455。)
  2. 必需的Java版本。 Java SE 5,6,7,8或Java EE?甚至可以在Android上使用吗?
  3. 大小即可。某些实现有很多依赖项。
  4. wss 支持。
  5. HTTP代理支持。
  6. wss over HTTP proxy 支持。请参阅this中的图2,了解WebSocket客户端库必须采取哪些措施来支持HTTP代理上的wss。
  7. SSL配置的灵活性。应该可以使用SSLSocketFactorySSLContext而不受不必要的限制。
  8. How HTML5 Web Sockets Interact With Proxy Servers 中的自定义HTTP标头,包括基本身份验证。
  9. HTTP代理协商中的自定义HTTP标头,包括代理服务器上的身份验证。
  10. 能否发送所有帧类型(续,二进制,文本,关闭,ping和pong)。大多数实现都没有为开发人员提供手动发送opening handshake未经请求的 fragmented frames的方法。
  11. 侦听器接口以接收各种WebSocket事件。糟糕的界面让开发人员感到沮丧。丰富的界面可以帮助开发人员编写健壮的应用程序。
  12. 能否查询WebSocket状态pong frames定义了CONNECTING,OPEN,CLOSING和CLOSED状态,但很少有实现以定义的方式维护其内部状态转换。
  13. 能够为套接字连接设置超时值。 (相当于Socket.RFC 6455(SocketAddress endpoint, int timeout)方法的第二个参数)
  14. 能够访问基础原始套接字
  15. 直观且易于使用的API
  16. 记录良好或不。
  17. connect(针对WebSocket的压缩扩展程序)支持(又称渗透 - deflate)。
  18. 重定向(3xx)支持。
  19. 摘要式身份验证支持。
  20. RFC 7692 涵盖了上述所有内容,但最后两项除外。此外,它的一个小而方便的功能是定期发送ping / pong帧。只需调用setPingInterval / setPongInterval方法即可实现(参见nv-websocket-client)。

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

答案 1 :(得分:4)

其他一些注意事项:

Tyrus适用于Android。但是,它在Android 5.0中使用的SSL库是错误的fail SSL handshakes。这应该在较新版本的Android中修复,但由于Android在许多设备上没有更新,这可能是一个问题。

根据SSL对其他websocket实现的实现方式,这可能也是一个问题。

AndroidAsync没有此SSL问题。它还有其他问题,例如not being able to set timeouts

答案 2 :(得分:3)

a)在gradle文件中添加此文件

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(() -> {


            });
        }
    };