1, the introduction of

The official website has related introduction, but from the perspective of a developer, this is far from enough, should be from a professional point of view to know what it is?

A Java network request library; Based on THE TCP connection, the Socket interface group is used to request. Realized two kinds of protocol network request HTTP, websocket; TLS protocol can be used for encryption; It can also realize proxy, certificate verification, basic Auth verification, etc

Therefore, the basic design of the network transmission layer, common aspects of the application layer; Involve knowledge direction;

  • The HTTP protocol
  • Websock agreement
  • The TLS protocol
  • TCP protocol
  • socket
  • certificate
  • Basic certification, third-party certification
  • Encryption, encoding

These basic knowledge, is to teach people to fish fish; Much of this was prescribed by the older powers, which we are only slowly coming to understand; In fact, the biggest takeaway from reading okHTTP source code is that how do you complete a set of protocol implementation, this idea is very important, to complete these ideas must know the basis is also very important

2, basic

2.1 Network Layering

I think network layering is important because it sets a basic set of rules that everyone follows, and makes it easier for everyone to work together. It is also conducive to the promotion of new agreements to solve pain points;The figure above shows the corresponding relationship between the three hierarchical models; Basically we use five or four tiers; The hierarchical model effectively decouples the dependencies of each level. Here’s a quick rundown of what each layer does:

** 1. Physical layer **

The lowest layer of the model. It is the data transmission medium of network communication. It consists of cables and equipment connecting different nodes. The main function is to use the transmission medium to provide physical connection for the data link layer, responsible for processing data transmission and monitoring data error rate, so as to transparent transmission of data flow.

** 2. Data link layer **

The main function is: on the basis of the services provided by the physical layer, establish data link connection between the communication entities, transmit data packets in the unit of “frame”, and use error control and flow control methods to make the physical line with error into error-free data link.

** 3. Network layer **

The main functions are: create logical links for data transmission between nodes, select the most appropriate path for packets through communication subnets through routing algorithm, and realize congestion control, network interconnection and other functions.

** 4. Transport layer **

The main function is to provide users with a reliable end-to-end service that handles packet errors, packet ordering, and other critical transport issues. The transport layer hides the details of lower levels of data communication from higher levels and is therefore a critical layer in computer communication architecture.

** 5. Session layer **

The main functions are: responsible for maintaining the transmission link between two nodes, so as to ensure the uninterrupted point-to-point transmission, and managing data exchange and other functions.

** 6. Presentation layer **

The main functions are: used to deal with the representation of information exchanged in two communication systems, mainly including data format transformation, data encryption and decryption, data compression and recovery and other functions.

** 7. Application layer **

The main functions are: it provides many services for application software, such as file server, database service, E-mail and other network software services.

From the perspective of Java, socket interface group based on TCP, UDP abstract has been provided, and most of the customized network needs can be made from socket, so, generally speaking, need to pay special attention to socket, the upper layer protocol;

2.2 the TCP/IP protocol

Not only TCP and IP, but all the protocols used for communication over IP. Specifically, IP or ICMP, TCP or UDP, TELNET or FTP, and HTTP are all TCP/IP protocols. Common protocols are IP, TCP, UDP, and WebSocket, all of which have protocol packet formats.

** IP protocol **

Network layer protocol; The delivery time and reliability of packets are not guaranteed, and the transmitted packets may be lost, repeated, delayed or out of order. It mainly includes three aspects: IP address scheme, packet encapsulation format and packet forwarding rules

** TCP protocol **

Transport layer protocol is a connection-oriented, reliable, byte stream based transport layer communication protocol. Designed to accommodate layered protocol hierarchies that support multi-network applications. TCP is relied on to provide reliable communication services between pairs of processes in a host computer connected to different but interconnected computer communication networks.

** UDP protocol **

Transport layer protocol, a connectionless transport layer protocol that does not establish a connection between the source end and the terminal before transmitting data, providing a simple, transaction-oriented unreliable information transfer service

** HTTP protocol **

Application layer protocol; One based on TCP/IP communication protocol to transfer data (HTML files, image files, query results, etc.); Because of its simple and fast way, it is suitable for distributed hypermedia information system

** WebSocket protocol **

Application layer protocol, a protocol for full duplex communication over a single TCP connection; WebSocket makes it easier to exchange data between the client and the server, allowing the server to actively push data to the client. A handshake between the browser and the server is all it takes to create a persistent connection and two-way data transfer. Websocket uses the 101 status code of the HTTP/1.1 protocol for handshake.

** TLS protocol **

Secure Transport Layer protocol; Application protocol-independent, used to provide confidentiality and data integrity between two communication applications; The first step is to shake hands, confirm whether the server or server-client is expected, and determine the communication encryption mode and key, followed by encryption mode + key for data transmission

2.3 the HTTP protocol

Defines how a Web client requests a Web page from a Web server and how the server delivers the Web page to the client. The HTTP protocol uses a request/response model. The client sends a request packet to the server containing the request method, URL, protocol version, request header, and request data. The server responds with a status line containing the protocol version, success or error code, server information, response headers, and response data.

2.3.1 HTTP Request Packets

Request line + request header + request bodyIt has the following characteristics:

  • Each line ends with a carriage return character and a newline character
  • Each line in the request header is in the same format
  • After the end of the request header, add a line containing only carriage return and line feed
  • The request body is not required

2.3.2 HTTP Response Packets

Response line + response header + response bodyIt has the following characteristics:

  • Each line ends with a carriage return character and a newline character
  • Each line in the response header is in the same format
  • After the end of the response header, add a line containing only carriage return and line feed
  • The response body is not required

2.3.3 Request method

  • GET

Makes a Show request to the specified resource. Using the GET method should only be used to read data and should not be used for “side effects” operations

  • HEAD

Like the GET method, it makes a request to the server for a specified resource. Only the server will not return the text portion of the resource. The advantage is that you can retrieve “information about the resource” (meta information or metadata) without having to transfer the entire content.

  • POST

