本站首页    管理页面    写新日志    退出


«September 2025»
123456
78910111213
14151617181920
21222324252627
282930


公告

戒除浮躁,读好书,交益友


我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:邢红瑞的blog
日志总数:523
评论数量:1142
留言数量:0
访问次数:9703497
建立时间:2004年12月20日




[jvm]未公开的mustang核心秘密(五):JDK中socket的连接 
原创空间,  软件技术,  电脑与网络

邢红瑞 发表于 2008/3/21 18:45:05

以前对windows下jdk的socket的connect函数感到很不理解,终于看一下。这个函数是在PlainSocketImpl.java定义 private native void socketCreate(boolean isServer) throws IOException;    private native void socketConnect(InetAddress address, int port, int timeout)实现在TwoStacksPlainSocketImpl.c中,不知道sun的那帮同学起了一个这样的名字。socketCreate函数比较简单,就不多说啦。主要是socketConnect,我把自己的注释,写在代码中/* * inetAddress is the address object passed to the socket connect * call. * * Class:     java_net_TwoStacksPlainSocketImpl * Method:    socketConnect * Signature: (Ljava/net/InetAddress;I)V */JNIEXPORT void JNICALLJava_java_net_TwoStacksPlainSocketImpl_socketConnect(JNIEnv *env, jobject this,                                            jobject iaObj, jint port,                                            jint timeout){    jint localport = (*env)->GetIntField(env, this, psi_localportID);     /* family and localport are int fields of iaObj */    int family;    jint fd, fd1=-1;    jint len;    int  ipv6_supported = ipv6_available();     /* fd initially points to the IPv4 socket and fd1 to the IPv6 socket     * If we want to connect to IPv6 then we swap the two sockets/objects     * This way, fd is always the connected socket, and fd1 always gets closed.     */    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);    jobject fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID);     SOCKETADDRESS him;     /* The result of the connection */    int connect_res;     if (!IS_NULL(fdObj)) {       /*得到文件描述符,这个存放在PlainSocketImpl的 private FileDescriptor fd1;   */        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);    }     if (ipv6_supported && !IS_NULL(fd1Obj)) {        fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);    }     if (IS_NULL(iaObj)) {        JNU_ThrowNullPointerException(env, "inet address argument is null.");        return;    }     if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&him, &len, JNI_FALSE) != 0) {      return;    }     family = him.him.sa_family;    if (family == AF_INET6) {        if (!ipv6_supported) {            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",                            "Protocol family not supported");            return;        } else {            if (fd1 == -1) {                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",                                "Destination unreachable");                return;            }            /* close the v4 socket, and set fd to be the v6 socket */            (*env)->SetObjectField(env, this, psi_fdID, fd1Obj);            (*env)->SetObjectField(env, this, psi_fd1ID, NULL);            NET_SocketClose(fd);            fd = fd1; fdObj = fd1Obj;        }    } else {        if (fd1 != -1) {            (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, -1);            NET_SocketClose(fd1);        }        if (fd == -1) {            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",                            "Destination unreachable");            return;        }    }    (*env)->SetObjectField(env, this, psi_fd1ID, NULL);    /*超时小于等于0,超时值零被解释为无限超时  */    if (timeout <= 0) {        connect_res = connect(fd, (struct sockaddr *) &him, SOCKETADDRESS_LEN(&him));        if (connect_res == SOCKET_ERROR) {            connect_res = WSAGetLastError();        }    } else {        int optval;        int optlen = sizeof(optval);         /* make socket non-blocking */        optval = 1;        /* 改成非阻塞模式 如果不使用ioctlsocket sock 默认为阻塞模式 在使用SELECT的时候要改成 非阻塞模式 ,不改成该模式也可以正常运行,select会自动把套节字置为非阻塞模式 */        ioctlsocket( fd, FIONBIO, &optval );         /* initiate the connect */        connect_res = connect(fd, (struct sockaddr *) &him, SOCKETADDRESS_LEN(&him));        if (connect_res == SOCKET_ERROR) {            if (WSAGetLastError() != WSAEWOULDBLOCK) {              /*WSAEWOULDBLOCK==10053不是错误 这是出错了          */                connect_res = WSAGetLastError();            } else {                fd_set wr, ex;                struct timeval t;                 FD_ZERO(&wr);                FD_ZERO(&ex);                FD_SET(fd, &wr);                FD_SET(fd, &ex);                t.tv_sec = timeout / 1000;                t.tv_usec = (timeout % 1000) * 1000;                 /*                 * Wait for timout, connection established or                 * connection failed. 等待连接                 */                connect_res = select(fd+1, 0, &wr, &ex, &t);                 /*                 * Timeout before connection is established/failed so                 * we throw exception and shutdown input/output to prevent                 * socket from being used.                 * The socket should be closed immediately by the caller.                返回socket的个数,成功了肯定不是0                 */                if (connect_res == 0) {                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",                                    "connect timed out");                    shutdown( fd, SD_BOTH );                      /* make socket blocking again - just in case */                    optval = 0;                    ioctlsocket( fd, FIONBIO, &optval );                    return;                }                 /*                 * We must now determine if the connection has been established                 * or if it has failed. The logic here is designed to work around                 * bug on Windows NT whereby using getsockopt to obtain the                 * last error (SO_ERROR) indicates there is no error. The workaround                 * on NT is to allow winsock to be scheduled and this is done by                 * yielding and retrying. As yielding is problematic in heavy                 * load conditions we attempt up to 3 times to get the error reason.      win2k 以后应该不用考虑的           */                if (!FD_ISSET(fd, &ex)) {                    connect_res = 0;                } else {                    int retry;                    for (retry=0; retry<3; retry++) {                        NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,                                       (char*)&connect_res, &optlen);                        if (connect_res) {                            break;                        }                        Sleep(0);                    }                     if (connect_res == 0) {                        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",                                        "Unable to establish connection");                        return;                    }                }            }        }         /* make socket blocking again */        optval = 0;        ioctlsocket(fd, FIONBIO, &optval);    }     if (connect_res) {        if (connect_res == WSAEADDRNOTAVAIL) {            JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",                "connect: Address is invalid on local machine, or port is not valid on remote machine");        } else {            NET_ThrowNew(env, connect_res, "connect");        }        return;    }     (*env)->SetIntField(env, fdObj, IO_fd_fdID, (int)fd);     /* set the remote peer address and port */    (*env)->SetObjectField(env, this, psi_addressID, iaObj);    (*env)->SetIntField(env, this, psi_portID, port);     /*     * we need to initialize the local port field if bind was called     * previously to the connect (by the client) then localport field     * will already be initialized     */    if (localport == 0) {        /* Now that we're a connected socket, let's extract the port number         * that the system chose for us and store it in the Socket object.         */        u_short port;        int len = SOCKETADDRESS_LEN(&him);        if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) {             if (WSAGetLastError() == WSAENOTSOCK) {                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",                        "Socket closed");            } else {                NET_ThrowCurrent(env, "getsockname failed");            }            return;        }        port = ntohs ((u_short)GET_PORT(&him));        (*env)->SetIntField(env, this, psi_localportID, (int) port);    }}


阅读全文(5721) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.047 second(s), page refreshed 144763285 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号