All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.nio.channels.enhance.EnhanceAsynchronousSocketChannel Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
package java.nio.channels.enhance;

import java.io.IOException;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.CompletionHandler;
import java.nio.channels.ConnectionPendingException;
import java.nio.channels.ReadPendingException;
import java.nio.channels.SelectionKey;
import java.nio.channels.ShutdownChannelGroupException;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritePendingException;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

final class EnhanceAsynchronousSocketChannel extends AsynchronousSocketChannel {

    private final SocketChannel channel;
    private final EnhanceAsynchronousChannelGroup group;
    private final EnhanceAsynchronousChannelGroup.Worker readWorker;
    private final EnhanceAsynchronousChannelGroup.Worker writeWorker;
    private final EnhanceAsynchronousChannelGroup.Worker connectWorker;
    private ByteBuffer readBuffer;
    private ByteBufferArray scatteringReadBuffer;
    private ByteBuffer writeBuffer;
    private ByteBufferArray gatheringWriteBuffer;
    private CompletionHandler readCompletionHandler;
    private CompletionHandler writeCompletionHandler;
    private CompletionHandler connectCompletionHandler;
    private FutureCompletionHandler connectFuture;
    private FutureCompletionHandler readFuture;
    private FutureCompletionHandler writeFuture;
    private Object readAttachment;
    private Object writeAttachment;
    private Object connectAttachment;
    private SelectionKey readSelectionKey;
    private SelectionKey readFutureSelectionKey;
    private SelectionKey writeSelectionKey;
    private SelectionKey connectSelectionKey;
    private boolean writePending;
    private boolean readPending;
    private boolean connectionPending;
    private SocketAddress remote;

    public EnhanceAsynchronousSocketChannel(EnhanceAsynchronousChannelGroup group, SocketChannel channel) throws IOException {
        super(group.provider());
        this.group = group;
        this.channel = channel;
        readWorker = group.getReadWorker();
        writeWorker = group.getWriteWorker();
        connectWorker = group.getConnectWorker();
        channel.configureBlocking(false);
    }

    @Override
    public void close() throws IOException {
        IOException exception = null;
        try {
            if (channel != null && channel.isOpen()) {
                channel.close();
            }
        } catch (IOException e) {
            exception = e;
        }
        if (readSelectionKey != null) {
            readSelectionKey.cancel();
            readSelectionKey = null;
        }
        if (readFutureSelectionKey != null) {
            readFutureSelectionKey.cancel();
            readFutureSelectionKey = null;
        }
        if (writeSelectionKey != null) {
            writeSelectionKey.cancel();
            writeSelectionKey = null;
        }
        if (connectSelectionKey != null) {
            connectSelectionKey.cancel();
            connectSelectionKey = null;
        }
        if (exception != null) {
            throw exception;
        }
    }

    @Override
    public AsynchronousSocketChannel bind(SocketAddress local) throws IOException {
        channel.bind(local);
        return this;
    }

    @Override
    public  AsynchronousSocketChannel setOption(SocketOption name, T value) throws IOException {
        channel.setOption(name, value);
        return this;
    }

    @Override
    public  T getOption(SocketOption name) throws IOException {
        return channel.getOption(name);
    }

    @Override
    public Set> supportedOptions() {
        return channel.supportedOptions();
    }

    @Override
    public AsynchronousSocketChannel shutdownInput() throws IOException {
        channel.shutdownInput();
        return this;
    }

    @Override
    public AsynchronousSocketChannel shutdownOutput() throws IOException {
        channel.shutdownOutput();
        return this;
    }

    @Override
    public SocketAddress getRemoteAddress() throws IOException {
        return channel.getRemoteAddress();
    }

    @Override
    public  void connect(SocketAddress remote, A attachment, CompletionHandler handler) {
        if (group.isTerminated()) {
            throw new ShutdownChannelGroupException();
        }
        if (channel.isConnected()) {
            throw new AlreadyConnectedException();
        }
        if (connectionPending) {
            throw new ConnectionPendingException();
        }
        connectionPending = true;
        this.connectAttachment = attachment;
        this.connectCompletionHandler = (CompletionHandler) handler;
        this.remote = remote;
        doConnect();
    }

    @Override
    public Future connect(SocketAddress remote) {
        FutureCompletionHandler connectFuture = new FutureCompletionHandler<>();
        connect(remote, null, connectFuture);
        this.connectFuture = connectFuture;
        return connectFuture;
    }

