java hotspot虚拟机 - SocketOutputStream

SocketOutputStream.java

就只有这一个native方法

/**
 * Writes to the socket.
 * @param fd the FileDescriptor
 * @param b the data to be written
 * @param off the start offset in the data
 * @param len the number of bytes that are written
 * @exception IOException If an I/O error has occurred.
 */
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
                                 int len) throws IOException;

SocketOutputStream.c

路径: openjdk.c

#include <windows.h>
#include <winsock2.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>

#include "java_net_SocketOutputStream.h"

#include "net_util.h"
#include "jni_util.h"

/************************************************************************
 * SocketOutputStream
 */
static jfieldID IO_fd_fdID;

/*
 * Class:     java_net_SocketOutputStream
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_init(JNIEnv *env, jclass cls) {
    IO_fd_fdID = NET_GetFileDescriptorID(env);
}

/*
 * Class:     java_net_SocketOutputStream
 * Method:    socketWrite
 * Signature: (Ljava/io/FileDescriptor;[BII)V
 */
JNIEXPORT void JNICALL
Java_java_net_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, JNU_JAVANETPKG "SocketException", "Socket closed");
        return;
    } else {
        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
    }
    if (IS_NULL(data)) {
        JNU_ThrowNullPointerException(env, "data argument");
        return;
    }

    /*
     * Use stack allocate buffer if possible. For large sizes we allocate
     * an intermediate buffer from the heap (up to a maximum). If heap is
     * unavailable just use our stack buffer.
     */
    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 (bufP == NULL) {
            bufP = BUF;
            buflen = MAX_BUFFER_LEN;
        }
    }

    while(len > 0) {
        int loff = 0;
        int chunkLen = min(buflen, len);
        int llen = chunkLen;
        int retry = 0;

        (*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);

        while(llen > 0) {
            int n = send(fd, bufP + loff, llen, 0);
            if (n > 0) {
                llen -= n;
                loff += n;
                continue;
            }

            /*
             * Due to a bug in Windows Sockets (observed on NT and Windows
             * 2000) it may be necessary to retry the send. The issue is that
             * on blocking sockets send/WSASend is supposed to block if there
             * is insufficient buffer space available. If there are a large
             * number of threads blocked on write due to congestion then it's
             * possile to hit the NT/2000 bug whereby send returns WSAENOBUFS.
             * The workaround we use is to retry the send. If we have a
             * large buffer to send (>2k) then we retry with a maximum of
             * 2k buffer. If we hit the issue with <=2k buffer then we backoff
             * for 1 second and retry again. We repeat this up to a reasonable
             * limit before bailing out and throwing an exception. In load
             * conditions we've observed that the send will succeed after 2-3
             * attempts but this depends on network buffers associated with
             * other sockets draining.
             */
            if (WSAGetLastError() == WSAENOBUFS) {
                if (llen > MAX_BUFFER_LEN) {
                    buflen = MAX_BUFFER_LEN;
                    chunkLen = MAX_BUFFER_LEN;
                    llen = MAX_BUFFER_LEN;
                    continue;
                }
                if (retry >= 30) {
                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
                        "No buffer space available - exhausted attempts to queue buffer");
                    if (bufP != BUF) {
                        free(bufP);
                    }
                    return;
                }
                Sleep(1000);
                retry++;
                continue;
            }

            /*
             * Send failed - can be caused by close or write error.
             */
            if (WSAGetLastError() == WSAENOTSOCK) {
                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
            } else {
                NET_ThrowCurrent(env, "socket write error");
            }
            if (bufP != BUF) {
                free(bufP);
            }
            return;
        }
        len -= chunkLen;
        off += chunkLen;
    }

    if (bufP != BUF) {
        free(bufP);
    }
}