Android okhttp3 利用socket进行read/write的底层实现跟踪

在okhttp3.internal.io.RealConnection#connectSocket中初始化了socket并进行了connect,此时tcp的三次握手已经搞定,接下来它通过okio库与远程socket建立I/O连接,如下代码所示:

  /** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
private void connectSocket(int connectTimeout, int readTimeout, int writeTimeout,
      ConnectionSpecSelector connectionSpecSelector) throws IOException {
    rawSocket.setSoTimeout(readTimeout);
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      throw new ConnectException("Failed to connect to " + route.socketAddress());
    }
    source = Okio.buffer(Okio.source(rawSocket));
    sink = Okio.buffer(Okio.sink(rawSocket));
。。。
}

Okio库是一个由square公司开发的,它补充了Java.io和java.nio的不足,以便能够更加方便,快速的访问、存储和处理你的数据。而OkHttp的底层也使用该库作为支持。

Okio中有两个关键的接口,Sink和Source,这两个接口都继承了Closeable接口;而Sink可以简单的看做OutputStream,Source可以简单的看做InputStream。而这两个接口都是支持读写超时设置的。它们各自有一个支持缓冲区的子类接口,BufferedSink和BufferedSource,而BufferedSink有一个实现类RealBufferedSink,BufferedSource有一个实现类RealBufferedSource;此外,Sink和Source它门还各自有一个支持gzip压缩的实现类GzipSink和GzipSource;一个具有委托功能的抽象类ForwardingSink和ForwardingSource;还有一个实现类便是InflaterSource和DeflaterSink,这两个类主要用于压缩,为GzipSink和GzipSource服务;整体的结构图如下
这里写图片描述

接下来以read为例,追踪底层实现(write的逻辑是类似的)。

1.okhttp3.internal.io.RealConnection#connectSocket

source = Okio.buffer(Okio.source(rawSocket));

2.okio#source

public static Source source(Socket socket) throws IOException {
    if (socket == null) throw new IllegalArgumentException("socket == null");
    AsyncTimeout timeout = timeout(socket);
    Source source = source(socket.getInputStream(), timeout);
    return timeout.source(source);
}

在这里从socket拿InputStream

3./libcore/luni/src/main/java/java/net/Socket.java

    public InputStream getInputStream() throws IOException {
        checkOpenAndCreate(false);
        if (isInputShutdown()) {
            throw new SocketException("Socket input is shutdown");
        }
        return impl.getInputStream();
    }

4./libcore/luni/src/main/java/java/net/PlainSocketImpl.java

    @Override protected synchronized InputStream getInputStream() throws IOException {
        checkNotClosed();
        return new PlainSocketInputStream(this);
    }

5./libcore/luni/src/main/java/java/net/PlainSocketImpl.java

    private static class PlainSocketInputStream extends InputStream {
        private final PlainSocketImpl socketImpl;

        public PlainSocketInputStream(PlainSocketImpl socketImpl) {
            this.socketImpl = socketImpl;
        }

        @Override public int available() throws IOException {
            return socketImpl.available();
        }

        @Override public void close() throws IOException {
            socketImpl.close();
        }

        @Override public int read() throws IOException {
            return Streams.readSingleByte(this);
        }

        @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException {
            return socketImpl.read(buffer, offset, byteCount);
        }
    }

接下来以read(byte[] buffer, int offset, int byteCount)为例。

6./libcore/luni/src/main/java/java/net/PlainSocketImpl.java

    private int read(byte[] buffer, int offset, int byteCount) throws IOException {
        if (byteCount == 0) {
            return 0;
        }
        Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
        if (shutdownInput) {
            return -1;
        }
        int readCount = IoBridge.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false);
        // Return of zero bytes for a blocking socket means a timeout occurred
        if (readCount == 0) {
            throw new SocketTimeoutException();
        }
        // Return of -1 indicates the peer was closed
        if (readCount == -1) {
            shutdownInput = true;
        }
        return readCount;
    }

IoBridge.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false)再次开始去调jni

7./libcore/luni/src/main/java/libcore/io/IoBridge.java

    public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
        int result;
        try {
            InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
            result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress);
            result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
        } catch (ErrnoException errnoException) {
            result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
        }
        return result;
    }

    private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount) {
        if (isRead && byteCount == 0) {
            return -1;
        }
        if (packet != null) {
            packet.setReceivedLength(byteCount);
            if (!isConnected) {
                packet.setAddress(srcAddress.getAddress());
                packet.setPort(srcAddress.getPort());
            }
        }
        return byteCount;
    }

==>Libcore.os.recvfrom

8.
/libcore/luni/src/main/java/libcore/io/BlockGuardOs.java

    @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
        BlockGuard.getThreadPolicy().onNetwork();
        return os.recvfrom(fd, buffer, flags, srcAddress);
    }

/libcore/luni/src/main/java/libcore/io/ForwardingOs.java

public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { 
    return os.recvfrom(fd, buffer, flags, srcAddress); 
}

9./libcore/luni/src/main/java/libcore/io/Posix.java

    public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
        if (buffer.isDirect()) {
            return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
        } else {
            return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, srcAddress);
        }
    }

private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;

终于看到了jni的声明

10./libcore/luni/src/main/native/libcore_io_Posix.cpp

static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) {
    ScopedBytesRW bytes(env, javaBytes);
    if (bytes.get() == NULL) {
        return -1;
    }
    sockaddr_storage ss;
    socklen_t sl = sizeof(ss);
    memset(&ss, 0, sizeof(ss));
    sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
    socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
    jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength);
    fillInetSocketAddress(env, recvCount, javaInetSocketAddress, ss);
    return recvCount;
}

#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
    return_type _rc = -1; \
    do { \
        { \
            int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
            AsynchronousSocketCloseMonitor _monitor(_fd); \
            _rc = syscall_name(_fd, __VA_ARGS__); \
        } \
        if (_rc == -1) { \
            if (jniGetFDFromFileDescriptor(jni_env, java_fd) == -1) { \
                jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
                break; \
            } else if (errno != EINTR) { \
                /* TODO: with a format string we could show the arguments too, like strace(1). */ \
                throwErrnoException(jni_env, # syscall_name); \
                break; \
            } \
        } \
    } while (_rc == -1); \
    _rc; })