    @Override
    public  void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) {
        read0(dst, null, timeout, unit, attachment, handler);
    }

    private  void read0(ByteBuffer readBuffer, ByteBufferArray scattering, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) {
        if (readPending) {
            throw new ReadPendingException();
        }
        readPending = true;
        this.readBuffer = readBuffer;
        this.scatteringReadBuffer = scattering;
        this.readAttachment = attachment;
        if (timeout > 0) {
            readFuture = new FutureCompletionHandler<>((CompletionHandler) handler, readAttachment);
            readCompletionHandler = (CompletionHandler) readFuture;
            group.getScheduledExecutor().schedule(readFuture, timeout, unit);
        } else {
            this.readCompletionHandler = (CompletionHandler) handler;
        }
        doRead(readFuture != null);
    }

    @Override
    public Future read(ByteBuffer readBuffer) {
        FutureCompletionHandler readFuture = new FutureCompletionHandler<>();
        this.readFuture = readFuture;
        read(readBuffer, 0, TimeUnit.MILLISECONDS, null, readFuture);
        return readFuture;
    }

    @Override
    public  void read(ByteBuffer[] dsts, int offset, int length, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) {
        read0(null, new ByteBufferArray(dsts, offset, length), timeout, unit, attachment, handler);
    }

    @Override
    public  void write(ByteBuffer src, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) {
        write0(src, null, timeout, unit, attachment, handler);
    }

    private  void write0(ByteBuffer writeBuffer, ByteBufferArray gathering, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) {
        if (writePending) {
            throw new WritePendingException();
        }

        writePending = true;
        this.writeBuffer = writeBuffer;
        this.gatheringWriteBuffer = gathering;
        this.writeAttachment = attachment;
        if (timeout > 0) {
            writeFuture = new FutureCompletionHandler<>((CompletionHandler) handler, writeAttachment);
            writeCompletionHandler = (CompletionHandler) writeFuture;
            group.getScheduledExecutor().schedule(writeFuture, timeout, unit);
        } else {
            this.writeCompletionHandler = (CompletionHandler) handler;
        }
        doWrite();
    }

    @Override
    public Future write(ByteBuffer src) {
        FutureCompletionHandler writeFuture = new FutureCompletionHandler<>();
        this.writeFuture = writeFuture;
        write0(src, null, 0, TimeUnit.MILLISECONDS, null, writeFuture);
        return writeFuture;
    }

    @Override
    public  void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) {
        write0(null, new ByteBufferArray(srcs, offset, length), timeout, unit, attachment, handler);
    }

    @Override
    public SocketAddress getLocalAddress() throws IOException {
        return channel.getLocalAddress();
    }

    public void doConnect() {
        try {
            /*通过Future调用并且触发了cancel*/
            if (connectFuture != null && connectFuture.isDone()) {
                resetConnect();
                return;
            }
            boolean connected = channel.isConnectionPending();
            if (connected || channel.connect(remote)) {
                connected = channel.finishConnect();
            }
            if (connected) {
                CompletionHandler completionHandler = connectCompletionHandler;
                Object attach = connectAttachment;
                resetConnect();
                completionHandler.completed(null, attach);
            } else if (connectSelectionKey == null) {
                connectWorker.addRegister(selector -> {
                    try {
                        connectSelectionKey = channel.register(selector, SelectionKey.OP_CONNECT);
                        connectSelectionKey.attach(EnhanceAsynchronousSocketChannel.this);
                    } catch (ClosedChannelException e) {
                        connectCompletionHandler.failed(e, connectAttachment);
                    }
                });
            } else {
                throw new IOException("unKnow exception");
            }
        } catch (IOException e) {
            connectCompletionHandler.failed(e, connectAttachment);
        }

    }

    private void resetConnect() {
        connectionPending = false;
        connectFuture = null;
        connectAttachment = null;
        connectCompletionHandler = null;
    }

    public void doRead(boolean direct) {
        try {
            //此前通过Future调用,且触发了cancel
            if (readFuture != null && readFuture.isDone()) {
                group.removeOps(readSelectionKey, SelectionKey.OP_READ);
                resetRead();
                return;
            }
            boolean isReadWorkThread = Thread.currentThread() == readWorker.getWorkerThread();
            boolean directRead = direct || (isReadWorkThread && readWorker.invoker++ < EnhanceAsynchronousChannelGroup.MAX_INVOKER);

            long readSize = 0;
            boolean hasRemain = true;
            if (directRead) {
                if (scatteringReadBuffer != null) {
                    readSize = channel.read(scatteringReadBuffer.getBuffers(), scatteringReadBuffer.getOffset(), scatteringReadBuffer.getLength());
                    hasRemain = hasRemaining(scatteringReadBuffer);
                } else {
                    readSize = channel.read(readBuffer);
                    hasRemain = readBuffer.hasRemaining();
                }
                //The read buffer is not full, there may be no readable data
                if (hasRemain && isReadWorkThread) {
                    readWorker.invoker = EnhanceAsynchronousChannelGroup.MAX_INVOKER;
                }
            }

            //注册至异步线程
            if (readFuture != null && readSize == 0) {
                group.removeOps(readSelectionKey, SelectionKey.OP_READ);
                group.registerFuture(selector -> {
                    try {
                        readFutureSelectionKey = channel.register(selector, SelectionKey.OP_READ);
                        readFutureSelectionKey.attach(EnhanceAsynchronousSocketChannel.this);
                    } catch (ClosedChannelException e) {
                        e.printStackTrace();
                        doRead(true);
                    }
                }, SelectionKey.OP_READ);
                return;
            }

            if (readSize != 0 || !hasRemain) {
                CompletionHandler completionHandler = readCompletionHandler;
                Object attach = readAttachment;
                ByteBufferArray scattering = scatteringReadBuffer;
                resetRead();
                if (scattering == null) {
                    completionHandler.completed((int) readSize, attach);
                } else {
                    completionHandler.completed(readSize, attach);
                }

                if (!readPending && readSelectionKey != null) {
                    group.removeOps(readSelectionKey, SelectionKey.OP_READ);
                }
            } else if (readSelectionKey == null) {
                readWorker.addRegister(selector -> {
                    try {
                        readSelectionKey = channel.register(selector, SelectionKey.OP_READ);
                        readSelectionKey.attach(EnhanceAsynchronousSocketChannel.this);
                    } catch (ClosedChannelException e) {
                        readCompletionHandler.failed(e, readAttachment);
                    }
                });
            } else {
                group.interestOps(readWorker, readSelectionKey, SelectionKey.OP_READ);
            }

        } catch (Throwable e) {
            if (readCompletionHandler == null) {
                e.printStackTrace();
                try {
                    close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            } else {
                readCompletionHandler.failed(e, readAttachment);
            }
        }
    }

    private void resetRead() {
        readPending = false;
        readFuture = null;
        readCompletionHandler = null;
        readAttachment = null;
        readBuffer = null;
        scatteringReadBuffer = null;
    }

    public void doWrite() {
        try {
            //此前通过Future调用,且触发了cancel
            if (writeFuture != null && writeFuture.isDone()) {
                resetWrite();
                return;
            }
            boolean directWrite;
            boolean isWriteWorkThread = Thread.currentThread() == writeWorker.getWorkerThread();
            if (isWriteWorkThread && writeFuture != null) {
                directWrite = writeWorker.invoker++ < EnhanceAsynchronousChannelGroup.MAX_INVOKER;
            } else {
                directWrite = true;
            }
            long writeSize = 0;
            boolean hasRemain = true;
            if (directWrite) {
                if (gatheringWriteBuffer != null) {
                    writeSize = channel.write(gatheringWriteBuffer.getBuffers(), gatheringWriteBuffer.getOffset(), gatheringWriteBuffer.getLength());
                    hasRemain = hasRemaining(gatheringWriteBuffer);
                } else {
                    writeSize = channel.write(writeBuffer);
                    hasRemain = writeBuffer.hasRemaining();
                }
                //The write buffer has not been emptied, there may be remaining data cannot be output
                if (isWriteWorkThread && hasRemain) {
                    writeWorker.invoker = EnhanceAsynchronousChannelGroup.MAX_INVOKER;
                }
            }

            //注册至异步线程
            if (writeFuture != null && writeSize == 0) {
                group.removeOps(writeSelectionKey, SelectionKey.OP_WRITE);
                group.registerFuture(selector -> {
                    try {
                        SelectionKey readSelectionKey = channel.register(selector, SelectionKey.OP_WRITE);
                        readSelectionKey.attach(EnhanceAsynchronousSocketChannel.this);
                    } catch (ClosedChannelException e) {
                        e.printStackTrace();
                        doWrite();
                    }
                }, SelectionKey.OP_WRITE);
                return;
            }

            if (writeSize != 0 || !hasRemain) {
                CompletionHandler completionHandler = writeCompletionHandler;
                Object attach = writeAttachment;
                ByteBufferArray scattering = gatheringWriteBuffer;
                resetWrite();
                if (scattering == null) {
                    completionHandler.completed((int) writeSize, attach);
                } else {
                    completionHandler.completed(writeSize, attach);
                }
                if (!writePending && writeSelectionKey != null) {
                    group.removeOps(writeSelectionKey, SelectionKey.OP_WRITE);
                }
            } else if (writeSelectionKey == null) {
                writeWorker.addRegister(selector -> {
                    try {
                        writeSelectionKey = channel.register(selector, SelectionKey.OP_WRITE);
                        writeSelectionKey.attach(EnhanceAsynchronousSocketChannel.this);
                    } catch (ClosedChannelException e) {
                        writeCompletionHandler.failed(e, writeAttachment);
                    }
                });
            } else {
                group.interestOps(writeWorker, writeSelectionKey, SelectionKey.OP_WRITE);
            }
        } catch (Throwable e) {
            if (writeCompletionHandler == null) {
                e.printStackTrace();
                try {
                    close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            } else {
                writeCompletionHandler.failed(e, writeAttachment);
            }
        }
    }

    private boolean hasRemaining(ByteBufferArray scattering) {
        for (int i = 0; i < scattering.getLength(); i++) {
            if (scattering.getBuffers()[scattering.getOffset() + i].hasRemaining()) {
                return true;
            }
        }
        return false;
    }

    private void resetWrite() {
        writePending = false;
        writeFuture = null;
        writeAttachment = null;
        writeCompletionHandler = null;
        writeBuffer = null;
        gatheringWriteBuffer = null;
    }

    @Override
    public boolean isOpen() {
        return channel.isOpen();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy