本平台的文章更新会有延迟,大家可以关注微信公众号-顾林海,包括年底前会更新kotlin由浅入深系列教程,目前计划在微信公众号进行首发,如果大家想获取最新教程,请关注微信公众号,谢谢!
在讲解如何利用OkHttp实现WebSocket之前,我们聊聊轮询技术,什么是轮询?轮询就是在特定的时间间隔,由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。
轮询分为两种:
短轮询:通过不断的向服务端发送数据,客户端发送Request,服务端直接返回Response(不管服务端数据有没有改变)。
长轮询:通过不断的向服务端发送数据,客户端发送Request,服务端发现数据没有改变,就将这个Request挂起,直到有最新数据再发送Response给客户端。
通过上面短轮询的介绍,不难发现它的缺点,如果在某段时间内,服务端数据没有任何变化,但客户端还是不停的发送请求给服务端,服务端也不管数据是否变化,直接返回结果给客户端,那么在这段时间内的所有请求其实是无效的。
这个时候长轮询就弥补了短轮询的缺点,客户端发送请求,服务器会查询数据是否更新,没有更新就会挂起这个请求,直到有新数据,服务端才会把Response返回给客户端,这样是不是就完美了?其实不是,服务端将客户端的请求挂起会导致资源的浪费,比如有1W人请求服务端,那这个时候服务端这边就要开启1W个线程,导致资源占用。
无论是使用短轮询还是使用长轮询,它们通信的方式还是通过HTTP请求的,HTTP头部比较大,但实际数据比较少,造成带宽的浪费,由于不停的轮询,导致服务器CPU占用过高。
既然短轮询和长轮询有这么多问题,那有没有什么解决方案呢?这时WebSocket登场,看下面这张图(来源网络)。
可以看到WebSocket的连接是长期存在的,并且可以不断的进行通信,这是一个全双工的通信模式,客户端可以不停的向服务端发送消息,服务端也可以不停的发送消息给客户端。
那么WebSocket和HTTP有什么区别呢?HTTP是一个Request对应一个Response,就是说客服发送一个请求,服务端接受到请求才会向客户端发送响应,Request和Response是一对一的关系,服务端比较懒,它一定要客户端给它发送请求,服务端才会有响应。相比而言,WebSocket在客户端与服务端建立连接后,客服端与服务端就可以进行全双工通信了,WebSocket在建立连接时也是用到了HTTP的协议,但建立起连接后,双方的通信与HTTP就没有任何关系了。
继续下个问题,WebSocket与Socket有什么关系?Socket并不是一种协议,它只是方便我们使用TCP或UDP抽象出来的一层,它是应用层和传输层之间的一种接口,而WebSocket是一种协议,总的来说,这两者压根没有任何关系。
总结:WebSocket是协议,是一个基于TCP的协议,为了建立一个WebSocket连接,需要向服务器发起一个HTTP请求,并加入头部信息“Upgrade:WebSocket”,服务器会解析这些附加的头信息,同时产生Response给客户端,这样客户端与服务端之间的WebSocket链接就连接起来了。
接下来就来介绍OkHttp中怎么使用WebSocket。实例代码如下:
private OkHttpClient mOkHttpClient;
private void webSocketConnect() {
mOkHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("http://192.168.1.1")
.build();
ClientWebSocketListener listener=new ClientWebSocketListener();
mOkHttpClient.newWebSocket(request,listener);
mOkHttpClient.dispatcher().executorService().shutdown();
}
既然使用的是OkHttp,那么第一步就得创建OkHttpClient对象,第二步创建Request对象,并设置url地址,第三步创建一个Listener,这个Listener用于客户端与服务端之间的异步通知,第三步通过OkHttpClient的newWebSocket方法建立客服端与服务端之间的连接,最后关闭Dispatcher当中的线程池。
ClientWebSocketListener代码如下:
private WebSocket mWebSocket;
private final class ClientWebSocketListener extends WebSocketListener{
@Override
public void onOpen(WebSocket webSocket, Response response) {
mWebSocket=webSocket;
mWebSocket.send("您好,我是客户端");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
Message message=Message.obtain();
message.obj=text;
mWebSocketHandler.sendMessage(message);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
Message message=Message.obtain();
message.obj=bytes.utf8();
mWebSocketHandler.sendMessage(message);
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
if(null!=mWebSocket){
mWebSocket.close(1000,"再见");
mWebSocket=null;
}
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, @javax.annotation.Nullable Response response) {
}
}
ClientWebSocketListener继承自WebSocketListener接口,并实现该接口中的一些方法。
onOpen方法实在客户端与服务端建立连接时的回调,可以通过WebSocket的send方法向服务端发送消息,由于OkHttp使用的是自己的后台发送数据,所以在send的时候不用担心会阻塞当前线程。
两个onMessage方法,只是传入类型不同,可以在这边获取服务端发送过来的消息,onMessage运行在工作线程,如果需要和UI线程进行交互,就得使用Handler来发送消息给UI线程。
onClosing方法表示服务端不再发送数据给客户端时的回调,准备关闭连接,我们可以在这个方法中关闭WebSocket连接。
onClosed方法表示已经被完全关闭了,这时候回调这个方法。onFailure方法在连接失败的时候会回调这个方法。
搜索微信“顾林海”公众号,定期推送优质文章。