Connections
Although you provide only the URL, OkHttp plans its connection to your webserver using three types: URL, Address, and Route.
尽管你只提供了URL,OkHttp使用三中方式(URL,地址和路由)来管理到服务器的连接
URLs
URLs (like https://github.com/square/okhttp
) are fundamental to HTTP and the Internet. In addition to being a universal, decentralized naming scheme for everything on the web, they also specify how to access web resources.
URLs(如https://github.com/square/okhttp
)是HTTP和Internet的基础。除了作为web上所有内容通分散命名方案之外,它还具体说明如何访问web资源
URLs are abstract:
URLs是抽象的
-
They specify that the call may be plaintext (
http
) or encrypted (https
), but not which cryptographic algorithms should be used. Nor do they specify how to verify the peer's certificates (the HostnameVerifier) or which certificates can be trusted (the SSLSocketFactory).它们指定调用可以是明文(http)或加密(https),但不指定应该使用哪种加密算法。它们也没有指定如何验证同行的证书(HostnameVerifier)或哪些证书可以信任(SSLSocketFactory)。
-
They don't specify whether a specific proxy server should be used or how to authenticate with that proxy server.
它们不指定是否应该使用特定的代理服务器,也不指定如何使用该代理服务器进行身份验证。
They're also concrete: each URL identifies a specific path (like /square/okhttp
) and query (like ?q=sharks&lang=en
). Each webserver hosts many URLs.
它们也是具体的:每个URL标识一个特定的路径(比如/square/okhttp)和查询(比如?q=sharks&lang=en)。每个web服务器承载许多url。
Addresses
Addresses specify a webserver (like github.com
) and all of the staticconfiguration necessary to connect to that server: the port number, HTTPS settings, and preferred network protocols (like HTTP/2 or SPDY).
Addresses指定一个web服务器(如github.com)和连接到该服务器所需的所有静态配置:端口号、HTTPS设置和首选网络协议(如HTTP/2或SPDY)。
URLs that share the same address may also share the same underlying TCP socket connection. Sharing a connection has substantial performance benefits: lower latency, higher throughput (due to TCP slow start) and conserved battery. OkHttp uses a ConnectionPool that automatically reuses HTTP/1.x connections and multiplexes HTTP/2 and SPDY connections.
共享相同地址的url也可以共享相同的底层TCP套接字连接。共享连接具有显著的性能优势:更低的延迟、更高的吞吐量(由于TCP启动缓慢)和节约的电池。OkHttp使用自动重用HTTP/1.x的ConnectionPool连接和多路传输HTTP/2和SPDY连接。
In OkHttp some fields of the address come from the URL (scheme, hostname, port) and the rest come from the OkHttpClient.
在OkHttp中,地址的一些字段来自URL(模式、主机名、端口),其余来自OkHttpClient。
Routes
Routes supply the dynamic information necessary to actually connect to a webserver. This is the specific IP address to attempt (as discovered by a DNS query), the exact proxy server to use (if a ProxySelector is in use), and which version of TLS to negotiate (for HTTPS connections).
路由提供实际连接到web服务器所需的动态信息。这是要尝试的特定IP地址(由DNS查询发现)、要使用的确切代理服务器(如果使用了代理选择器)和要协商的TLS版本(对于HTTPS连接)。
There may be many routes for a single address. For example, a webserver that is hosted in multiple datacenters may yield multiple IP addresses in its DNS response.
一个地址可能有多条路由。例如,驻留在多个数据中心的web服务器可能在其DNS响应中产生多个IP地址。
Connections
When you request a URL with OkHttp, here's what it does:
当您使用OkHttp请求URL时,它是这样做的:
-
It uses the URL and configured OkHttpClient to create an address. This address specifies how we'll connect to the webserver.
它使用URL和配置的OkHttpClient来创建一个地址。这个地址指定了我们将如何连接到web服务器。
-
It attempts to retrieve a connection with that address from the connection pool.
它尝试从连接池中检索具有该地址的连接。
-
If it doesn't find a connection in the pool, it selects a route to attempt. This usually means making a DNS request to get the server's IP addresses. It then selects a TLS version and proxy server if necessary.
如果在池中没有找到连接,则选择要尝试的路由。这通常意味着发出DNS请求以获取服务器的IP地址。然后在必要时选择TLS版本和代理服务器。
-
If it's a new route, it connects by building either a direct socket connection, a TLS tunnel (for HTTPS over an HTTP proxy), or a direct TLS connection. It does TLS handshakes as necessary.
如果是新路由,则通过构建直接套接字连接、TLS隧道(用于HTTP代理上的HTTPS)或直接TLS连接进行连接。它会在必要的时候握手。
-
It sends the HTTP request and reads the response.
它发送HTTP请求并读取响应。
If there's a problem with the connection, OkHttp will select another route and try again. This allows OkHttp to recover when a subset of a server's addresses are unreachable. It's also useful when a pooled connection is stale or if the attempted TLS version is unsupported.
如果连接有问题,OkHttp将选择另一条路由并重试。这允许OkHttp在无法访问服务器地址的子集时进行恢复。当池连接过时或不支持尝试的TLS版本时,它也有用。
Once the response has been received, the connection will be returned to the pool so it can be reused for a future request. Connections are evicted from the pool after a period of inactivity.
一旦接收到响应,连接将返回到池中,以便将来的请求可以重用它。在一段时间不活动之后,将从池中删除连接。
Recipes
We've written some recipes that demonstrate how to solve common problems with OkHttp. Read through them to learn about how everything works together. Cut-and-paste these examples freely; that's what they're for.
我们已经编写了一些例子,演示了如何使用OkHttp解决常见问题。通读它们,了解所有东西是如何一起工作的。自由剪切和粘贴这些示例;这就是它们的用途。
Synchronous Get
同步Get
Download a file, print its headers, and print its response body as a string.
下载文件,打印其头部,并将其响应体打印为字符串。
The string()
method on response body is convenient and efficient for small documents. But if the response body is large (greater than 1 MiB), avoid string()
because it will load the entire document into memory. In that case, prefer to process the body as a stream.
response body上的string()方法对于小型文档来说既方便又高效。但是如果响应体很大(大于1 MiB),则避免string(),因为它将把整个文档加载到内存中。在这种情况下,更喜欢将主体作为流进行处理。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
}
}
Asynchronous Get
异步Get
Download a file on a worker thread, and get called back when the response is readable. The callback is made after the response headers are ready. Reading the response body may still block. OkHttp doesn't currently offer asynchronous APIs to receive a response body in parts.
在工作线程上下载文件,并在响应可读时被回调。回调是在响应头准备好之后进行的。读取响应体仍然可能阻塞。OkHttp目前不提供异步api来接收部分响应体。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(responseBody.string());
}
}
});
}
Accessing Headers
Typically HTTP headers work like a Map
: each field has one value or none. But some headers permit multiple values, like Guava's Multimap. For example, it's legal and common for an HTTP response to supply multiple Vary
headers. OkHttp's APIs attempt to make both cases comfortable.
通常,HTTP报头的工作方式类似于Map
When writing request headers, use header(name, value)
to set the only occurrence of name
to value
. If there are existing values, they will be removed before the new value is added. Use addHeader(name, value)
to add a header without removing the headers already present.
在编写请求标头时,使用header(name、value)将唯一出现的名称设置为值。如果存在现有值,将在添加新值之前删除它们。使用addHeader(name,value)添加标题,而不删除已经存在的标题。
When reading response a header, use header(name)
to return the last occurrence of the named value. Usually this is also the only occurrence! If no value is present, header(name)
will return null. To read all of a field's values as a list, use headers(name)
.
读取报头响应时,使用header(name)返回命名值的最后一次出现。通常这也是唯一的发生!如果没有值,header(name)将返回null。要将字段的所有值作为列表读取,请使用headers (name)。
To visit all headers, use the Headers
class which supports access by index.
要访问所有标题,请使用支持按索引访问的Headers类。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println("Server: " + response.header("Server"));
System.out.println("Date: " + response.header("Date"));
System.out.println("Vary: " + response.headers("Vary"));
}
}
Posting a String
Use an HTTP POST to send a request body to a service. This example posts a markdown document to a web service that renders markdown as HTML. Because the entire request body is in memory simultaneously, avoid posting large (greater than 1 MiB) documents using this API.
使用HTTP POST向服务发送请求体。这个示例将一个markdown文档发布到一个web服务,该服务将markdown呈现为HTML。由于整个请求体同时位于内存中,因此避免使用此API发布大型(大于1 MiB)文档。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
}
Post Streaming
Here we POST
a request body as a stream. The content of this request body is being generated as it's being written. This example streams directly into the Okio buffered sink. Your programs may prefer an OutputStream
, which you can get from BufferedSink.outputStream()
.
在这里,我们将请求体作为流发布。这个请求体的内容是在编写时生成的。这个例子直接流进Okio缓冲接收器。您的程序可能更喜欢OutputStream,您可以从BufferedSink.outputStream()获得它。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody requestBody = new RequestBody() {
@Override public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
}
Posting a File
It's easy to use a file as a request body.
将文件用作请求体很容易。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
File file = new File("README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
}
Posting form parameters
Use FormBody.Builder
to build a request body that works like an HTML tag. Names and values will be encoded using an HTML-compatible form URL encoding.
使用FormBody。构建器来构建一个类似于HTML