当前位置: 首页 > 科技观察

使用OkHttp实现WebSocket长连接

时间:2023-03-15 10:41:09 科技观察

今天给大家带来一篇旧文,介绍WebSocket,看懂就知道了。前言最近老板提出了一个新需求。做一个物联网相关的APP,其中一个要求就是客户端需要时不时地向服务器发送和接收消息。内部操作系统:这是怎么做到的?通过界面轮询?定时访问接口,有数据就更新?不行不行,这样既浪费资源又耗电,会导致很多请求都是无效的网络操作。长连接怎么样?WebSocket协议似乎不错。通过握手建立长连接后,就可以随时向服务器发送和接收消息了。就是这样!如何整合呢?前段时间查看OkHttp源码的时候,发现它支持Websocket协议,那就试试吧!我们走吧!WebSocket简介下面简单介绍一下WebSocket。我们都知道Http是一种应用层的通信协议,但是它只支持单向主动通信,服务端不能主动向客户端推送消息。而且Http是无状态的,即每次通信都没有关联,导致与服务器的关系松散。为了解决长期以来与服务器通信的痛点,HTML5规范引出了WebSocket协议(你知道这个名字是怎么来的吗,它是从HTML5规范衍生出来的,跟老爸姓),也就是基于TCP协议的全双工协议。通信协议。和Http一样属于应用层协议,下层还是需要通过TCP建立连接。但是,TCP连接建立后,WebSocket需要通过Http进行握手,即通过Http向服务器发送GET请求报文,告诉服务器我要建立WebSocket连接,你准备好了。具体方法是在header信息中添加相关参数。然后服务器响应我知道,并且将连接协议改为WebSocket,开始建立长连接。这里贴上请求头和响应头信息,从网上找张图片:3851594110877_.pic.jpg简单说明一下参数:URL一般以ws或wss开头,ws对应Websocket协议,wss对应WebSocketover安全协议。类似于Http和Https的关系。请求方式为GET方式。Connection:Upgrade,表示客户端要连接升级,不使用Http协议。Upgrade:websocket,表示客户端需要升级才能建立Websocket连接。Sec-Websocket-Key:key,这个key是随机生成的,服务端会通过这个参数来验证请求是否有效。Sec-WebSocket-Version:13,websocket使用的协议,一般为13。Sec-webSocket-Extension:permessage-deflate,客户端指定的一些扩展协议,比如这里的permessage-deflate是WebSocket的一种压缩协议。响应码为101,表示响应协议升级,后续数据交互按照Upgradet指定的WebSocket协议进行。OkHttp实现添加OkHttp依赖implementation("com.squareup.okhttp3:okhttp:4.7.2")实现代码首先初始化OkHttpClient和WebSocket实例:/***InitializeWebSocket*/publicvoidinit(){mWbSocketUrl="ws://echo.websocket.org";mClient=newOkHttpClient.Builder().pingInterval(10,TimeUnit.SECONDS).build();Requestrequest=newRequest.Builder().url(mWbSocketUrl).build();mWebSocket=mClient.newWebSocket(request,newWsListener());}这里主要配置OkHttp的一些参数和WebSocket的连接地址。newWebSocket方法是初始化和连接WebSocket。这里需要注意的是pingInterval方法的配置,主要用于设置WebSocket连接的保活。相信做过长连接的同学都知道,长连接一般每隔几秒发一条消息告诉服务器我在线,服务器会回复一条消息表示已经收到,这就确认连接成功了是正常的。客户端和服务器都在线。如果服务器没有及时收到这条消息,服务器可能会主动关闭连接以节省资源。如果客户端没有正常收到返回的消息,也会进行一些重连等操作,所以这个keep-alive消息很重要。我们称这个消息为心跳包,一般用PING和PONG来表示,就像乒乓球一样,来来去去。所以这里的pingInterval就是设置发送心跳包的时间间隔。设置该方法后,OkHttp会自动为我们发送心跳包事件,即ping包。当间隔时间到了,没有收到pong包,就会调用监听事件中的onFailure方法,此时我们可以重连。但是由于实际业务需求不同,okhttp心跳包事件给我们的权限较小,我们也可以自己完成心跳包事件,即WebSocket连接成功后,开始定时发送ping包,检查在发送下一个ping包之前,是否已经收到上一个pong包,如果没有,则视为异常,开始重连。有兴趣的同学可以看看文末的相关源码。连接建立后,我们就可以正常发送和读取消息了,如上图WsListener监听事件:,@NotNullStringreason){super.onClosed(webSocket,code,reason);}@OverridepublicvoidonClosing(@NotNullWebSocketwebSocket,intcode,@NotNullStringreason){super.onClosing(webSocket,code,reason);}@OverridepublicvoidonFailure(@NotNull@WebSocketwebSocket,@NotNullThrowableNullableResponseresponse){super.onFailure(webSocket,t,response);}@OverridepublicvoidonMessage(@NotNullWebSocketwebSocket,@NotNullStringtext){super.onMessage(webSocket,text);Log.e(TAG,"客户端收到消息:"+text);onWSDataChanged(DATE_NORMAL,text);//测试发送消息webSocket.send("Iamaclient,hello");}@OverridepublicvoidonMessage(@NotNullWebSocketwebSocket,@NotNullByteStringbytes){super.onMessage(webSocket,bytes);}@OverridepublicvoidonOpen(@NotNullWebSocketwebSocket,@NotNullResponseresponse){super.onOpen(webSocket,response);Log.e(TAG,"连接成功!");}}//发送字符串消息publicvoidsend(finalStringmessage){if(mWebSocket!=null){mWebSocket.send(message);}}/***发送字节消息*@parammessage*/publicvoidsend(finalByteStringmessage){if(mWebSocket!=null){mWebSocket.send(message);}}//主动断开连接publicvoiddisconnect(intcode,Stringreason){if(mWebSocket!=null)mWebSocket.close(code,reason);}这里要注意,回调方法都是在子线程回调,如果需要更新UI,需要切换到主线程,基本操作就这么多,还是很简单的,初始化Websocket-connection-连接成功-发送和接收消息,其中,WebSocket类是一个操作接口,主要提供以下方法send(text:String)发送String类型的消息send(bytes:ByteString)发送二进制类型的消息close(code:Int,reason:String?)关闭WebSocket连接如果有同学想测试WebSocket的功能,但是没有实际的s永远,他们该怎么办?其实OkHttp官方有一个MockWebSocket服务可以用来模拟服务端。一起来试试:模拟服务器先集成MockWebSocket服务库:implementation'com.squareup.okhttp3:mockwebserver:4.7.2'然后就可以新建一个MockWebServer,添加MockResponse作为接收消息的响应。MockWebServermMockWebServer=newMockWebServer();MockResponseresponse=newMockResponse().withWebSocketUpgrade(newWebSocketListener(){@OverridepublicvoidonOpen(@NotNullWebSocketwebSocket,@NotNullResponseresponse){super.onOpen(webSocket,response(clientconnection).//,"服务端收到客户端连接成功回调:");mWebSocket=webSocket;mWebSocket.send("我是服务器,你好");}@OverridepublicvoidonMessage(@NotNullWebSocketwebSocket,@NotNullStringtext){super.onMessage(webSocket,text);Log.e(TAG,"服务器收到消息:"+text);}@OverridepublicvoidonClosed(@NotNullWebSocketwebSocket,intcode,@NotNullStringreason){super.onClosed(webSocket,code,reason);Log.e(TAG,"onClosed:");}});mMockWebServer.enqueue(响应);这里,服务端收到客户端的连接成功消息后,向客户端发送消息。需要注意的是这段代码要在子线程中执行,因为主线程不能进行网络操作。然后就可以初始化Websocket客户端了://获取连接url,初始化websocket客户端StringwebsocketUrl="ws://"+mMockWebServer.getHostName()+":"+mMockWebServer.getPort()+"/";WSManager。getInstance().init(websocketUrl);ok,运行项目//运行结果E/jimu:mWbSocketUrl=ws://localhost:38355/E/jimu:服务器收到客户端连接成功回调:E/jimu:连接成功!E/jimu:客户端收到消息:我是服务端,你好E/jimu:服务端收到消息:我是客户端,你好参考https://github.com/square/okhttp来自微信公众号「码上码,可以通过以下二维码关注。转载本文请联系码尚砂公众号。