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

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_errno
END(socket)

至此,socket创建跟踪完毕~











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hello2mao

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

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

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

打赏作者

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

抵扣说明:

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

余额充值