Android okhttp3 利用socket进行read/write的底层实现跟踪
来源:互联网 发布:软件行业的职业规划 编辑:程序博客网 时间:2024/06/05 04:54
在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_errnoEND(recvfrom)
最终recvfrom是用汇编实现的,使用swi进行了系统调用
- Android okhttp3 利用socket进行read/write的底层实现跟踪
- Android okhttp3 进行socket connect&poll的底层实现跟踪
- Android okhttp3 创建Socket的底层实现追踪
- 关于socket的read和write
- socket api -- read write
- Android okhttp3 DNS 底层实现追踪(一)
- Android okhttp3 DNS 底层实现追踪(二)
- read/fread write/fwrite 的实现原理
- read/write函数实现文件的copy
- socket编程之read/write
- socket中read、write、send、recv函数的比较
- socket中read、write、send、recv函数的比较
- socket中read、write、send、recv函数的比较
- socket中read、write、send、recv函数的比较
- socket中read、write、send、recv函数的比较
- socket中read、write、send、recv函数的比较
- socket中read、write、send、recv函数的比较
- socket编程中的read、write与recv、send的区别
- P2P打洞原理
- Config
- CentOS将普通的命令加入环境变量
- android studio 开发中获取SHA1及MD5
- android 6.0 以下的 MediaPlayer 居然不能携带 http headers Referer
- Android okhttp3 利用socket进行read/write的底层实现跟踪
- HTTP之分块传输
- 什么是PING值,PING值的计算方法
- vue.js的安装部署
- iOS-仿赤兔、新浪微博动态列表(带评论、点赞、转发和分享,自动计算行高功能)
- shell变量的作用域
- iOS-运行程序屏幕上下有黑边
- 初学Latex (一)
- asp.net 使用ajax请求地址接口数据 ajax返回类型是json js get post请求数据