JEP 353: Reimplement the Legacy Socket API(JDK 13)

1. 简介

JDK中的java.net.Socket 和 java.net.ServerSocket实现非常古老,可以上溯到JDK 1.0,该实现是混合了JAVA和C代码,非常难于维护和调试。另外,该实现使用线程堆栈作为I/O缓冲区,这带来一系列的移植性和可用性问题。

因此JDK 13中实现了新的java.net.SocketImpl的实现类sun.nio.ch.NioSocketImpl,以替代原实现。

2. 分析

2.1 类图

Socket.png

2.2 主要方法

2.2.1 create

创建SocketImpl实例时,判断是否显式开启了USE_PLAINSOCKETIMPL,如是则使用旧的PlainSocketImpl,否则使用NioSocketImpl。

    /**
     * Creates an instance of platform's SocketImpl
     */
    @SuppressWarnings("unchecked")
    static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
        if (USE_PLAINSOCKETIMPL) {
            return (S) new PlainSocketImpl(server);
        } else {
            return (S) new NioSocketImpl(server);
        }
    }

2.2.1 bind

NioSocketImpl

    @Override
    protected void bind(InetAddress host, int port) throws IOException {
        synchronized (stateLock) {
            ensureOpen();
            if (localport != 0)
                throw new SocketException("Already bound");
            NetHooks.beforeTcpBind(fd, host, port);
            Net.bind(fd, host, port);
            // set the address field to the given host address to keep
            // compatibility with PlainSocketImpl. When binding to 0.0.0.0
            // then the actual local address will be ::0 when IPv6 is enabled.
            address = host;
            localport = Net.localAddress(fd).getPort();
        }
    }

AbstractPlainSocketImpl

    protected synchronized void bind(InetAddress address, int lport)
        throws IOException
    {
       synchronized (fdLock) {
            if (!closePending && (socket == null || !socket.isBound())) {
                NetHooks.beforeTcpBind(fd, address, lport);
            }
        }
        socketBind(address, lport);
        if (socket != null)
            socket.setBound();
        if (serverSocket != null)
            serverSocket.setBound();
    }
  • NioSocketImpl使用了新的对象锁stateLock替代旧的fdLock,使用时机更加明确。
  • NIO相关的底层操作均被迁移到sun.nio.ch.Net类中的native方法,而非之前散落在各个类的native方法。
  • NioSocketImpl与NIO共享相同的JDK内部基础类sun.nio.ch.Net,不再需要自己的native代码,提高了代码的可维护性。

2.2.2 listen

NioSocketImpl

    @Override
    protected void listen(int backlog) throws IOException {
        synchronized (stateLock) {
            ensureOpen();
            if (localport == 0)
                throw new SocketException("Not bound");
            Net.listen(fd, backlog < 1 ? 50 : backlog);
        }
    }

AbstractPlainSocketImpl

    protected synchronized void listen(int count) throws IOException {
        socketListen(count);
    }
  • NioSocketImpl使用对象锁stateLock进行并发控制,而AbstractPlainSocketImpl则在native代码中进行并发控制,难以调试
  • NioSocketImpl增加了对于边界条件的判断

2.2.3 accept

NioSocketImpl

/**
     * Accepts a new connection so that the given SocketImpl is connected to
     * the peer. The SocketImpl must be a newly created NioSocketImpl.
     */
    @Override
    protected void accept(SocketImpl si) throws IOException {
        NioSocketImpl nsi = (NioSocketImpl) si;
        if (nsi.state != ST_NEW)
            throw new SocketException("Not a newly created SocketImpl");

        FileDescriptor newfd = new FileDescriptor();
        InetSocketAddress[] isaa = new InetSocketAddress[1];

        // acquire the lock, adjusting the timeout for cases where several
        // threads are accepting connections and there is a timeout set
        ReentrantLock acceptLock = readLock;
        int timeout = this.timeout;
        long remainingNanos = 0;
        if (timeout > 0) {
            remainingNanos = tryLock(acceptLock, timeout, MILLISECONDS);
            if (remainingNanos <= 0) {
                assert !acceptLock.isHeldByCurrentThread();
                throw new SocketTimeoutException("Accept timed out");
            }
        } else {
            acceptLock.lock();
        }

        // accept a connection
        try {
            int n = 0;
            FileDescriptor fd = beginAccept();
            try {
                if (remainingNanos > 0) {
                    // accept with timeout
                    configureNonBlocking(fd);
                    n = timedAccept(fd, newfd, isaa, remainingNanos);
                } else {
                    // accept, no timeout
                    n = Net.accept(fd, newfd, isaa);
                    while (IOStatus.okayToRetry(n) && isOpen()) {
                        park(fd, Net.POLLIN);
                        n = Net.accept(fd, newfd, isaa);
                    }
                }
            } finally {
                endAccept(n > 0);
                assert IOStatus.check(n);
            }
        } finally {
            acceptLock.unlock();
        }

        // get local address and configure accepted socket to blocking mode
        InetSocketAddress localAddress;
        try {
            localAddress = Net.localAddress(newfd);
            IOUtil.configureBlocking(newfd, true);
        } catch (IOException ioe) {
            nd.close(newfd);
            throw ioe;
        }

        // set the fields
        synchronized (nsi.stateLock) {
            nsi.fd = newfd;
            nsi.stream = true;
            nsi.closer = FileDescriptorCloser.create(nsi);
            nsi.localport = localAddress.getPort();
            nsi.address = isaa[0].getAddress();
            nsi.port = isaa[0].getPort();
            nsi.state = ST_CONNECTED;
        }
    }

AbstractPlainSocketImpl

    /**
     * Accepts connections.
     * @param s the connection
     */
    protected void accept(SocketImpl s) throws IOException {
        acquireFD();
        try {
            socketAccept(s);
        } finally {
            releaseFD();
        }
    }
  • NioSocketImpl使用ReentrantLock进行并发控制,而AbstractPlainSocketImpl在native代码中进行并发控制。
  • NioSocketImpl将大量代码逻辑迁移到Java代码中,便于维护和调试。

3. 总结

综上所述,新的NioSocketImpl类将大量native代码迁移到Java中,并使用Java内置锁和显式锁进行并发控制,提高了代码的可维护性。

4. 相关链接

OpenJDK 13 源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值