Android okhttp3 创建Socket的底层实现追踪

来源:互联网 发布:阐释者淘宝 编辑:程序博客网 时间:2024/06/13 01:42

1.概述

OkHttp3的最底层是Socket,而不是URLConnection,它通过Platform的Class.forName()反射获得当前Runtime使用的socket库,调用栈如下

okhttp//实现HTTP协议
==>framwork//JRE,实现JDK中Socket封装
    ==>jvm//JDK的实现,本质对libc标准库的native封装
        ==>bionic//android下的libc标准库
            ==>systemcall//用户态切换入内核
                ==>kernel//实现下协议栈(L4,L3)与网络驱动(一般是L2,L1)

注:需求决定,Android版本4.4.4 okhttp 3.2.0大笑


2.因为底层使用Socket,所以在okhttp3源码全局搜索"new Socket"这个关键词,定位在:

okhttp3.internal.io.RealConnection#connect

rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP            ? address.socketFactory().createSocket()            : new Socket(proxy);

3.在此处打断点,调试



rawSocket为

所以address.socketFactory()返回的是DefaultSocketFactory.java


4./libcore/luni/src/main/java/javax/net/DefaultSocketFactory.java

/** * Default implementation of {@link javax.net.SocketFactory} */final class DefaultSocketFactory extends SocketFactory {    DefaultSocketFactory() {    }    @Override    public Socket createSocket() throws IOException {        return new Socket();    }


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

    public Socket() {        this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl();        this.proxy = null;    }

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

    public PlainSocketImpl() {        this(new FileDescriptor());    }

7.以上步骤还没通过jni调用libc.so,真正创建socket是在connectSocket中

okhttp3.internal.io.RealConnection#connect

connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector);


8.okhttp3.internal.io.RealConnection#connectSocket
  /** 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);


9./libcore/luni/src/main/java/java/net/Socket.java#setSoTimeout

    /**     * Sets this socket's {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds.     * Use 0 for no timeout.     * To take effect, this option must be set before the blocking method was called.     */    public synchronized void setSoTimeout(int timeout) throws SocketException {        checkOpenAndCreate(true);        if (timeout < 0) {            throw new IllegalArgumentException("timeout < 0");        }        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));    }

10./libcore/luni/src/main/java/java/net/Socket.java#checkOpenAndCreate

    /**     * Checks whether the socket is closed, and throws an exception. Otherwise     * creates the underlying SocketImpl.     *     * @throws SocketException     *             if the socket is closed.     */    private void checkOpenAndCreate(boolean create) throws SocketException {        if (isClosed()) {            throw new SocketException("Socket is closed");        }        if (!create) {            if (!isConnected()) {                throw new SocketException("Socket is not connected");                // a connected socket must be created            }            /*             * return directly to fix a possible bug, if !create, should return             * here             */            return;        }        if (isCreated) {            return;        }        synchronized (this) {            if (isCreated) {                return;            }            try {                impl.create(true);            } catch (SocketException e) {                throw e;            } catch (IOException e) {                throw new SocketException(e.toString());            }            isCreated = true;        }    }

最后调用PlainSocketImpl.java的create


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

    protected void create(boolean streaming) throws IOException {        this.streaming = streaming;        this.fd = IoBridge.socket(streaming);    }

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

    public static FileDescriptor socket(boolean stream) throws SocketException {        FileDescriptor fd;        try {            fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);            // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults            // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which            // would be correct for the *unicast* hop limit).            // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to            // have been applied as a result of that discussion. If that bug is ever fixed, we can            // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.            // (IPv4 is already correct.)            if (!stream) {                Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);            }            return fd;        } catch (ErrnoException errnoException) {            throw errnoException.rethrowAsSocketException();        }    }
总算往jni方向去了:Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0)

可见:创建socket时,domain为AF_INET6,类型为SOCK_STREAM(对于http来说)

在c层可以用这两个条件来过滤http的socket创建


12.

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

public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {        return tagSocket(os.socket(domain, type, protocol));    }

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

public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {  return os.socket(domain, type, protocol); }


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

public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;


14./libcore/luni/src/main/native/libcore_io_Posix.cpp     JNI层,此方法为java的代理

static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {    int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;}

NATIVE_METHOD(Posix, socket, "(III)Ljava/io/FileDescriptor;"),


15./bionic/libc/arch-arm/syscalls/socket.S

socket通过汇编实现,汇编代码中通过swi调用中断号实现功能

ENTRY(socket)    mov     ip, r7    ldr     r7, =__NR_socket    swi     #0    mov     r7, ip    cmn     r0, #(MAX_ERRNO + 1)    bxls    lr    neg     r0, r0    b       __set_errnoEND(socket)

至此,socket创建跟踪完毕~











0 0
原创粉丝点击