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(&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_errnoEND(recvfrom)
最终recvfrom是用汇编实现的,使用swi进行了系统调用
更多相关文章
- tinyalsa 与 audioroute
- python搭建服务器实现两个Android客户端间收发消息
- Android(安卓)4.0调用http接口php网站的api
- Android(安卓)Audio System 之二:AudioFlinger
- Android使用HttpURLConnection访问接口
- 【Android】用retrofit使用单独的URL和处理form-data格式的POST
- C++的Android接口---配置NDK
- 在android状态栏上添加多个图标
- 【开源框架】一个基于回调机制的多线程异步Http连接框架