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

152
我希望在我的 Android 应用中添加一个 服务,它在后台运行并保持 WebSocket 连接(可能持续几个小时甚至几天),并定期向服务器发送一些数据。现在似乎有很多 Java 的 WebSocket 库可用,我不确定应该使用哪一个: 此外,Android也有一个原生socket.io客户端库: 使用socket.io Android客户端对我来说很方便,因为我计划在web前端中使用nodejs/socket.io。但是该原生客户端还很年轻,存在一些未解决的问题。而且,据我了解,使用socket.io客户端库对于Android应用程序并没有任何好处(除了与socket.io 1.0服务器兼容之外),因为客户端WebSocket支持可以得到保证。
我的要求如下:
  • 与Android API 9及更高版本兼容
  • 通过SSL进行连接的可能性
  • 长时间保持连接而无需持久wakelock
  • 与可用的nodejs websocket服务器实现或socket.io兼容
请问哪个库符合这些要求?

也许可以考虑使用 Atmosphere。参见这个问题 - Basil Bourque
2
我不是WebSocket或Atmosphere的专家。我只知道Atmosphere经过充分验证,在许多项目中用于推送功能,包括WebSocket支持。我的唯一经验是间接的,在构建Vaadin Web应用程序时。Vaadin使用Atmosphere实现自动推送功能。但要注意,WebSocket仍然相对较新,在其简短的历史中定义、规范和各种实现都发生了许多变化。因此,无论你如何选择,都可能会遇到“问题”。 - Basil Bourque
2
FYI,Autobahn已经发布了,他们有一个华丽的网站。但是直到你花时间安装和尝试运行它之前,没有注意到“未实现安全WebSockets”。下一个。 - cloudsurfin
2
我没有足够的声望来评论,所以我将其作为答案写下来,因为我已经阅读了您在问题中提到的相同要求,并且okhttp帮助我满足了所有要求。 自从3.5版本推出以来,它支持Web套接字,因此使用okHttp(Web服务调用+ Web套接字支持)是一个额外的优势。 这是开始的链接。 https://medium.com/@ssaurel/learn-to-use-websockets-on-android-with-okhttp-ba5f00aea988 - Kaleem Patel
12
这类问题不应该被关闭。 - Martin Berger
显示剩余4条评论
3个回答

137

