Android连接Websocket问题


安卓连接Websocket问题

---------------------------------- 2020-04-22-11:20:00

前言:Websocket是方便客户端与服务器实现长连接、可以及时的收到服务器推送过来的消息。一般都用在web端与服务器的连接中。那么我们安卓应该如何连接Websocket呢?

首先了解一下什么是Websocket、Websocket与普通的Socket区别在哪里。先看一下百度百科给出的说法。

WebSokect百度百科

Socket(套接字)百度百科

OK,了解了Websocket与Socket的区别之后、我们可以着手来连接了。github上有很多安卓连接Websocket的案例,大家也可以自己去找现成的轮子。我几乎每个轮子都用过了。其中遇到过很多的问题。还是觉得Google的OKHTTP最好用。OKHTTP的强大之处不仅在于支持HTTP/HTTPS、还支持Websocket。

Websocket包含两种、WS与WSS。这两个有什么区别呢?就相当于HTTP与HTTPS的区别。不懂的去自行百度哈。我们公司使用wss://xxx.xxx.com,wss开头的网址。其中遇到了很多错误。

最常见的就是timeout、连接超时。在网上搜了好多也没有能解决的例子。直到今天。我在CSDN发现了这条博客,简直是发现了救星一样。先感谢一波作者。

Android WebSocket连接不成功

来谈谈我的过程。首先连接超时、其中也出现了HandShake,因为我使用的是WSS协议。所以我感觉是在请求第一次握手的时候、就已经失败了。所以连接不成功,后来又发现好像是证书验证的问题。所以找了好多案例来试,都无功而返。于是我使用OKHTTP来忽略证书验证。

X509TrustManager xtm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
        
        SSLContext sslContext = null;
        sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

        HostnameVerifier DO_NOT_VERIFT = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

再使用OKHTTPBuilder进行连接。

OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .readTimeout(5000, TimeUnit.SECONDS)
                    .connectTimeout(5000, TimeUnit.SECONDS)
                    .sslSocketFactory(sslContext.getSocketFactory())
                    .hostnameVerifier(DO_NOT_VERIFT)
                    .build();

Request request = new Request.Builder()
                    .url(Constants.HOSTNAME)
                    .build();

 webSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
                @Override
                public void onOpen(WebSocket webSocket, Response response) {
                    super.onOpen(webSocket, response);
                    Log.e(TAG, "onOpen: ");
                }

                @Override
                public void onMessage(WebSocket webSocket, String text) {
                    super.onMessage(webSocket, text);
                    Log.e(TAG, "onMessage: " + text);
                    
                }

                @Override
                public void onMessage(WebSocket webSocket, ByteString bytes) {
                    super.onMessage(webSocket, bytes);
                    Log.e(TAG, "onMessage: ");
                }

                @Override
                public void onClosing(WebSocket webSocket, int code, String reason) {
                    super.onClosing(webSocket, code, reason);
                    Log.e(TAG, "onClosing: code = " + code + " reason " + reason);
                }

                @Override
                public void onClosed(WebSocket webSocket, int code, String reason) {
                    super.onClosed(webSocket, code, reason);
                    Log.e(TAG, "onClosed: code = " + code + " reason " + reason);
                }

                @Override
                public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {
                    super.onFailure(webSocket, t, response);
                    Log.e(TAG, "onFailure: " + t.toString());
                }
            });

这时运行发现打印信息仍然是onFilure:timeOut。已经要发疯了。于是我跟进了一下源码。发现了一些端倪

 /**
   * Uses {@code request} to connect a new web socket.
   */
  @Override public WebSocket newWebSocket(Request request, WebSocketListener listener) {
    RealWebSocket webSocket = new RealWebSocket(request, listener, new Random(), pingInterval);
    webSocket.connect(this);
    return webSocket;
  }

再跟进一下RealWebSocket()

 public RealWebSocket(Request request, WebSocketListener listener, Random random,
     long pingIntervalMillis) {
   if (!"GET".equals(request.method())) {
     throw new IllegalArgumentException("Request must be GET: " + request.method());
   }
   this.originalRequest = request;
   this.listener = listener;
   this.random = random;
   this.pingIntervalMillis = pingIntervalMillis;

   byte[] nonce = new byte[16];
   random.nextBytes(nonce);
   this.key = ByteString.of(nonce).base64();

   this.writerRunnable = new Runnable() {
     @Override public void run() {
       try {
         while (writeOneFrame()) {
         }
       } catch (IOException e) {
         failWebSocket(e, null);
       }
     }
   };
 }