这边是jni的实现

11./bionic/libc/arch-arm/syscalls/recvfrom.S

ENTRY(recvfrom)
    mov     ip, sp
    .save   {r4, r5, r6, r7}
    stmfd   sp!, {r4, r5, r6, r7}
    ldmfd   ip, {r4, r5, r6}
    ldr     r7, =__NR_recvfrom
    swi     #0
    ldmfd   sp!, {r4, r5, r6, r7}
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno
END(recvfrom)

最终recvfrom是用汇编实现的,使用swi进行了系统调用

好的,以下是使用 OkHttp3 发起 HTTP/2.0 POST 请求的 Java 代码: ```java import okhttp3.*; import java.io.IOException; public class Http2PostRequestExample { public static void main(String[] args) throws IOException { OkHttpClient client = new OkHttpClient(); // 构建 HTTP/2.0 的请求 Request request = new Request.Builder() .url("https://example.com/api") .post(RequestBody.create(MediaType.parse("application/json"), "{\"key\":\"value\"}")) .build(); // 发送请求 Response response = client.newCall(request).execute(); // 处理响应 if (response.isSuccessful()) { System.out.println(response.body().string()); } else { System.err.println("Unexpected response code: " + response); } } } ``` 这个例子中,我们使用 OkHttp3 的 `OkHttpClient` 类来创建 HTTP 客户端,然后构建一个 HTTP/2.0 的 POST 请求。在 `post()` 方法中,我们使用 `RequestBody.create()` 方法来创建一个请求体,并指定它的 MIME 类型为 `application/json`。最后,我们使用 `client.newCall(request).execute()` 方法来发送请求,并使用 `response.body().string()` 方法来获取响应内容。 需要注意的是,如果你要使用 HTTP/2.0 协议,你需要确保你的服务器端已经支持 HTTP/2.0。另外,OkHttp3 默认情况下会自动选择最佳的协议版本,如果你想强制使用 HTTP/2.0,可以在创建 `OkHttpClient` 对象时指定 `protocols()` 方法,如下所示: ```java OkHttpClient client = new OkHttpClient.Builder() .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) .build(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello2mao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值