首先查看 Socket.getOutputStream 方法可以知道此 outputStream 是由 impl 获取的
public OutputStream getOutputStream() throws IOException {
...
OutputStream os = null;
try {
os = AccessController.doPrivileged(
new PrivilegedExceptionAction<OutputStream>() {
public OutputStream run() throws IOException {
return impl.getOutputStream();
}
});
} catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException();
}
return os;
}
impl 的实现就得看 ServerSocket 是拿到的什么样的 Socket 了,看 Socket.accept() 方法
public Socket accept() throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (!isBound())
throw new SocketException("Socket is not bound yet");
Socket s = new Socket((SocketImpl) null);
implAccept(s);
return s;
}
很明显,逻辑都在 implAccept(s) 里
protected final void implAccept(Socket s) throws IOException {
SocketImpl si = null;
try {
if (s.impl == null)
s.setImpl();
else {
s.impl.reset();
}
si = s.impl;
s.impl = null;
si.address = new InetAddress();
si.fd = new FileDescriptor();
getImpl().accept(si);
...
} catch (IOException e) {
...
} catch (SecurityException e) {
...
}
s.impl = si;
s.postAccept();
}
很明显,逻辑都在 s.setImpl() 里,而这里的代码在 AndroidStudio 又看不到了
void setImpl() {
if (factory != null) {
impl = factory.createSocketImpl();
checkOldImpl();
} else {
// No need to do a checkOldImpl() here, we know it's an up to date
// SocketImpl!
impl = new SocksSocketImpl();
}
if (impl != null)
impl.setSocket(this);
}
那无参数创建的 Socket,factory 默认为 null,这里就一定会把 impl 赋值为 SocksSocketImpl。SocksSocketImpl 是继承于 PlainSocketImpl 的,PlainSocketImpl 又是继承于 AbstractPlainSocketImpl 的,查看 AbstractPlainSocketImpl 的 getOutputStream 方法
protected synchronized OutputStream getOutputStream() throws IOException {
synchronized (fdLock) {
if (isClosedOrPending())
throw new IOException("Socket Closed");
if (shut_wr)
throw new IOException("Socket output is shutdown");
if (socketOutputStream == null)
socketOutputStream = new SocketOutputStream(this);
}
return socketOutputStream;
}
得知 Socket.outputStream 的真实实现是 SocketOutputStream。
它的 write(byte b[], int off, int len)
已经被 SocketOutputStream 重写了
public void write(byte b[], int off, int len) throws IOException {
socketWrite(b, off, len);
}
private void socketWrite(byte b[], int off, int len) throws IOException {
...
FileDescriptor fd = impl.acquireFD();
try {
// Android-added: Check BlockGuard policy in socketWrite.
BlockGuard.getThreadPolicy().onNetwork();
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
if (impl.isClosedOrPending()) {
throw new SocketException("Socket closed");
} else {
throw se;
}
} finally {
impl.releaseFD();
}
}
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
int len) throws IOException;
实际上是交给了 socketWrite0 来处理,这是一个 native 方法。这里的BlockGuard.getThreadPolicy().onNetwork()
不用关注,这是线程策略相关的,不影响流程。
JNIEXPORT void JNICALL
SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
jobject fdObj,
jbyteArray data,
jint off, jint len) {
char *bufP;
char BUF[MAX_BUFFER_LEN];
int buflen;
int fd;
if (IS_NULL(fdObj)) {
JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
return;
} else {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
/* Bug 4086704 - If the Socket associated with this file descriptor
* was closed (sysCloseFD), the the file descriptor is set to -1.
*/
if (fd == -1) {
JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
return;
}
}
if (len <= MAX_BUFFER_LEN) {
bufP = BUF;
buflen = MAX_BUFFER_LEN;
} else {
buflen = min(MAX_HEAP_BUFFER_LEN, len);
bufP = (char *)malloc((size_t)buflen);
/* if heap exhausted resort to stack buffer */
if (bufP == NULL) {
bufP = BUF;
buflen = MAX_BUFFER_LEN;
}
}
while(len > 0) {
int loff = 0;
int chunkLen = min(buflen, len);
int llen = chunkLen;
(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
while(llen > 0) {
int n = NET_Send(fd, bufP + loff, llen, 0);
if (n > 0) {
llen -= n;
loff += n;
continue;
}
if (n == JVM_IO_INTR) {
JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
} else {
if (errno == ECONNRESET) {
JNU_ThrowByName(env, "sun/net/ConnectionResetException",
"Connection reset");
} else {
NET_ThrowByNameWithLastError(env, "java/net/SocketException",
"Write failed");
}
}
if (bufP != BUF) {
free(bufP);
}
return;
}
len -= chunkLen;
off += chunkLen;
}
if (bufP != BUF) {
free(bufP);
}
}
首先第一个判断 fdObj 一般来说是不会为 null 的,查看 AbstractPlainSocketImpl 实现可佐证这一点。
第二个判断是当 fd = -1 的时候会抛一个错,且有代码注释说明当套接字被关闭时,fd 会被设置为 -1。
直接写一个单元测试,报出来的错误是
Server : processSocket error : java.net.SocketException: Broken pipe
发现报出来的错误信息是 Broken pipe,在 socketWrite0 的实现里根本没有。
进一步在 NET_Send 中寻找,这里是到 JVM 层了,底层调用的 send
__BIONIC_FORTIFY_INLINE
ssize_t send(int socket, const void* const buf __pass_object_size0, size_t len, int flags)
__overloadable
__clang_error_if(__bos_unevaluated_lt(__bos0(buf), len),
"'send' called with size bigger than buffer") {
return sendto(socket, buf, len, flags, NULL, 0);
}
这个 send 是 bionic 库的函数,这个库中提供了网络套接字的功能,下载下来这个库,可以在库中找到这个字符串。在 bionic/libc/bionic/strerror.cpp 中可以看到 [EPIPE] = "Broken pipe"
,对应的错误码被定义为 32。
#define EPIPE 32 /* Broken pipe */
目前不知道这个字符串是怎么传到 java 层的,可以确定的是调用底层网络库的 send 会返回错误码,可依据此来判断发生了什么错误。