Submit data to a specified resource, asking the server to process it (for example, submit a form or upload a file). The data is included in the request article. This request may create a new resource or modify an existing resource, or both.

  • PUT

Uploads its latest content to the specified resource location.

  • DELETE

Requests the server to remove the resource identified by request-URI.

  • TRACE

The command output displays the requests received by the server for testing or diagnosis.

  • OPTIONS

This method causes the server to return all HTTP request methods supported by the resource. Use ‘*’ instead of the resource name to send an OPTIONS request to the Web server to test whether the server functions properly.

  • CONNECT

Reserved in HTTP/1.1 for proxy servers that can pipe connections. Typically used for links to SSL encrypted servers (via an unencrypted HTTP proxy server).

2.3.4 certification

** Basic auth** : A form of login authentication that allows a Web browser or other client program to provide credentials in the form of a user name and password upon request; Almost all popular Web browsers support basic authentication, but it is usually used on trusted networks. If the connection is not trusted, authentication is not secure. Add header information to HTTP request packets:

Authorization: Basic YWRtaW46YWRtaW4= // Authorization: "Base64 encrypted string for Basic username and password"Copy the code

And the code above can resolve the 401 error; If the error is 407, you need to use the HTTP proxy-authenticate attribute, and the values are consistent. If the server fails to Authenticate, it uses the response header field www-authenticate to explain the reason

** OAuth authentication **: The OAuth protocol provides a secure, open and easy standard for user resource authorization. Different from previous authorization methods, OAUTH authorization does not allow the third party to touch the user’s account information (such as user name and password), that is, the third party does not need to use the user’s user name and password to apply for the authorization of the user resources, so OAUTH is safe. The basic process is shown below

2.3.5 HTTP three-way handshake

2.3.6 Wave four times

2.3.7 HTTP cache

HTTP caches include strong cache and negotiation cache.

HTTP caching means that when a client requests a resource from the server, it arrives in the browser cache first, and if the browser has a copy of the resource to request, it can fetch the resource directly from the browser cache rather than from the original server. Common HTTP caches can only cache resources in response to GET requests, not other types of responses, so the subsequent request caches refer to GET requests.

HTTP caching always starts with the second request. The first time a resource is requested, the server returns the resource and its cache parameters in the Respone Header header. On the second request, the browser determines whether the request parameters hit the strong cache and sends them to the server as 200. Otherwise, the browser adds the request parameters to the request header and sends them to the server to see if the negotiation cache is hit. If 304 is hit, the server returns a new resource. HTTP cache header parameters are as follows:

2.3.8 url encoding

The query part of the URL request and the form value pair in the request body should be displayed in ASCII code. Only letters and numbers [0-9a-za-z], some special symbols “$-_.+! *'(), “[excluding double quotes], and reserved words can be used unencoded in urls. RFC 1738 does not specify specific coding methods, so it is confusing; Then illegal characters need to be processed; Considering the Chinese language; Here is how Okhttp is handled