再跟进一下connect方法

 public void connect(OkHttpClient client) {
    client = client.newBuilder()
        .eventListener(EventListener.NONE)
        .protocols(ONLY_HTTP1)
        .build();
    final Request request = originalRequest.newBuilder()
        .header("Upgrade", "websocket")
        .header("Connection", "Upgrade")
        .header("Sec-WebSocket-Key", key)
        .header("Sec-WebSocket-Version", "13")
        .build();
    call = Internal.instance.newWebSocketCall(client, request);
    call.timeout().clearTimeout();
    call.enqueue(new Callback() {
      @Override public void onResponse(Call call, Response response) {
        try {
          checkResponse(response);
        } catch (ProtocolException e) {
          failWebSocket(e, response);
          closeQuietly(response);
          return;
        }

        // Promote the HTTP streams into web socket streams.
        StreamAllocation streamAllocation = Internal.instance.streamAllocation(call);
        streamAllocation.noNewStreams(); // Prevent connection pooling!
        Streams streams = streamAllocation.connection().newWebSocketStreams(streamAllocation);

        // Process all web socket messages.
        try {
          listener.onOpen(RealWebSocket.this, response);
          String name = "OkHttp WebSocket " + request.url().redact();
          initReaderAndWriter(name, streams);
          streamAllocation.connection().socket().setSoTimeout(0);
          loopReader();
        } catch (Exception e) {
          failWebSocket(e, null);
        }
      }

      @Override public void onFailure(Call call, IOException e) {
        failWebSocket(e, null);
      }
    });
  } 

看着是没有什么问题的吧。但是问题就出在这里

final Request request = originalRequest.newBuilder()
        .header("Upgrade", "websocket")
        .header("Connection", "Upgrade")
        .header("Sec-WebSocket-Key", key)
        .header("Sec-WebSocket-Version", "13")
        .build();

我好像发现了什么惊人的秘密!!

我用websocket在线验证的网址的开发者工具F12试着抓一下打开链接的包。比对一下Request Headers.

Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
Connection: Upgrade
Host: 222.128.10.136:2224
Origin: http://www.websocket-test.com
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: DvuNBBqVEML+7xA6+71fdg==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3756.400 QQBrowser/10.5.4039.400

发现这根本不一样啊。难怪链接不上。

于是又回过头来修改自己的RequestBuilder。这次我把上面的Header全部都copy过来,添加在自己的请求头中。

 Request request = new Request.Builder()
                    .url(Constants.HOSTNAME)
                    .addHeader("Accept-Encoding", "gzip, deflate")
                    .addHeader("Accept-Language", "zh-CN,zh;q=0.9")
                    .addHeader("Cache-Control", "no-cache")
                    .addHeader("Connection", "Upgrade")
                    .addHeader("Origin", "http://www.websocket-test.com")
                    .addHeader("Pragma", "no-cache")
                    .addHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits")
                    .addHeader("Sec-WebSocket-Key", "DvuNBBqVEML+7xA6+71fdg==")
                    .addHeader("Sec-WebSocket-Version", "13")
                    .addHeader("Upgrade", "websocket")
                    .build();

试着运行一下。查看log信息:onOpen: !!!这是什么情况。我蒙了。我更加肯定问题是出在这里。

可能是与服务器端没有沟通好吧!

奇怪的知识又增加了呢~,如果有了解的大神们欢迎给予评价。有要批评的,我也会虚心接受。

提前祝大家五一节日快乐!!

—创作不易。转载请注明链接

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android连接WebSocket服务非常简单。首先,你需要在你的项目中添加OkHttp库的依赖。在build.gradle文件中添加下面的代码: ```groovy implementation 'com.squareup.okhttp3:okhttp:3.14.9' implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.14.9' implementation 'com.squareup.okhttp3:logging-interceptor:3.14.9' ``` 然后,在你的Activity或者Fragment中,使用以下代码连接WebSocket服务: ```java import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.WebSocket; import okhttp3.WebSocketListener; public class MainActivity extends AppCompatActivity { private WebSocket webSocket; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("ws://your-websocket-url") .build(); WebSocketListener socketListener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { // 连接成功时的逻辑 } @Override public void onMessage(WebSocket webSocket, String text) { // 接收到消息时的逻辑 } @Override public void onClosed(WebSocket webSocket, int code, String reason) { // 连接关闭时的逻辑 } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { // 连接失败时的逻辑 } }; webSocket = client.newWebSocket(request, socketListener); } } ``` 在上面的代码中,你需要替换`ws://your-websocket-url`为你真实的WebSocket服务的URL。在`WebSocketListener`的回调方法中,你可以处理WebSocket连接状态,接收和发送消息。当你不再需要WebSocket连接时,你可以通过调用`webSocket.close()`方法关闭连接。 以上就是在Android连接WebSocket服务的简单示例。你可以根据自己的需求来处理WebSocket的消息和事件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值