A, sequence

OkHttp is probably the most widely used network library on Android, and it’s usually used for HTTP requests, but it actually supports Websockets and is very easy to use.

This article will discuss some details of WebSocket implementation using OkHttp, including the introduction of WebSocket, and how to do authentication before transmission, long connection survival and its principles.

Second, WebSocket introduction

2.1 Why WebSocket is Used?

When we do client development, the most contact application layer network protocol is HTTP protocol, and the WebSocket introduced today is also based on TCP protocol, which is a lightweight network communication protocol and also belongs to the application layer protocol.

WebSocket, like HTTP/2, is actually born to solve some defects of HTTP/1.1, and WebSocket is aimed at the “request-reply” mode of communication defects in the “half duplex” mode.

“Request-reply” is a “half duplex” communication mode, data transmission must go through a request response, this complete communication process, communication at the same time data can only be transmitted in one direction. Its biggest problem is that HTTP is a passive communication mode, the server must wait for the client to request before returning data, can not actively send data to the client.

As a result, before WebSocket appeared, some services that required real-time performance were usually implemented based on a simple mode like Polling. Polling is when the client periodically initiates a request. If the server has data that needs to be passed, it can use this request to respond to data.

The disadvantage of polling is also very obvious, a lot of idle time, in fact, is repeatedly sending invalid requests, which is obviously a waste of resources.

Although later HTTP/2 and HTTP/3 have added features such as Stream and Server Push to address this half-duplex defect, “request-reply” is still the main communication mode of HTTP protocol.

WebSocket protocol is defined by THE HTML5 specification, originally designed for browsers, can avoid the same-origin restriction, browsers can communicate with any server, modern browsers have basically supported WebSocket.

Although WebSocket is originally defined in HTML5, it is also suitable for mobile terminals. Although mobile terminals can communicate directly with servers through sockets, WebSocket can communicate with port 80 (HTTP) or 443 (HTTPS). Effectively avoid some firewall interception.

WebSocket is a true full-duplex mode, also known as “long connection”. After the handshake connection is completed, both the client and server can initiate the request and reply the response actively, and the transmission on both sides is independent of each other.

2.2 WebSocket Features

WebSocket data transmission is based on TCP protocol, but before transmission, there is a process of shaking hands, both parties confirm the eyes, can formally transmit data.

WebSocket’s handshake process, in line with its “Web” nature, is implemented using HTTP’s own “protocol upgrade”.

Before establishing a connection, the client also needs to know the address of the server. WebSocket does not take a different approach. Instead, it uses the URL format of HTTP, but the protocol identifier is changed to “WS” or “WSS”, indicating the plaintext and encrypted WebSocket protocols respectively. This is similar to HTTP and HTTPS.

Here are some examples of WebSocket urls:

ws://cxmydev.com/some/path
ws://cxmydev.com:8080/some/path
wss://cxmydev.com:443?uid=xxx
Copy the code

After the connection is established, WebSocket transmits data in the form of binary frames, commonly used including the data frame MESSAGE used for data transmission and three control frames:

  • PING: active PING frame.
  • PONG: Reply after receiving PING frame;
  • CLOSE: actively closes the WebSocket connection.

For more details about the WebSocket Protocol, please refer to the WebSocket Protocol Specification.

With this basic knowledge, we can basically use WebSocket without falling into a pit.

Let’s summarize the WebSocket features:

  1. WebSocket is based on TCP protocol and is friendly to the server.
  2. The default port is 80 or 443. The handshake phase uses HTTP protocol, which is not easily shielded by the firewall and can pass various HTTP proxy servers.
  3. Data transmission is lighter than HTTP, with fewer HTTP headers, lower performance overhead and more efficient communication.
  4. MESSAGE frames can be used to send text or binary data. If the data is too large, it will be divided into multiple MESSAGE frames.
  5. WebSocket uses HTTP URLS and the protocol identifier is “WS” or “WSS”.

So let’s look at how to use WebSocket with OkHttp.

OkHttp for WebSocket

3.1 Establishing a WebSocket Connection

WebSocket is easy to implement with OkHttp. Its OkHttpClient provides the newWebSocket() method, which directly establishes a WebSocket connection and completes communication.