The character string —–> utf8 encoding ——> then the name/value character string confirmation ——> does not meet the conditions convert to % hexadecimal (one digit to two digits) The following characters must be converted to the hexadecimal % semicolon format

  1. The value contains special characters less than 32 characters
  2. The special character is 127
  3. “‘ :; < = > @ [] ^ ` {} | /? # &! $(),~ these special strings
    • –>

Final value pair, split value pair with &, name=value; For example, under the

https://www.baidu.com/s?wd=ascii%E7%A0%81%E5%AF%B9%E7%85%A7%E8%A1%A8&rsv_spt=1&rsv_iqid=0x9cde902e00267942&issp=1&f=3&rs v_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&rsv_dl=ts_1&oq=url%25E7%25BC%2596%25E7%25A0%2581&rsv_bty pe=t&inputT=4274&rsv_t=1e26hAToVzHm03kNMXEMGa5yj9Lg%2FGLlPiWj6bRe%2FMMtSzQdI2ys8h8qjdP3vsqEwb4a&rsv_pq=b5c4fa78002cb7bd& rsv_sug3=37&rsv_sug1=33&rsv_sug7=100&rsv_sug2=1&prefixsug=AS%2526lt%253Bii&rsp=1&rsv_sug4=4816Copy the code

2.3.9 http2

SPDY is a TCP based transport layer protocol developed by Google to minimize network latency, improve network speed, and optimize users’ network experience. SPDY is not intended as an alternative to HTTP, but rather an enhancement of the HTTP protocol. Features of the new protocol include data stream multiplexing, request prioritization, and HTTP header compression.

HTTP/2 is developed on the basis of SPDY/2. Compared to HTTP/1.x, HTTP/2 has made major changes and optimizations in the underlying transport:

  • HTTP/2 transmits data in binary format rather than HTTP/1.x text format.
  • HTTP/2 uses HPACK for compressed transmission of message headers, which can save the network traffic occupied by message headers.
  • Multiplexing, in plain English, means that all requests are made concurrently over a TCP connection.
  • Server Push: The Server can Push resources to the client faster

Frame is the basis of the HTTP/2 binary format and can be thought of as its TCP packet. HTTP/2 has so many new features because of changes in the underlying data format. The basic format of a Frame is as follows (source okhttp)

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Length (24) | +---------------+---------------+---------------+ | Type (8) | Flags (8) | +-+-+-----------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0...) . +---------------------------------------------------------------+Copy the code
  • Length: indicates the Length of the Frame Payload. The Length of the Frame Header is a fixed 9 bytes (Length + Type + Flags + R + Stream Identifier = 72 bits).
  • Type: Determines whether the data stored in this Frame Payload belongs to the HTTP Header or HTTP Body. In addition, HTTP/2 defines some other Frame types. For example, when this field is 0, it represents the DATA Type (i.e. the Body part of HTTP/1.x).
  • Flags: A total of 8 bits, each of which serves as the flag. Each different Frame Type has a different Frame Flags. For example, when the last Frame of type DATA is sent, the last bit of Flags is set to 1 (Flags &= 0x01), indicating END_STREAM, indicating that this Frame is the last packet of the stream.
  • R: Reserved bit.
  • Stream Identifier: Stream ID. When the client and server establish a TCP connection, they will send a Stream with Stream ID = 0 to do some initialization work. The client and server then send the request/response starting at 1.

Frame Consists of Frame Header and Frame Payload. Whether it’s the old HTTP Header or HTTP Body, in HTTP/2, you store that data into Frame Payload, Frame by Frame, and send the response/request. The Frame Type is distinguished by the Type in the Frame Header.

HTTP/2 Header compression uses HPACK, which uses an index table to define commonly used HTTP headers. Store commonly used HTTP headers in a table. When requested, it only needs to send the index location in the table. Not only does it reduce the amount of data by indexing key-value pairs, but it also compresses the string size by Huffman encoding.

2.4 the TLS protocol

A lot of people confuse SSL with this protocol; In fact, SSL is the predecessor of TSL, TSL is developed on the basis of SSL; SSL 1.0 -> SSL 2.0 -> SSL 3.0 = TSL 1.0 -> TSL 1.1 -> TSL 1.2 -> TSL 1.3

This protocol consists of two layers: TLS Record and TLS Handshake.

The TLS recording protocol is used to encapsulate various high-level protocols. One of these encapsulation protocols, the handshake protocol, allows the server and client to authenticate each other and negotiate encryption algorithms and encryption keys before the application protocol transmits and receives its first data byte.

The TLS handshake protocol provides connection security

** TLS handshake **

The figure (not owned by the author, but from the Internet) basically includes all the operations in the handshake process. The process is like this, but there are still many details unknown, if necessary, with Jun in-depth understanding

** TLS certificate **

A digital certificate that complies with the TLS protocol and is issued by a globally trusted certificate authority (CA) after authenticating the server. Used for authentication and encrypted data transmission.

The basic architecture of digital certificate is public key PKI, which uses a pair of keys to implement encryption and decryption. The key includes a private key and a public key. The private key is used for signature and decryption and is user-defined and known only by the user. The public key is used for signature verification and encryption and can be shared by multiple users.

Computers, mobile phones, their systems have root certificates;

2.5 encryption

Encryption algorithm is not symmetric encryption, asymmetric encryption;

** Symmetric encryption ** : Encryption algorithm that uses the same key for encryption and decryption. The advantages of symmetric encryption algorithm lie in the high speed of encryption and decryption and the difficulty of cracking when using long keys

Common symmetric encryption algorithms: DES, 3DES, DESX, Blowfish, IDEA, RC4, RC5, RC6, and AES

** Asymmetric encryption ** : Encryption algorithms using different keys for encryption and decryption, also known as public-private key encryption. Suppose two users want to encrypt and exchange data, and the two parties exchange public keys. One party uses the other party’s public key to encrypt data, and the other party can decrypt data with its own private key.

Common asymmetric encryption algorithms: RSA, ECC (for mobile devices), Diffie-Hellman, El Gamal, DSA (for digital signature)

** Hash algorithm ** : A one-way algorithm in which the user can use the Hash algorithm to generate a unique Hash value of a specified length for the target information, but cannot use the Hash value to retrieve the target information. Therefore, the Hash algorithm is commonly used for irrecoverable password storage and information integrity verification.

Common Hash algorithms are MD2, MD4, MD5, HAVAL, SHA, SHA-1, HMAC, HMAC-MD5, and hMAC-SHA1

And encryption and decryption easy to confuse the concept: encoding and decoding, compression decompression

** Encoding is the process of converting information from one form or format to another, while decoding is the process of restoring information; It can be seen that encryption and decryption is decoding, but decoding is not necessarily encryption and decryption; Common encoding: character encoding, PCM encoding, encryption

** Compression uncompression ** : Compression is a mechanism for reducing the size of a computer file through a specific algorithm. Decompress and restore the file process; Divided into lossy compression, lossless compression; Common rar compressed format, zip, tar, jar, iso, gzip,.bz2

2.6 the websocket protocol

The websocket connection is divided into two parts. The first step is to shake hands through HTTP/HTTPS. The second step is to send messages to each other according to WebSocket packets after a long connection is established

** Handshake request **

HTTP request packets and reply packets are basically fixed

GET url HTTP/1.1
Host: host
Connection: Upgrade
Origin:null
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Copy the code

Server agrees reply

HTTP/1.1 101 Switching Protocols Connection:Upgrade Server: Beetle websocket Server Upgrade: websocket Date:Mon, 26 Mon 2020 23:42:44 GMT Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:content-type Sec-WebSocket-Accept:FCKgUr8c7OsDsLFeJTWrJw6WO8Q=Copy the code

** WebSocket message **

Pictures from the Internet; The content is roughly as follows:

FIN: 1bit, whether it is the last frame of the message RSV 1-3:1 bit, alternate, default 0 opcode: Bit 4, Frame Type 0x00 Continuous Message Fragment 0x01 Text Message Fragment 0x02 Binary Message Fragment 0x03 to 0x07 Reserved test operation for future non-control message fragment 0x08 Connection Closed 0x09 Heartbeat Check Ping 0x0A Heartbeat check pong 0x0B ~ MASK: Defines whether the transmitted data has a MASK. If the value is set to 1, the MASK key must be placed in the masking-key area. The value of this bit is 1 for all messages sent by the client to the server. 7bit indicates the length of transmitted data, in bytes. When the length is 7 bits and the number is 126, the following 2 bytes also represent the data length. When the length is 7 bits and the number is 127, the following 8 bytes also represent the data length. Masking-key: 0 or 4 bits. It takes effect only when the MASK is set to 1. Playload data: refers to the sum of Extension data and Application data. Extension data + Application data. Extension data: If there is no special agreement between the client and the server, the length of Extension data is always 0.Copy the code

2.7 the Socket

The socket is the cornerstone of communication and the basic operation unit of the TCP/IP protocol. A socket can be thought of as an endpoint for two-way communication between processes on different hosts, constituting a programming interface within a single host and across the entire network. Think of it as two network applications communicating, each communicating the endpoint of the connection, which is a logical concept; There are three types of sockets

  1. Stream socket (SOCK_STREAM)

Streaming sockets are used to provide connection-oriented, reliable data transfer services. The service will ensure that data can be delivered error-free, duplication-free, and received in sequence. Stream sockets provide reliable data services because of their use of the Transmission control protocol, or TCP. The process is generally shown as follows

  1. Datagram socket (SOCK_DGRAM)

Datagram sockets provide a connectionless service. The service does not guarantee the reliability of data transmission. Data may be lost or duplicated during transmission, and data cannot be received sequentially. Datagram sockets transmit data using THE UDP protocol. 3. Raw socket (SOCK_RAW) A raw socket can read and write IP packets that are not processed by the kernel. A stream socket can read only TCP data, and a datagram socket can read only UDP data.

2.8 Proxy in Java

Proxy server is an important server security function. It mainly works in the session layer of OSI model, so as to play the role of firewall, but also can achieve the function of climbing the wall, accelerating, etc. The main two related classes in Java, ProxySelector

** Proxy**

Proxy(Proxy.Type type, SocketAddress sa)
Copy the code

There are three types of proxy: DIRECT (DIRECT connection, without Proxy), HTTP (advanced protocol Proxy, HTTP, FTP Proxy), SOCKS (SOCKS V4, V5 Proxy) Proxy can be passed in both the Socket constructor method and urL. openConnection method.

** ProxySelector**, abstract class, the object of this class can be based on the URL you want to connect to automatically select the most appropriate proxy, but the class is abstract class, need to inherit the class to achieve personalized, advanced proxy automatic selection; There are two main abstract methods

  • List SELECT (URI URI) : Given a URI (the parent of the URL, but in practice only the URL) returns a List of proxies best suited to access the URI. The first proxy in the List is preferred, the second is used if not, and the connectFailed method is called if not all
  • ConnectFailed: Handles the connection failure

The system already provides a default selector; We can also set our own,

ProxySelector.setDefault(ProxySelector ps)
Copy the code

In addition to the two methods, Java also provides the following methods for vm environment variables: The corresponding variables are in the format of protocol.xxx; The protocol can be HTTP, HTTPS, sock, or SOCKS. XXX can be the following three types

  • ProxyHost: indicates the IP address of the proxy
  • ProxyPort: indicates the port of the proxy server
  • NonProxyHosts: no need to use the proxy can access url (the host), you can specify multiple, with a | between multiple sites, allows you to use the wildcard * said

Related Settings

static Properties System.getProperties(); Synchronized Object properties.setProperty (String key, String value); // Set the property value and return the old valueCopy the code

That’s it for the basics, let’s go through the okHTTP architecture flow and some details

3. Okhttp architecture process

It is divided into HTTP request and Websocket request. A Websocket request consists of two parts: HTTP request protocol switch. A websocket connection is established and information is exchanged.

HTTP requests can be divided into request content, request connection, and response content

3.1 Request Request object

Contains the content of HTTP request packets. The builder pattern is utilized to encapsulate the request information

final HttpUrl url; final String method; final Headers headers; final @Nullable RequestBody body; final Map<Class<? >, Object> tags; private volatile @Nullable CacheControl cacheControl;Copy the code

HttpUrl: ENCAPSULates URL information. Query values urL-encode information

  http://username:password@hostname:port/path/?query#fragment
Copy the code

-Leonard: What’s the method of the request? -Leonard: What’s the method of the request? CacheControl: Collects and encapsulates the CacheControl field information

** RequestBody** RequestBody abstract class; These include the content-Type (which is resolved by MediaType classes), length, and how the Content is written; Class provides the create static method to add objects; The okHTTP class provides two implementation classes, but you can customize them to implement the following methods

  public abstract @Nullable MediaType contentType();

  public long contentLength() throws IOException {
    return -1;
  }

  public abstract void writeTo(BufferedSink sink) throws IOException;
Copy the code

FormBody: Form content; It’s actually a series of name and value pairs; Each value pair is separated by &, and its name and value need TO be URL encoded

MultipartBody: multiple content bodies; End: \r\n–boundary–\r\n; The middle Content consists of a lot of header Content, but there must be content-Length, Content-Type headers, and lines \r\n for the header and bottom Content; Each content can have its own header, with only opening and closing flags, as opposed to a single request body

3.2 Response Corresponding object

It contains request information, protocol, response code, response information, handshake information, response header, response body, request timestamp, response timestamp, request-event manager, response cache information

  final Request request;
  final Protocol protocol;
  final int code;
  final String message;
  final @Nullable Handshake handshake;
  final Headers headers;
  final @Nullable ResponseBody body;
  final @Nullable Response networkResponse;
  final @Nullable Response cacheResponse;
  final @Nullable Response priorResponse;
  final long sentRequestAtMillis;
  final long receivedResponseAtMillis;
  final @Nullable Exchange exchange;
  private volatile @Nullable CacheControl cacheControl;
Copy the code

Handshake: TLS Handshake information, TLS version, negotiated encryption suite, service certificate, local certificate

ResponseBody: is also an abstract class with a custom implementation that provides the length of the content category, and how to write it

  public abstract @Nullable MediaType contentType();

  public abstract long contentLength();

  public abstract BufferedSource source();
Copy the code

Exchange: request information write, response information write, available socket lookup, event call, whether to process timeout request

3.3 the HTTP request

The OkHttpClient object provides an API to complete request encapsulation;

The newCall method generates a Call object and performs some initialization information

  public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false);
  }
  
  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.transmitter = new Transmitter(client, call);
    return call;
  }
Copy the code

The enqueue method makes asynchronous requests; Asynchronous tasks that are not requested are generated and scheduled in the scheduler

public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  transmitter.callStart();
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Copy the code

If no websocket request is found, the number of requests with the same service is set to 0. If yes, the number is set to 0

void enqueue(AsyncCall call) { synchronized (this) { readyAsyncCalls.add(call); if (! call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host()); if (existingCall ! = null) call.reuseCallsPerHostFrom(existingCall); } } promoteAndExecute(); }Copy the code

Find all tasks that are ready and add AsyncCall that meet the requirements to the run queue and run; The maximum number of requests is 64 by default, and the maximum number of requests for a host is 5 by default, which can be adjusted by yourself. The promoteAndExecute method at Dispatch, if I reference it, I’m going to have a problem with newline writing, forgive me;

Call the AsyncCall execute method for processing

protected void execute() {
    boolean signalledCallback = false;
    transmitter.timeoutEnter();
    try {
      Response response = getResponseWithInterceptorChain();
      signalledCallback = true;
      responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
      if (signalledCallback) {
        Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
      } else {
        responseCallback.onFailure(RealCall.this, e);
      }
    } catch (Throwable t) {
      cancel();
      if (!signalledCallback) {
        IOException canceledException = new IOException("canceled due to " + t);
        canceledException.addSuppressed(t);
        responseCallback.onFailure(RealCall.this, canceledException);
      }
      throw t;
    } finally {
      client.dispatcher().finished(this);
    }
  }
Copy the code

RealCall invokes execute to process synchronization tasks

public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } transmitter.timeoutEnter(); transmitter.callStart(); try { client.dispatcher().executed(this); return getResponseWithInterceptorChain(); } finally { client.dispatcher().finished(this); }}Copy the code

Both synchronous and asynchronous tasks are added to the execution queue. However, asynchronous tasks may be restricted to execution. All article use getResponseWithInterceptorChain method performs specific requests, and remove queue, and check in after the asynchronous task execution

GetResponseWithInterceptorChain method performs the action, is known as the responsibility of the chain, according to data has the following functions: RetryAndFollowUpInterceptor, BridgeInterceptor, CacheInterceptor, ConnectInterceptor, custom duty chain, CallServerInterceptor; By the Interceptor. Chain. Proceed (originalRequest), open the whole process

Response getResponseWithInterceptorChain() throws IOException {
  List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());
  interceptors.add(new RetryAndFollowUpInterceptor(client));
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  interceptors.add(new CacheInterceptor(client.internalCache()));
  interceptors.add(new ConnectInterceptor(client));
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  interceptors.add(new CallServerInterceptor(forWebSocket));

  Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
      originalRequest, this, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());

  boolean calledNoMoreExchanges = false;
  try {
    Response response = chain.proceed(originalRequest);
    if (transmitter.isCanceled()) {
      closeQuietly(response);
      throw new IOException("Canceled");
    }
    return response;
  } catch (IOException e) {
    calledNoMoreExchanges = true;
    throw transmitter.noMoreExchanges(e);
  } finally {
    if (!calledNoMoreExchanges) {
      transmitter.noMoreExchanges(null);
    }
  }
}
Copy the code

Proceed (RealInterceptorChain) : Calls the intercept method of the interceptor corresponding to the current index and adds a new to the RealInterceptorChain object of the next index

public Response proceed(Request request) throws IOException { return proceed(request, transmitter, exchange); } public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; . RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange, index + 1, request, call, connectTimeout, readTimeout, writeTimeout); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); . return response; }Copy the code

RetryAndFollowUpInterceptor: redirect or failures retry mechanism; Some of the objects making the request are initialized, then the request is made, and the results are processed

BridgeInterceptor: Adds necessary HTTP request header information

CacheInterceptor: First, it checks whether the cache result is valid. If yes, it returns; if no, it calls the responsibility chain again. When the responsibility chain ends, the response is cached

ConnectInterceptor: Multiplexes socket links for requests, including TLS handshake

CallServerInterceptor: writes request packets and parses response packets

3.3.1 RetryAndFollowUpInterceptor

public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RealInterceptorChain realChain = (RealInterceptorChain) chain; Transmitter transmitter = realChain.transmitter(); int followUpCount = 0; Response priorResponse = null; while (true) { transmitter.prepareToConnect(request); if (transmitter.isCanceled()) { throw new IOException("Canceled"); } Response response; boolean success = false; try { response = realChain.proceed(request, transmitter, null); success = true; } catch (RouteException e) { if (! recover(e.getLastConnectException(), transmitter, false, request)) { throw e.getFirstConnectException(); } continue; } catch (IOException e) { boolean requestSendStarted = ! (e instanceof ConnectionShutdownException); if (! recover(e, transmitter, requestSendStarted, request)) throw e; continue; } finally { if (! success) { transmitter.exchangeDoneDueToException(); } } if (priorResponse ! = null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build(); } Exchange exchange = Internal.instance.exchange(response); Route route = exchange ! = null ? exchange.connection().route() : null; Request followUp = followUpRequest(response, route); if (followUp == null) { if (exchange ! = null && exchange.isDuplex()) { transmitter.timeoutEarlyExit(); } return response; } RequestBody followUpBody = followUp.body(); if (followUpBody ! = null && followUpBody.isOneShot()) { return response; } closeQuietly(response.body()); if (transmitter.hasExchange()) { exchange.detachWithViolence(); } if (++followUpCount > MAX_FOLLOW_UPS) { throw new ProtocolException("Too many follow-up requests: " + followUpCount); } request = followUp; priorResponse = response; }}Copy the code

An infinite loop is used to process retry, which is followed by a chain of responsibilities to obtain the processing result, and then the decision is made whether to break out of the loop

While infinite loop processing; Jump out condition:

  • The request is successful
  • Number of requests exceeds the threshold
  • The RequestBody object can only be used once. The default is false and can be used multiple times
  • Some return result codes indicate that no retries are required
  • Some exceptions occurred during processing that are not connectable

For error code handling, the case will be re-tried

  • 407 error, the proxy server is a HTTP proxy, OkHttpClient. ProxyAuthenticator object validation return the request object effectively, the default return invalid object
  • 401 error, OkHttpClient authenticator object validation returns the request object effectively, the default return invalid object
  • 301-303 or 307/308 and it is a GET or head request. The redirected URL extracted from the response header Location succeeded
  • 408 error OkhttpClient. RetryOnConnectionFailure to true (the default is false), request body is empty or reusable, 408 error, Retry – for the first time After response header does not exist or value less than or equal to 0
  • 503 Error The first 503 error and retry-after response header does not exist or has a value equal to 0

3.3.2 rainfall distribution on 10-12 BridgeInterceptor

public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); RequestBody body = userRequest.body(); if (body ! = null) { MediaType contentType = body.contentType(); if (contentType ! = null) { requestBuilder.header("Content-Type", contentType.toString()); } long contentLength = body.contentLength(); if (contentLength ! = -1) { requestBuilder.header("Content-Length", Long.toString(contentLength)); requestBuilder.removeHeader("Transfer-Encoding"); } else { requestBuilder.header("Transfer-Encoding", "chunked"); requestBuilder.removeHeader("Content-Length"); } } if (userRequest.header("Host") == null) { requestBuilder.header("Host", hostHeader(userRequest.url(), false)); } if (userRequest.header("Connection") == null) { requestBuilder.header("Connection", "Keep-Alive"); } boolean transparentGzip = false; if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { transparentGzip = true; requestBuilder.header("Accept-Encoding", "gzip"); } List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url()); if (! cookies.isEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies)); } if (userRequest.header("User-Agent") == null) { requestBuilder.header("User-Agent", Version.userAgent()); } Response networkResponse = chain.proceed(requestBuilder.build()); HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); Response.Builder responseBuilder = networkResponse.newBuilder() .request(userRequest); if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) { GzipSource responseBody = new GzipSource(networkResponse.body().source()); Headers strippedHeaders = networkResponse.headers().newBuilder() .removeAll("Content-Encoding") .removeAll("Content-Length") .build(); responseBuilder.headers(strippedHeaders); String contentType = networkResponse.header("Content-Type"); responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody))); } return responseBuilder.build(); }Copy the code

Add the necessary request header information based on the request content

  • If the request body is not empty, request Encoding Type content-Type, Length Content-Length or ransfer-encoding: chunked is added
  • Example Add Host request header information
  • Added the long Connection request header Connection: keep-alive
  • Added the accept-encoding: gzip if the accept-encoding and Range are not set when requested
  • Add the User ID: user-agent
  • Add url cache header. Default cache none

After the header information is added, the subsequent responsibility chain processing is carried out. And the results are processed:

  1. Set-cookie is processed, and nothing is done if the Cookie handler object is the default
  • Resolve value pairs, expiration information Expires, max-age, Domain, PATH, Secure, httpOnly
  1. If the request receives a GZIP and the result returns one, the response header removes content-Encoding and Content-Length
  2. Return response body

3.3.3 CacheInterceptor

Response intercept(Chain chain) throws IOException { Response cacheCandidate = cache ! = null ? cache.get(chain.request()) : null; long now = System.currentTimeMillis(); CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); Request networkRequest = strategy.networkRequest; Response cacheResponse = strategy.cacheResponse; if (cache ! = null) { cache.trackResponse(strategy); } if (cacheCandidate ! = null && cacheResponse == null) { closeQuietly(cacheCandidate.body()); } if (networkRequest == null && cacheResponse == null) { return new Response.Builder() .request(chain.request()) .protocol(Protocol.HTTP_1_1) .code(504) .message("Unsatisfiable Request (only-if-cached)") .body(Util.EMPTY_RESPONSE) .sentRequestAtMillis(-1L) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); } if (networkRequest == null) { return cacheResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build(); } Response networkResponse = null; try { networkResponse = chain.proceed(networkRequest); } finally { if (networkResponse == null && cacheCandidate ! = null) { closeQuietly(cacheCandidate.body()); } } if (cacheResponse ! = null) { if (networkResponse.code() == HTTP_NOT_MODIFIED) { Response response = cacheResponse.newBuilder() .headers(combine(cacheResponse.headers(), networkResponse.headers())) .sentRequestAtMillis(networkResponse.sentRequestAtMillis()) .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis()) .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); networkResponse.body().close(); cache.trackConditionalCacheHit(); cache.update(cacheResponse, response); return response; } else { closeQuietly(cacheResponse.body()); } } Response response = networkResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); if (cache ! = null) { if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) { CacheRequest cacheRequest = cache.put(response); return cacheWritingResponse(cacheRequest, response); } if (HttpMethod.invalidatesCache(networkRequest.method())) { try { cache.remove(networkRequest); } catch (IOException ignored) { } } } return response; }Copy the code
  1. Caching is not required by default. If the cache is set to only-if-cached, the result is returned; otherwise, a 504 error is returned
  2. Make the remaining chain of responsibility requests
  3. If the return code is not 304, it is cached. 304 closes the response body; Return processing results; 304 Resources are not modified on the server and the cache is available

3.3.4 ConnectInterceptor

public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (RealInterceptorChain) chain; Request request = realChain.request(); Transmitter transmitter = realChain.transmitter(); boolean doExtensiveHealthChecks = ! request.method().equals("GET"); Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks); return realChain.proceed(request, transmitter, exchange); }Copy the code

NewExchange method ** socket connection, TLS handshake

Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
  synchronized (connectionPool) {
    if (noMoreExchanges) {
      throw new IllegalStateException("released");
    }
    if (exchange != null) {
      throw new IllegalStateException("cannot make a new request because the previous response "
          + "is still open: please call response.close()");
    }
  }

  ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
  Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);

  synchronized (connectionPool) {
    this.exchange = result;
    this.exchangeRequestDone = false;
    this.exchangeResponseDone = false;
    return result;
  }
}
Copy the code

Continue processing in the ** exchangeFinder.find method **

public ExchangeCodec find( OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) { int connectTimeout = chain.connectTimeoutMillis(); int readTimeout = chain.readTimeoutMillis(); int writeTimeout = chain.writeTimeoutMillis(); int pingIntervalMillis = client.pingIntervalMillis(); boolean connectionRetryEnabled = client.retryOnConnectionFailure(); try { RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks); return resultConnection.newCodec(client, chain); } catch (RouteException e) { trackFailure(); throw e; } catch (IOException e) { trackFailure(); throw new RouteException(e); }}Copy the code

Realconnection. newCodec returns the codec of socket input and output information. Internally, it is determined by whether the H2 or H2_prior_knowledge protocol is used; Http2ExchangeCodec or Http1ExchangeCodec

** findHealthyConnection method continues — ** findConnection method continues

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException { boolean foundPooledConnection = false; RealConnection result = null; Route selectedRoute = null; RealConnection releasedConnection; Socket toClose; Synchronized (connectionPool) {...... Find available RealConnection objects that have already been connected; If not, create a new one, Result. Connect (connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, Call, eventListener); . return result; }Copy the code

First get the RealConnection object, first from the pool, and then create a new one; Realconnection.connect is then called for processing

public void connect(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled, Call call, EventListener eventListener) { if (protocol ! = null) throw new IllegalStateException("already connected"); RouteException routeException = null; List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs(); ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs); If (route.address().sslSocketFactory() == null) {...... Omit while (true) {try {if (route.requirestunnel ()) {connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener); break; } } else { connectSocket(connectTimeout, readTimeout, call, eventListener); } establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener); eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol); break; } catch (IOException e) {...... Omit}}...... Omit}Copy the code

** The connectTunnel method ** processes HTTPS requests using an HTTP proxy and uses the connectSocket method internally

private void connectSocket(int connectTimeout, int readTimeout, Call call, EventListener eventListener) throws IOException { Proxy proxy = route.proxy(); Address address = route.address(); rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP ? address.socketFactory().createSocket() : new Socket(proxy); eventListener.connectStart(call, route.socketAddress(), proxy); rawSocket.setSoTimeout(readTimeout); try { Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); } catch (ConnectException e) { ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress()); ce.initCause(e); throw ce; } try { source = Okio.buffer(Okio.source(rawSocket)); sink = Okio.buffer(Okio.sink(rawSocket)); } catch (NullPointerException npe) { if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) { throw new IOException(npe); }}}Copy the code

Get ().connectSocket(rawSocket, route.socketAddress(), connectTimeout); If successful, the input and output streams are retrieved into the member variables

** establishProtocol method ** It is possible to establish a TLS connection after a socket connection is successful; In addition, the protocol is parsed to determine the HTTP packet parsing mode. ConnectTls TLS handshake

private void establishProtocol(ConnectionSpecSelector connectionSpecSelector, int pingIntervalMillis, Call call, EventListener eventListener) throws IOException { if (route.address().sslSocketFactory() == null) { if (route.address().protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)) { socket = rawSocket; protocol = Protocol.H2_PRIOR_KNOWLEDGE; startHttp2(pingIntervalMillis); return; } socket = rawSocket; protocol = Protocol.HTTP_1_1; return; } eventListener.secureConnectStart(call); connectTls(connectionSpecSelector); eventListener.secureConnectEnd(call, handshake); if (protocol == Protocol.HTTP_2) { startHttp2(pingIntervalMillis); }}Copy the code

Sslsocket. startHandshake is used to obtain the handshake information SSLSession

    sslSocket.startHandshake();
    SSLSession sslSocketSession = sslSocket.getSession();
Copy the code

3.3.5 CallServerInterceptor

Forget about custom processors; And custom processor values are valid for HTTP;

public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (RealInterceptorChain) chain; Exchange exchange = realChain.exchange(); Request request = realChain.request(); long sentRequestMillis = System.currentTimeMillis(); exchange.writeRequestHeaders(request); boolean responseHeadersStarted = false; Response.Builder responseBuilder = null; if (HttpMethod.permitsRequestBody(request.method()) && request.body() ! = null) { if ("100-continue".equalsIgnoreCase(request.header("Expect"))) { exchange.flushRequest(); responseHeadersStarted = true; exchange.responseHeadersStart(); responseBuilder = exchange.readResponseHeaders(true); } if (responseBuilder == null) { if (request.body().isDuplex()) { exchange.flushRequest(); BufferedSink bufferedRequestBody = Okio.buffer( exchange.createRequestBody(request, true)); request.body().writeTo(bufferedRequestBody); } else { BufferedSink bufferedRequestBody = Okio.buffer( exchange.createRequestBody(request, false)); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } } else { exchange.noRequestBody(); if (! exchange.connection().isMultiplexed()) { exchange.noNewExchangesOnConnection(); } } } else { exchange.noRequestBody(); } if (request.body() == null || ! request.body().isDuplex()) { exchange.finishRequest(); } if (! responseHeadersStarted) { exchange.responseHeadersStart(); } if (responseBuilder == null) { responseBuilder = exchange.readResponseHeaders(false); } Response response = responseBuilder .request(request) .handshake(exchange.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); int code = response.code(); if (code == 100) { response = exchange.readResponseHeaders(false) .request(request) .handshake(exchange.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); code = response.code(); } exchange.responseHeadersEnd(response); if (forWebSocket && code == 101) { response = response.newBuilder() .body(Util.EMPTY_RESPONSE) .build(); } else { response = response.newBuilder() .body(exchange.openResponseBody(response)) .build(); } if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) { exchange.noNewExchangesOnConnection(); } if ((code == 204 || code == 205) && response.body().contentLength() > 0) { throw new ProtocolException( "HTTP " + code  + " had non-zero Content-Length: " + response.body().contentLength()); } return response; }Copy the code
  1. Write header information via Exchange
  2. If it is not a HEAD or GET request, it is written to the request body
  3. Returns 100 status code, continues writing, and retrieves the status code
  4. Get the response header information from Exchange
  5. 101 and webSocket, the message body is empty, otherwise the response body is read through Exchange
  6. 204, 205 User-defined exceptions are thrown
  7. Returns the result

Exchange class

  • The writeRequestHeaders method writes the request line and the request header, and writes the separator \r\n from the body
  • The createRequestBody method returns the flow of the request body
  • The readResponseHeaders method reads the header
  • The openResponseBody method gets the ResponseBody object that contains the ResponseBody

3.4 the websocket request

OkHttpClient. NewWebsocket method for entrance to begin;

    RealWebSocket webSocket = new RealWebSocket(request, listener, new Random(), pingInterval);
    webSocket.connect(this);
    return webSocket;
  }
Copy the code

Connect method of RealWebSocket; The HTTP connection requires a special header that tells the server to switch to Websocket. If the server replies with 101, it succeeds. Three things happen in the request success callback: a timed ping heartbeat packet is enabled; The read loop is enabled and the write operation may be enabled

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.enqueue(new Callback() {
    @Override public void onResponse(Call call, Response response) {
      Exchange exchange = Internal.instance.exchange(response);
      Streams streams;
      try {
        checkUpgradeSuccess(response, exchange);
        streams = exchange.newWebSocketStreams();
      } catch (IOException e) {
        if (exchange != null) exchange.webSocketUpgradeFailed();
        failWebSocket(e, response);
        closeQuietly(response);
        return;
      }

      try {
        String name = "OkHttp WebSocket " + request.url().redact();
        initReaderAndWriter(name, streams);
        listener.onOpen(RealWebSocket.this, response);
        loopReader();
      } catch (Exception e) {
        failWebSocket(e, null);
      }
    }

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

Send a message via realWebsocket. send; The WebSocketListener listens to receive messages and enable or disable long connections.

** checkUpgradeSuccess method ** Verify again that the WebSocket connection is successful

The initReaderAndWriter method initializes the webSocket I/O stream and enables heartbeat packets

public void initReaderAndWriter(String name, Streams streams) throws IOException { synchronized (this) { this.streams = streams; this.writer = new WebSocketWriter(streams.client, streams.sink, random); this.executor = new ScheduledThreadPoolExecutor(1, Util.threadFactory(name, false)); if (pingIntervalMillis ! = 0) { executor.scheduleAtFixedRate( new PingRunnable(), pingIntervalMillis, pingIntervalMillis, MILLISECONDS); } if (! messageAndCloseQueue.isEmpty()) { runWriter(); } } reader = new WebSocketReader(streams.client, streams.source, this); }Copy the code

Reader,writer,writer PingIntervalMillis: indicates the heartbeat packet frequency. If the value is 0, no heartbeat packet is available. MessageAndCloseQueue is a message queue, and if it has one, it writes to it

** loopReader method ** opens the read loop; Read messages are divided into two types of control messages, other messages; Read messages are returned via a callback

public void loopReader() throws IOException { while (receivedCloseCode == -1) { reader.processNextFrame(); } } void processNextFrame() throws IOException { readHeader(); if (isControlFrame) { readControlFrame(); } else { readMessageFrame(); }}Copy the code

** send method ** sends a message; If the long connection is normal and the message sent by the long connection is larger than 16MB, the long connection is closed and the message cannot be sent. The message is put into the messageAndCloseQueue queue for writing

public boolean send(String text) {
  if (text == null) throw new NullPointerException("text == null");
  return send(ByteString.encodeUtf8(text), OPCODE_TEXT);
}

public boolean send(ByteString bytes) {
  if (bytes == null) throw new NullPointerException("bytes == null");
  return send(bytes, OPCODE_BINARY);
}

private synchronized boolean send(ByteString data, int formatOpcode) {
  if (failed || enqueuedClose) return false;

  if (queueSize + data.size() > MAX_QUEUE_SIZE) {
    close(CLOSE_CLIENT_GOING_AWAY, null);
    return false;
  }

  queueSize += data.size();
  messageAndCloseQueue.add(new Message(formatOpcode, data));
  runWriter();
  return true;
}
Copy the code

So much for the context

4, endnotes

To be honest, there’s a lot involved; But if you want to have a deep understanding of the Okhttp framework, you know the basics and what HTTP requests and Websocket requests really do. So I can see from a framework point of view, how it’s encapsulated, what the benefits are, and what I can learn from it in the future; The author is now in the mind, some details are clear, but some are still understanding; What’s more, these understandings may be easy to say individually, but when they are put together, they are difficult to explain. Although this article today is just about finished; But the content has a lot of incomplete places, but I believe that after reading this, I look at okHTTP has a lot of places to understand will be much easier, I will continue to read okHTTP source code, in this article, some parts will do some repairs; There will also be new chapters for separate module readings.

Other common use of the third party series, understanding, can enter

  1. Gson library source code analysis (2.8.5 version)
  2. Okio library source code analysis (version 1.7.2)
  3. Retrofit Source Code Analysis (version 2.9.0)
  4. Picasso 2.71828

Technology changes quickly, but the basic technology, the theoretical knowledge is always the same; The author hopes to share the basic knowledge of common technical points in the rest of his life. If you think the article is well written, please pay attention and like it. If there are mistakes in the article, please give me more advice!