ServerSocket构造函数源码分析

总结:

(1) 创建SocketImpl并设置为ServerSocket属性

    /**
     * Create a server with the specified port, listen backlog, and
     * local IP address to bind to.  The <i>bindAddr</i> argument
     * can be used on a multi-homed host for a ServerSocket that
     * will only accept connect requests to one of its addresses.
     * If <i>bindAddr</i> is null, it will default accepting
     * connections on any/all local addresses.
     * The port must be between 0 and 65535, inclusive.
     * A port number of {@code 0} means that the port number is
     * automatically allocated, typically from an ephemeral port range.
     * This port number can then be retrieved by calling
     * {@link #getLocalPort getLocalPort}.
     *
     * <P>If there is a security manager, this method
     * calls its {@code checkListen} method
     * with the {@code port} argument
     * as its argument to ensure the operation is allowed.
     * This could result in a SecurityException.
     *
     * The {@code backlog} argument is the requested maximum number of
     * pending connections on the socket. Its exact semantics are implementation
     * specific. In particular, an implementation may impose a maximum length
     * or may choose to ignore the parameter altogther. The value provided
     * should be greater than {@code 0}. If it is less than or equal to
     * {@code 0}, then an implementation specific default will be used.
     * <P>
     * @param port  the port number, or {@code 0} to use a port
     *              number that is automatically allocated.
     * @param backlog requested maximum length of the queue of incoming
     *                connections.
     * @param bindAddr the local InetAddress the server will bind to
     *
     * @throws  SecurityException if a security manager exists and
     * its {@code checkListen} method doesn't allow the operation.
     *
     * @throws  IOException if an I/O error occurs when opening the socket.
     * @exception  IllegalArgumentException if the port parameter is outside
     *             the specified range of valid port values, which is between
     *             0 and 65535, inclusive.
     *
     * @see SocketOptions
     * @see SocketImpl
     * @see SecurityManager#checkListen
     * @since   JDK1.1
     */
    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl(); //1
        if (port < 0 || port > 0xFFFF)
            throw new IllegalArgumentException(
                       "Port value out of range: " + port);
        if (backlog < 1)
          backlog = 50;
        try {
            bind(new InetSocketAddress(bindAddr, port), backlog);
        } catch(SecurityException e) {
            close();
            throw e;
        } catch(IOException e) {
            close();
            throw e;
        }
    }
1. setImpl()

创建SocksSocketImpl对象
SocksSocketImpl构造函数
与ServerSocket绑定
在这里插入图片描述
其实是将SocksSocketImpl的Impl也就是DualStackPlainSocketImpl的serverSocke属性设置为该ServerSocket(看了SocksSocketImpl构造函数就明白了这句话)

这里千万不要搞混了:
SocksSocketImpl 作为 ServerSocket的Impl,
DualStackPlainSocketImpl作为SocksSocketImpl对象的Impl属性。
因为DualStackPlainSocketImpl是接近底层的实现,可以说SocksSocketImpl 封装功能,通过底下的DualStackPlainSocketImpl实现,有点上级交给下级,下级交给下下级的感觉,老打工人了。(调侃一下,这样的代码结构还是有实际意义的)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 验证port

在这里插入图片描述

3. 绑定端口,关键一步

将我们的套接字信息(IP + port) 封装为要给InetSocketAddress对象,然后作为bind()的入参
在这里插入图片描述
在这里插入图片描述

还是通用的经过一系列判断之后使用一个try catch封装工作核心
在这里插入图片描述

就两步需要看

getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);

这里得到的是ServerSocket的Impl也就是我们说的SocksSocketImpl

SocksSocketImpl#bind

其实SocksSocketImpl是没有实现这个方法的,只需要继承父类的方法就行了
在这里插入图片描述
自然是交给我们的下下级 DualStackPlainSocketImpl实现的
在这里插入图片描述

其实这里DualStackPlainSocketImpl也没有实现这个方法,直接继承的它的父类的bind方法,更确切的说,bind方法还不够底层,通过AbstractPlainSocketImpl处理一下再交给DualStackPlainSocketImpl#bind0来最终实现底层操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后交给Jdk底层去处理,处理结束后,最后执行
底层代码在附录:

在这里插入图片描述
在这里插入图片描述
表示建立了连接:

SocksSocketImpl#listen

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一样的道理
在这里插入图片描述

附录

bind0源码
/*
 * Class:     java_net_PlainDatagramSocketImpl
 * Method:    bind
 * Signature: (ILjava/net/InetAddress;)V
 */
JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
                                           jint localport, jobject iaObj) {
    /* fdObj is the FileDescriptor field on this */
    jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
    /* fd is an int field on fdObj */
    int fd;
    int len = 0;
    SOCKADDR him;

    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(iaObj)) {
        JNU_ThrowNullPointerException(env, "iaObj is null.");
        return;
    }

    /* bind */
    if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
      return;
    }
    setDefaultScopeID(env, (struct sockaddr *)&him);

    if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0)  {
        if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
            errno == EPERM || errno == EACCES) {
            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
                            "Bind failed");
        } else {
            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
                            "Bind failed");
        }
        return;
    }

    /* initialize the local port */
    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.
         */
        if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) {
            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
                            "Error getting socket name");
            return;
        }

        localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);

        (*env)->SetIntField(env, this, pdsi_localPortID, localport);
    } else {
        (*env)->SetIntField(env, this, pdsi_localPortID, localport);
    }
}
listen0源码
/*
 * Class:     java_net_DualStackPlainSocketImpl
 * Method:    listen0
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0
  (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
    if (listen(fd, backlog) == SOCKET_ERROR) {
        NET_ThrowNew(env, WSAGetLastError(), "listen failed");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值