fun connectionWebSockt(hostName:String,port:Int){
  val httpClient = OkHttpClient.Builder()
      .pingInterval(40, TimeUnit.SECONDS) // Set the PING frame sending interval
      .build()
  val webSocketUrl = "ws://${hostName}:${port}"
  val request = Request.Builder()
      .url(webSocketUrl)
      .build()
  httpClient.newWebSocket(request, object:WebSocketListener(){
    // ...})}Copy the code

I’m sure those familiar with OkHttp will have no problem with the above code, except that the URL is replaced with the “WS” protocol identifier. In addition, you need to configure pingInterval(), which is covered in more detail later.

When newWebSocket() is called, the WebSocket connection begins, but the core operations are in the abstract class WebSocketListener.

3.2 use WebSocketListener

WebSocketListener is an abstract class that defines a number of methods with which all operations on the WebSocket can be performed through method callbacks.

var mWebSocket : WebSocket? = null
fun connectionWebSockt(hostName:String,port:Int){
  // ...
  httpClient.newWebSocket(request, object:WebSocketListener(){
    override fun onOpen(webSocket: WebSocket, response: Response) {
      super.onOpen(webSocket, response)
      // The WebSocket connection is established
      mWebSocket = webSocket
    }

    override fun onMessage(webSocket: WebSocket, text: String) {
      super.onMessage(webSocket, text)
      // Received a String message from the server
    }

    override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
      super.onClosing(webSocket, code, reason)
      // The server is ready to CLOSE the connection
    }

    override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
      super.onClosed(webSocket, code, reason)
      // The WebSocket connection is closed
    }

    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?). {
      super.onFailure(webSocket, t, response)
      / / make a mistake}})}Copy the code

All WebSocketListener method callbacks contain an object of type WebSocket, which is the currently established WebSocket connection entity through which to send WebSocket messages to the server.

If you need to send messages at other times, you can save the webSocket object for later use when the onOpen() callback completes the connection.

WebSocket in OkHttp is itself an interface, and its implementation class is RealWebSocket, which defines methods for sending messages and closing connections:

  • Send (text) : Sends a String message.
  • Send (bytes) : sends a binary message.
  • Close (code, reason) : Actively closes the WebSocket connection;

Using these callbacks and methods provided by WebSocket, we can complete WebSocket communication.

3.3 the Mock WebSocket

Sometimes OkHttp also provides an extended MockWebSocket service to simulate the server for our testing purposes.

MockWebSocket requires additional Gradle references, preferably consistent with the OkHttp version:

api 'com. Squareup. Okhttp3: okhttp: 3.9.1'
api 'com. Squareup. Okhttp3: mockwebserver: 3.9.1'
Copy the code

MockWebServer is also very simple to use, just using the MockWebSocket class.

var mMockWebSocket: MockWebServer? = null
fun mockWebSocket(a) {
  if(mMockWebSocket ! =null) {
    return} mMockWebSocket = MockWebServer() mMockWebSocket? .enqueue(MockResponse().withWebSocketUpgrade(object : WebSocketListener() {

    override fun onOpen(webSocket: WebSocket, response: Response) {
      super.onOpen(webSocket, response)
      // Callback if there is a client connection
    }

    override fun onMessage(webSocket: WebSocket, text: String) {
      super.onMessage(webSocket, text)
      // Callback when a new message is received
    }

    override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
      super.onClosing(webSocket, code, reason)
      // Callback when the client actively shuts down
    }

    override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
      super.onClosed(webSocket, code, reason)
      // The WebSocket connection is closed
    }

    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?). {
      super.onFailure(webSocket, t, response)
      / / make a mistake}}}))Copy the code

The Mock WebSocket server still needs to use the WebSocketListener we talked about earlier.

You can then use mMockWebSocket to get the IP and port of the Mock service.

valhostName = mMockWebSocket? .getHostName()valport = mMockWebSocket? .getPort()val url = "ws:${hostName}:${port}"
Copy the code

Note that both methods need to be called from a child thread, otherwise an exception will be received.

Although it is not necessary to Mock a server when the server is complete, it is still recommended that you Mock a server locally, log in, and watch a complete WebSocket connection and send messages.

3.4 How Do I Authenticate WebSocket

Let’s talk about WebSocket connection authentication.

The so-called authentication is actually for security reasons, so that anyone can connect to WebSocket after the server starts the connection service, which will definitely cause some security problems. Second, the server also needs to match the WebSocket connection entity with a real user, otherwise the business is not guaranteed.