一些注释。

  • koush/AndroidAsync不执行关闭握手,这是RFC 6455所要求的。有关详细信息,请参见此处

  • Project Tyrus适用于Android,但请确保其许可证(CDDL 1.1和GPL 2与CPE)以及其大小(使用ProGuard减少WebSocket客户端jar大小)符合您的要求。此外,请注意Tyrus在文本大小较大时可能会抛出异常(这可能是一个错误)。有关详细信息,请参见此处

  • Jetty: 两年前的电子邮件线程中的jetty-users邮件列表说:"我们目前没有适用于Android的Jetty 9 WebSocket客户端。有计划尝试从JDK 7到JDK 5/6回溯Jetty WebSocket客户端以供Android使用,但这比完成我们的JSR-356 Java WebSocket API (javax.websocket)的实现优先级要低。" Jetty关于其WebSocket客户端API的当前文档没有提到任何与Android相关的内容。

  • codebutler/android-websocket没有执行关闭握手,该握手在RFC 6455中是必须的,并且在关闭时可能会抛出异常。请参见此处

  • Atmosphere/wasync使用AsyncHttpClient/async-http-client作为其WebSocket实现。因此,应该提到AsyncHttpClient/async-http-client而不是Wasync。

  • firebase/TubeSock没有验证Sec-WebSocket-Accept。这违反了RFC 6455。此外,TubeSock在构建文本消息方面存在错误。如果您在文本消息中使用多字节UTF-8字符,则迟早会遇到此错误。请参见delight-im/Android-DDP中的Issue 3以获取有关TubeSock问题的长列表。

  • 考虑点

    选择Java编写的WebSocket客户端实现时需注意以下几个方面:

    1. 合规性。不少实现并未实施RFC 6455所要求的 关闭握手。(如果没有实现关闭握手会发生什么?请参见此文
    2. 所需Java版本。Java SE 5、6、7、8或Java EE?甚至在Android上也可以工作?
    3. 大小。一些实现有许多依赖项。
    4. wss支持。
    5. HTTP代理支持。
    6. 通过HTTP代理的wss支持。请参考HTML5 Web Sockets与代理服务器交互中Figure 2,了解WebSocket客户端库必须做哪些工作才能支持通过HTTP代理的wss。
    7. SSL配置的灵活性。应该能够在不必要的限制下使用SSLSocketFactorySSLContext
    8. 握手中添加自定义HTTP头,包括基本身份验证。
    9. 在HTTP代理协商中添加自定义HTTP头,包括在代理服务器进行身份验证。
    10. 能够发送所有类型的帧(续帧、二进制帧、文本帧、关闭帧、ping 帧和 pong 帧)或不能。大多数实现不提供开发人员手动发送分片帧未经请求的 pong 帧的方式。
    11. 监听接口用于接收各种 WebSocket 事件。差的接口会让开发者感到沮丧,而丰富的接口则可以帮助开发者编写健壮的应用程序。
    12. 能否查询 WebSocket 状态RFC 6455 定义了连接、打开、正在关闭和关闭等状态,但很少有实现以定义的方式维护其内部状态转换。
    13. 能否设置套接字连接的超时值?(相当于Socket.connect(SocketAddress endpoint, int timeout)方法的第二个参数)
    14. 能否访问底层原始套接字
    15. 易于使用的直观 API或不是。
    16. 是否有良好的文档
    17. RFC 7692(WebSocket 的压缩扩展)支持(也称为 permessage-deflate)。
    18. 重定向(3xx)支持。
    19. 摘要认证支持。
    20. nv-websocket-client 覆盖了上述所有内容,除了最后两个。此外,它的一个小但方便的功能是定期发送 ping/pong 帧。只需调用 setPingInterval/setPongInterval 方法即可实现 (详见JavaDoc)。

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


    1
    nv-websocket-client库还在开发中吗? 我使用TooTallNate/Java-WebSockets时遇到了自动断开连接的问题,错误代码为1006,且没有原因... nv-websocket是否也可以解决这个问题? - Ankit Bansal
    1
    关于1006,规范(RFC 6455)指出,终端点不得将该代码设置为关闭控制帧中的状态代码。这意味着该代码是在客户端生成的。您可以通过WebSocketListeneronDisconnected方法和onError方法获取有关断开连接的更多信息。onError方法会给您一个WebSocketException实例。调用其getError()方法以查看问题所在。 - Takahiko Kawasaki
    7
    对于WebSocket协议,我尝试了OkHttp和Autobahn(也怀疑这个回答中的自我推销)。Autobahn很容易使用,但没有SSL。OkHttp几乎没有(合并的)文档(截至2016年2月)。由于不知道解决方法(如将超时设置为0或关闭传入消息),我浪费了很多时间阅读它们的代码和异常信息,以使简单的示例可以运行。放弃了这两个库(和我的挫败感),我发现nv有很好的文档,而且非常容易使用。 - cloudsurfin
    1
    你对Square/okhttp的新WebSockets支持有什么想法吗?https://medium.com/square-corner-blog/web-sockets-now-shipping-in-okhttp-3-5-463a9eec82d1 - scorpiodawg
    2
    我不清楚OkHttp的细节。非常抱歉,作为Authlete, Inc.的创始人(“API安全初创公司Authlete获得120万美元种子资金”),我非常忙碌,无法抽出时间来研究OkHttp并更新考虑要点清单。关于我的回答后的更改,请参见CHANGES.md。请注意,nv-websocket-client只是我的兴趣爱好,而OkHttp似乎是一个拥有138个贡献者的大型项目。 - Takahiko Kawasaki
    显示剩余9条评论

    4

    其他一些考虑因素:

    Tyrus 在 Android 平台上可用。但在 Android 5.0 上使用的 SSL 库存在缺陷,导致 SSL 握手失败 。尽管这个问题应该已经在新版本的 Android 中得到了修复,但由于许多设备没有得到更新,这可能会成为一个问题。

    根据其他 WebSocket 实现中 SSL 的实现方式,这也可能成为一个问题。

    AndroidAsync 没有 SSL 问题。但它确实存在其他问题,例如 无法设置超时时间


    1

    a) 在gradle文件中添加以下内容:

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

    b) 在应用程序Activity中添加以下行:

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

    不支持ws://协议。 - Girish Bhutiya

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接