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(); }
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);
/** 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);
/** * 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); }
public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
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创建跟踪完毕~
更多相关文章
- Android AdbCommandRejectedException和cannot bind to套接字地
- Android分别使用HTTP协议和TCP协议实现上传文件
- Android底层驱动开发 -驱动配置篇
- Android底层开发技术实战详解——内核、移植和驱动
- 蓝牙协议栈调试记录