The question then comes back to, how and when do you pass some business data to the server during the entire process of WebSocket communication? Of course, after the WebSocket connection is established, it is necessary to send some authentication data to the server immediately, but it is obviously not elegant enough.

As mentioned above, WebSocket uses HTTP “protocol upgrade” in the handshake phase, which is essentially HTTP packets sending some special header data to complete the protocol upgrade.

In RealWebSocket, for example, there are procedures for constructing headers, such as Upgrade, Connection, and so on.

public void connect(OkHttpClient client) {
  // ...
  final Request request = originalRequest.newBuilder()
    .header("Upgrade"."websocket")
    .header("Connection"."Upgrade")
    .header("Sec-WebSocket-Key", key)
    .header("Sec-WebSocket-Version"."13")
    .build();
  //....
}
Copy the code

In fact, in the WebSocket phase, some authentication data, such as UID and token, can be transmitted through the Header. The specific method is to add the Header to the Request when constructing it. There are no examples here.

In addition, WebSocket URLS can also carry parameters.

wss://cxmydev.com:443?uid=xxx&token=xxx
Copy the code

3.5 the WebSocket keep alive

The connection established by WebSocket is what we call a long connection. Each connection is a resource to the server. But the server tends to close a connection when it has not received messages for a long time. In fact, the keepalive of WebSocket is to periodically send an empty message to the server to ensure that the connection will not be disconnected by the server.

Then we write a timer and send a MESSAGE to the server mwebsocket.send () at a fixed interval, which can achieve the purpose of keeping alive. However, the MESSAGE frame is actually sent in this way. If we use WebSocket, there is a more elegant way.

As mentioned earlier, WebSocket transmits data in binary frames, including PING frames for keepalive, whereas OkHttp automatically sends PING frames and data at intervals with a simple configuration.

We only need to set the PING frame sending interval with pingInterval() when constructing OkHttpClient, which defaults to 0, so no setting.

val httpClient = OkHttpClient.Builder()
      .pingInterval(40, TimeUnit.SECONDS) // Set the PING frame sending interval
      .build()
Copy the code

The value must be negotiated with the server. It is generally recommended to set a value less than 60 seconds.

The logic is in the RealWebSocket class.

public void initReaderAndWriter(String name, Streams streams) throws IOException {
  synchronized (this) {
    // ...
    if(pingIntervalMillis ! =0) {
      executor.scheduleAtFixedRate(
        new PingRunnable(), pingIntervalMillis, pingIntervalMillis, MILLISECONDS);
    }
    // ...
  }
  // ...
}
Copy the code

PingRunnabel eventually calls writePingFrame() at intervals to write PING frames to the WebSocketWriter to keep the server connection alive.

Four, summary

This article covers WebSocket and how to implement WebSocket support using OkHttp.

Here’s a quick summary:

  1. WebSocket is a full-duplex long-connection application layer protocol, which can be used to achieve active push communication from the server to the client.
  2. The key to using WebSocket in OkHttp is thisnewWebSocket()Methods andWebSocketListenerThis abstract class, after the final connection is established, can pass throughWebSocketObject to send a message to the peer end;
  3. WebSocket authentication can be implemented by adding headers or URL parameters to HTTP requests in the handshake phase.
  4. To keep WebSocket alive, PING frames need to be sent periodically. The interval for sending PING frames can be passed in OkHttppingInterval()Method setting;

As an added bonus, OkHttp added WebSocket support in V3.4.1. The previous version required the OKHTTP-WS extension library to support it, but that was in 2016 and I don’t think anyone is using that old version of OkHttp anymore.

Did this article help you? Message, forward, favorites is the biggest support, thank you! If the data in this article is good, I will share an explanation of the implementation of WebSocket and the WebSocket protocol in OkHttp.

Reference:

  • WebSocket tutorial: http://www.ruanyifeng.com/blog/2017/05/websocket.html
  • The WebSocket Protocol:https://tools.ietf.org/html/rfc6455#page-37

Recommend:

  • Replacing Android? Fuchsia OS, Google’s new system, will soon be finalised, detailing the choice of programming languages
  • AS 3.6 stable release has been released. What changes does the new version bring?
  • Comics: talk about thread growth/recycle strategies in thread pools

“Growth”, will get the learning materials I prepared.