【Socket / Grizzly】Grizzly TCP Server & Client

Simple TCP Server

import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.filterchain.Filter;
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;
import org.glassfish.grizzly.utils.DelayedExecutor;
import org.glassfish.grizzly.utils.IdleTimeoutFilter;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class SimpleTcpServer {

    /** CPU 线程数 */
    protected static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    /** 积压量 */
    protected volatile int backlog = 4096;
    /** 监听端口 */
    protected volatile int port = Short.MAX_VALUE;
    /** 关闭标识 */
    protected volatile boolean closed = false;
    /** {@link java.net.SocketAddress} endpoint */
    protected volatile SocketAddress endpoint;
    /** {@link org.glassfish.grizzly.nio.transport.TCPNIOTransport} */
    protected volatile TCPNIOTransport transport;

    /**
     * Create a tcp server.
     *
     * @param port          监听端口
     */
    public TcpServer(int port) {
        this.port = port;
        this.endpoint = new InetSocketAddress(port);
    }

    /**
     * Create a tcp server.
     *
     * @param port             监听端口
     * @param backlog          积压量
     */
    public TcpServer(int port, int backlog) {
        this.backlog = backlog;
        this.port = port;
        this.endpoint = new InetSocketAddress(port);
    }

    /**
     * Stop the tcp server.
     */
    public void stop() {
        this.closed = true;
        Optional.ofNullable(this.transport).ifPresent(x -> { x.unbindAll(); x.shutdown(); });
    }

    /**
     * Start the tcp server.
     *
     * @param filters
     * @throws Exception
     */
    public void start(List<Filter> filters) throws Exception {
        this.closed = false;
        TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();
        builder.setServerConnectionBackLog(this.backlog);
        builder.setReuseAddress(true);
        builder.setKeepAlive(true);
        builder.setTcpNoDelay(true);
        builder.setLinger(0);
        this.transport = builder.build();
        FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
        final DelayedExecutor delayedExecutor = IdleTimeoutFilter.createDefaultIdleDelayedExecutor();
        delayedExecutor.start();
        filterChainBuilder.add(new IdleTimeoutFilter(delayedExecutor, 30, TimeUnit.SECONDS));
        filterChainBuilder.add(new TransportFilter());
        Optional.ofNullable(filters).ifPresent(filterChainBuilder::addAll);
        this.transport.setProcessor(filterChainBuilder.build());
        doBind();
    }

    /**
     *
     */
    protected void doBind() {
        if (this.closed) {
            return;
        }
        while (!this.closed) {
            try {
                this.transport.bind(this.endpoint);
                this.transport.start();
                return;
            } catch (Exception ignored) {
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignored) {
            }
        }
    }

}

Simple TCP Client

import org.glassfish.grizzly.CloseListener;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.EmptyCompletionHandler;
import org.glassfish.grizzly.filterchain.Filter;
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;
import org.glassfish.grizzly.utils.DelayedExecutor;
import org.glassfish.grizzly.utils.IdleTimeoutFilter;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class SimpleTcpClient {

    /** 连接标识 */
    protected final AtomicBoolean connecting = new AtomicBoolean(false);
    protected final ExecutorService retrier
            = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1));
    /** 连接地址 */
    protected volatile String addr = "127.0.0.1";
    /** 连接端口 */
    protected volatile int port = Short.MAX_VALUE;
    /** 关闭标识 */
    protected volatile boolean closed = false;

    /** {@link java.net.SocketAddress} endpoint */
    protected volatile SocketAddress endpoint;
    /** {@link org.glassfish.grizzly.nio.transport.TCPNIOTransport} */
    protected volatile TCPNIOTransport transport;
    /** {@link org.glassfish.grizzly.Connection} */
    protected volatile Connection connection;

    /**
     * Create a tcp client.
     *
     * @param addr      IP 地址
     * @param port      端口号
     */
    public TcpClient(String addr, int port) {
        this.addr = addr;
        this.port = port;
        this.endpoint = new InetSocketAddress(addr, port);
    }

    /**
     * Close the tcp client.
     */
    public void stop() {
        this.closed = true;
        Optional.ofNullable(this.connection).ifPresent(Connection::closeSilently);
        Optional.ofNullable(this.transport).ifPresent(x -> { x.unbind(connection); x.shutdown(); });
    }

    /**
     * Connect to tcp server.
     *
     * @param filters
     * @throws Exception
     */
    public void start(List<Filter> filters) throws Exception {
        this.closed = false;
        TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();
        builder.setReuseAddress(true);
        builder.setKeepAlive(true);
        builder.setTcpNoDelay(true);
        builder.setLinger(0);
        this.transport = builder.build();
        FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
        final DelayedExecutor delayedExecutor = IdleTimeoutFilter.createDefaultIdleDelayedExecutor();
        delayedExecutor.start();
        filterChainBuilder.add(new IdleTimeoutFilter(delayedExecutor, 30, TimeUnit.SECONDS));
        filterChainBuilder.add(new TransportFilter());
        Optional.ofNullable(filters).ifPresent(filterChainBuilder::addAll);
        this.transport.setProcessor(filterChainBuilder.build());
        this.transport.start();
        doConnect();
    }

    /**
     *
     */
    protected void doConnect() {
        if (this.closed) {
            return;
        }
        if (!connecting.compareAndSet(false, true)) {
            return;
        }
        retryConnectLoop();
    }

    protected void retryConnectLoop() {
        try {
            this.transport.connect(this.endpoint, new EmptyCompletionHandler<Connection>() {
                @Override
                public void failed(Throwable throwable) {
                    connecting.lazySet(false);
                    retrier.submit(() -> doConnect());
                }
                @Override
                public void completed(Connection result) {
                    connection = result;
                    connection.addCloseListener((CloseListener) (closeable, iCloseType) -> {
                        retrier.submit(() -> doConnect());
                    });
                    connecting.lazySet(false);
                }
            });
        } catch (Exception ignored) {
        }
    }

    /**
     *
     * @param data
     */
    public void sent(Object data) {
        Optional.ofNullable(this.connection)
                .filter(Connection::isOpen)
                .ifPresent(x -> x.write(data));
    }

//    @Override
//    protected void finalize() throws Throwable {
//        try {
//            retrier.shutdown();
//            if (!retrier.awaitTermination(1, TimeUnit.SECONDS)) {
//                retrier.shutdownNow();
//            }
//        } catch (Exception ignored) {
//        }
//    }

}

LineBasedFrameDecoder

import org.glassfish.grizzly.*;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.attributes.AttributeStorage;

import java.util.function.Supplier;
import java.util.logging.Logger;

/**
 * A decoder that splits the received {@link Buffer}s by one or more line delimiters.
 * It recognizes both {@code \n} and {@code \r\n} as line delimiters.
 *
 * <p>For example, if you received the following four fragmented packets:
 * <pre>
 * +-----+-----+-----+
 * | ABC | DEF | GHI |
 * +-----+-----+-----+
 * </pre>
 * A {@link LineBasedFrameDecoder} will decode them into the following three packets:
 * <pre>
 * +-----+-----+-----+
 * | ABC | DEF | GHI |
 * +-----+-----+-----+
 * </pre>
 */
public class LineBasedFrameDecoder extends AbstractTransformer<Buffer, Buffer> {

    private static final Logger logger = Grizzly.logger(LineBasedFrameDecoder.class);

    private final int maxLength;
    private final boolean stripDelimiter;
    private final boolean failFast;

    // 连接级解码状态(存储在AttributeStorage中,每个连接独立)
    private final Attribute<DecoderState> decoderStateAttr;

    private static class DecoderState {
        private boolean discarding;
        private int discardedBytes;
        private int offset;
    }

    /**
     * Creates a new decoder.
     *
     * @param maxLength the maximum length of the decoded frame.
     *                  A {@link TransformationException} will be thrown if the length exceeds this value.
     */
    public LineBasedFrameDecoder(int maxLength) {
        this(maxLength, true, false);
    }

    /**
     * Creates a new decoder.
     *
     * @param maxLength      the maximum length of the decoded frame.
     *                       A {@link TransformationException} will be thrown if the length exceeds this value.
     * @param stripDelimiter whether the decoded frame should strip out the line delimiter or not.
     * @param failFast       if {@code true}, a {@link TransformationException} is thrown as soon as the frame length
     *                       exceeds the {@code maxLength}. If {@code false}, a {@link TransformationException} is thrown
     *                       after the full frame has been read.
     */
    public LineBasedFrameDecoder(int maxLength, boolean stripDelimiter, boolean failFast) {
        if (maxLength <= 0) {
            throw new IllegalArgumentException("maxLength must be a positive integer: " + maxLength);
        }
        this.maxLength = maxLength;
        this.stripDelimiter = stripDelimiter;
        this.failFast = failFast;
        this.decoderStateAttr = this.attributeBuilder.createAttribute(
                this.getNamePrefix() + ".DecoderState"
        );
    }

    @Override
    protected TransformationResult<Buffer, Buffer> transformImpl(AttributeStorage storage, Buffer input) throws TransformationException {
        Buffer decoded = decode(storage, input);
        if (decoded == null) {
            return TransformationResult.createIncompletedResult(input);
        }
        return TransformationResult.createCompletedResult(decoded, input);
    }

    @Override
    public void release(AttributeStorage storage) {
        this.decoderStateAttr.remove(storage);
        super.release(storage);
    }

    private <T> T computeIfAbsent(AttributeStorage storage, Attribute<T> attribute, Supplier<T> supplier) {
        T value = attribute.get(storage);
        if (value == null) {
            value = supplier.get();
            attribute.set(storage, value);
        }
        return value;
    }

    /**
     * Decode the given buffer into a frame.
     *
     * @param storage the connection to decode
     * @param buffer  the buffer to decode
     * @return the decoded frame, or {@code null} if no complete frame was found
     * @throws TransformationException if the frame exceeds the maximum length
     */
    protected Buffer decode(AttributeStorage storage, Buffer buffer) throws TransformationException {
        DecoderState state = computeIfAbsent(storage, this.decoderStateAttr, DecoderState::new);

        final int eol = findEndOfLine(buffer, state);
        if (!state.discarding) {
            if (eol >= 0) {
                final Buffer frame;
                final int length = eol - buffer.position();
                final int delimLength = buffer.get(eol) == (byte) '\r' ? 2 : 1;
                if (length > maxLength) {
                    skipBytes(buffer, length + delimLength);
                    fail(length);
                    return null;
                }
                if (stripDelimiter) {
                    frame = readRetainedSlice(buffer, length);
                    skipBytes(buffer, delimLength);
                } else {
                    frame = readRetainedSlice(buffer, length + delimLength);
                }
                return frame;
            } else {
                final int length = buffer.remaining();
                if (length > maxLength) {
                    state.discardedBytes = length;
                    skipBytes(buffer, length);
                    state.discarding = true;
                    state.offset = 0;
                    if (failFast) {
                        fail("over " + state.discardedBytes);
                    }
                }
                return null;
            }
        } else {
            if (eol >= 0) {
                final int length = state.discardedBytes + eol - buffer.position();
                final int delimLength = buffer.get(eol) == (byte) '\r' ? 2 : 1;
                skipBytes(buffer, eol - buffer.position() + delimLength);
                state.discardedBytes = 0;
                state.discarding = false;
                if (!failFast) {
                    fail(length);
                }
            } else {
                state.discardedBytes += buffer.remaining();
                skipBytes(buffer, buffer.remaining());
                state.offset = 0;
            }
            return null;
        }
    }

    private void fail(int length) {
        fail(String.valueOf(length));
    }

    private void fail(String length) {
        throw new TransformationException(
                "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')');
    }

    /**
     * Find the end of line (either {@code \n} or {@code \r\n}) in the given buffer.
     *
     * @param buffer the buffer to search
     * @param state  the state to search
     * @return the index of the line feed character, or -1 if not found
     */
    private int findEndOfLine(final Buffer buffer, DecoderState state) {
        int totalLength = buffer.remaining();
        int readerIndex = buffer.position();
        int i = indexOf(buffer, readerIndex + state.offset, totalLength - state.offset, (byte) '\n');
        if (i >= 0) {
            state.offset = 0;
            if (i > readerIndex && buffer.get(i - 1) == (byte) '\r') {
                i--;
            }
        } else {
            state.offset = totalLength;
        }
        return i;
    }

    /**
     * Find the index of the given byte in the buffer, starting from the given offset.
     *
     * @param buffer    the buffer to search
     * @param fromIndex the index to start searching from
     * @param length    the number of bytes to search
     * @param value     the byte to search for
     * @return the index of the byte, or -1 if not found
     */
    private static int indexOf(Buffer buffer, int fromIndex, int length, byte value) {
        for (int i = 0; i < length; i++) {
            if (buffer.get(fromIndex + i) == value) {
                return fromIndex + i;
            }
        }
        return -1;
    }

    @Override
    public String getName() {
        return "LineBasedFrameDecoder";
    }

    @Override
    public boolean hasInputRemaining(AttributeStorage storage, Buffer input) {
        return input != null && input.hasRemaining();
    }

    public Buffer readRetainedSlice(Buffer buffer, int length) {
        int readerIndex = buffer.position();
        // 返回 slice 共享内存视图,内存生命周期由父 buffer 管理
        // 调用方不应 dispose,Grizzly 框架会处理
        Buffer output = buffer.slice(readerIndex, readerIndex + length);
        buffer.position(readerIndex + length);
        return output;
    }

    private static Buffer skipBytes(Buffer input, int offset) {
        int readerIndex = input.position();
        input.position(readerIndex + offset);
        return input;
    }
}

DelimiterBasedFrameDecoder

import com.xkind.demo.socket.grizzly.internal.ObjectUtil;
import org.glassfish.grizzly.*;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.attributes.AttributeStorage;

import java.util.function.Supplier;
import java.util.logging.Logger;

/**
 * A decoder that splits the received {@link Buffer}s by one or more delimiters.
 * It is particularly useful for decoding the frames which end with a delimiter
 * such as NUL or newline characters.
 *
 * <h3>Specifying more than one delimiter</h3>
 * <p>
 * {@link DelimiterBasedFrameDecoder} allows you to specify more than one delimiter.
 * If more than one delimiter is found in the buffer, it chooses the delimiter which
 * produces the shortest frame.
 */
public class DelimiterBasedFrameDecoder extends AbstractTransformer<Buffer, Buffer> {

    private static final Logger logger = Grizzly.logger(DelimiterBasedFrameDecoder.class);

    private final Buffer[] delimiters;
    private final int maxFrameLength;
    private final boolean stripDelimiter;
    private final boolean failFast;

    // 连接级解码状态(存储在AttributeStorage中,每个连接独立)
    private final Attribute<DecoderState> decoderStateAttr;

    private static class DecoderState {
        private boolean discardingTooLongFrame;
        private int tooLongFrameLength;
    }

    /**
     * Creates a new instance.
     *
     * @param maxFrameLength the maximum length of the decoded frame.
     *                       A {@link TransformationException} will be thrown if the length exceeds this value.
     * @param delimiter      the delimiter
     */
    public DelimiterBasedFrameDecoder(int maxFrameLength, Buffer delimiter) {
        this(maxFrameLength, true, false, delimiter);
    }

    /**
     * Creates a new instance.
     *
     * @param maxFrameLength the maximum length of the decoded frame.
     *                       A {@link TransformationException} will be thrown if the length exceeds this value.
     * @param stripDelimiter whether the decoded frame should strip out the delimiter or not
     * @param delimiter      the delimiter
     */
    public DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, Buffer delimiter) {
        this(maxFrameLength, stripDelimiter, false, delimiter);
    }

    /**
     * Creates a new instance.
     *
     * @param maxFrameLength the maximum length of the decoded frame.
     *                       A {@link TransformationException} will be thrown if the length exceeds this value.
     * @param stripDelimiter whether the decoded frame should strip out the delimiter or not
     * @param failFast       if {@code true}, a {@link TransformationException} is thrown as soon as the frame length
     *                       exceeds the {@code maxFrameLength}. If {@code false}, a {@link TransformationException} is
     *                       thrown after the full frame has been read.
     * @param delimiter      the delimiter
     */
    public DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, boolean failFast, Buffer delimiter) {
        this(maxFrameLength, stripDelimiter, failFast, new Buffer[]{ObjectUtil.checkNotNull(delimiter, "delimiter")});
    }

    /**
     * Creates a new instance.
     *
     * @param maxFrameLength the maximum length of the decoded frame.
     *                       A {@link TransformationException} will be thrown if the length exceeds this value.
     * @param delimiters     the delimiters
     */
    public DelimiterBasedFrameDecoder(int maxFrameLength, Buffer... delimiters) {
        this(maxFrameLength, true, false, delimiters);
    }

    /**
     * Creates a new instance.
     *
     * @param maxFrameLength the maximum length of the decoded frame.
     *                       A {@link TransformationException} will be thrown if the length exceeds this value.
     * @param stripDelimiter whether the decoded frame should strip out the delimiter or not
     * @param failFast       if {@code true}, a {@link TransformationException} is thrown as soon as the frame length
     *                       exceeds the {@code maxFrameLength}. If {@code false}, a {@link TransformationException} is
     *                       thrown after the full frame has been read.
     * @param delimiters     the delimiters
     */
    public DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, boolean failFast, Buffer... delimiters) {
        validateMaxFrameLength(maxFrameLength);
        ObjectUtil.checkNonEmpty(delimiters, "delimiters");
        for (Buffer d : delimiters) {
            if (d == null || !d.hasRemaining()) {
                throw new IllegalArgumentException("delimiter must not be null or empty");
            }
        }
        this.delimiters = delimiters;
        this.maxFrameLength = maxFrameLength;
        this.stripDelimiter = stripDelimiter;
        this.failFast = failFast;
        this.decoderStateAttr = this.attributeBuilder.createAttribute(
                this.getNamePrefix() + ".DecoderState"
        );
    }

    private static void validateMaxFrameLength(int maxFrameLength) {
        if (maxFrameLength <= 0) {
            throw new IllegalArgumentException("maxFrameLength must be a positive integer: " + maxFrameLength);
        }
    }

    @Override
    protected TransformationResult<Buffer, Buffer> transformImpl(AttributeStorage storage, Buffer input) throws TransformationException {
        Object decoded = decode(storage, input);
        if (decoded == null) {
            return TransformationResult.createIncompletedResult(input);
        }
        return TransformationResult.createCompletedResult((Buffer) decoded, input);
    }

    @Override
    public void release(AttributeStorage storage) {
        this.decoderStateAttr.remove(storage);
        super.release(storage);
    }

    private <T> T computeIfAbsent(AttributeStorage storage, Attribute<T> attribute, Supplier<T> supplier) {
        T value = attribute.get(storage);
        if (value == null) {
            value = supplier.get();
            attribute.set(storage, value);
        }
        return value;
    }

    /**
     * Decode the given buffer into a frame.
     *
     * @param storage the connection to decode
     * @param buffer  the buffer to decode
     * @return the decoded frame, or {@code null} if no complete frame was found
     * @throws TransformationException if the frame exceeds the maximum length
     */
    protected Object decode(AttributeStorage storage, Buffer buffer) throws TransformationException {
        DecoderState state = computeIfAbsent(storage, this.decoderStateAttr, DecoderState::new);

        // Try all delimiters and choose the frame that produces the shortest frame.
        int minFrameLength = Integer.MAX_VALUE;
        Buffer minDelim = null;
        for (Buffer delim : delimiters) {
            int frameLength = indexOf(buffer, delim);
            if (frameLength >= 0 && frameLength < minFrameLength) {
                minFrameLength = frameLength;
                minDelim = delim;
            }
        }

        if (minDelim != null) {
            int minDelimLength = minDelim.remaining();
            if (state.discardingTooLongFrame) {
                // We've discarding the frame so far and found the end of the frame.
                state.discardingTooLongFrame = false;
                skipBytes(buffer, minFrameLength + minDelimLength);
                int tooLongFrameLength = state.tooLongFrameLength;
                state.tooLongFrameLength = 0;
                if (!failFast) {
                    fail(tooLongFrameLength);
                }
                return null;
            }
            if (minFrameLength > maxFrameLength) {
                // Discard read frame.
                skipBytes(buffer, minFrameLength + minDelimLength);
                fail(minFrameLength);
                return null;
            }
            Buffer frame;
            if (stripDelimiter) {
                frame = readRetainedSlice(buffer, minFrameLength);
                skipBytes(buffer, minDelimLength);
            } else {
                frame = readRetainedSlice(buffer, minFrameLength + minDelimLength);
            }
            return frame;
        } else {
            if (!state.discardingTooLongFrame) {
                if (buffer.remaining() > maxFrameLength) {
                    // Discard the content of the buffer until a delimiter is found.
                    state.tooLongFrameLength = buffer.remaining();
                    skipBytes(buffer, buffer.remaining());
                    state.discardingTooLongFrame = true;
                    if (failFast) {
                        fail(state.tooLongFrameLength);
                    }
                }
            } else {
                // Still discarding the buffer since a delimiter is not found.
                state.tooLongFrameLength += buffer.remaining();
                skipBytes(buffer, buffer.remaining());
            }
            return null;
        }
    }

    private void fail(long frameLength) {
        if (frameLength > 0) {
            throw new TransformationException(
                    "frame length exceeds " + maxFrameLength + ": " + frameLength + " - discarded");
        } else {
            throw new TransformationException("frame length exceeds " + maxFrameLength + " - discarding");
        }
    }

    /**
     * Find the index of the first occurrence of the given delimiter in the buffer.
     *
     * @param buffer the buffer to search
     * @param delim  the delimiter to search for
     * @return the index of the first byte of the delimiter, or -1 if not found
     */
    private static int indexOf(Buffer buffer, Buffer delim) {
        int delimLength = delim.remaining();
        if (delimLength == 0) {
            return 0;
        }
        int readableBytes = buffer.remaining();
        int readerIndex = buffer.position();
        byte firstByte = delim.get(delim.position());

        for (int i = 0; i < readableBytes - delimLength + 1; i++) {
            if (buffer.get(readerIndex + i) == firstByte) {
                boolean match = true;
                for (int j = 1; j < delimLength; j++) {
                    if (buffer.get(readerIndex + i + j) != delim.get(delim.position() + j)) {
                        match = false;
                        break;
                    }
                }
                if (match) {
                    return i;
                }
            }
        }
        return -1;
    }

    @Override
    public String getName() {
        return "DelimiterBasedFrameDecoder";
    }

    @Override
    public boolean hasInputRemaining(AttributeStorage storage, Buffer input) {
        return input != null && input.hasRemaining();
    }

    public Buffer readRetainedSlice(Buffer buffer, int length) {
        int readerIndex = buffer.position();
        // 返回 slice 共享内存视图,内存生命周期由父 buffer 管理
        // 调用方不应 dispose,Grizzly 框架会处理
        Buffer output = buffer.slice(readerIndex, readerIndex + length);
        buffer.position(readerIndex + length);
        return output;
    }

    private static Buffer skipBytes(Buffer input, int offset) {
        int readerIndex = input.position();
        input.position(readerIndex + offset);
        return input;
    }
}

FixedLengthFrameDecoder

import com.xkind.demo.socket.grizzly.internal.ObjectUtil;
import org.glassfish.grizzly.AbstractTransformer;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.TransformationException;
import org.glassfish.grizzly.TransformationResult;
import org.glassfish.grizzly.attributes.AttributeStorage;

import java.util.logging.Logger;

/**
 * A decoder that splits the received {@link Buffer}s by the fixed number of bytes.
 * For example, if you received the following four fragmented packets:
 * <pre>
 * +---+----+------+----+
 * | A | BC | DEFG | HI |
 * +---+----+------+----+
 * </pre>
 * A {@link FixedLengthFrameDecoder}({@code 3}) will decode them into the
 * following three packets:
 * <pre>
 * +-----+-----+-----+
 * | ABC | DEF | GHI |
 * +-----+-----+-----+
 * </pre>
 */
public class FixedLengthFrameDecoder extends AbstractTransformer<Buffer, Buffer> {

    private static final Logger logger = Grizzly.logger(FixedLengthFrameDecoder.class);

    private final int frameLength;

    /**
     * Creates a new instance.
     *
     * @param frameLength the length of the frame
     */
    public FixedLengthFrameDecoder(int frameLength) {
        ObjectUtil.checkPositive(frameLength, "frameLength");
        this.frameLength = frameLength;
    }

    @Override
    protected TransformationResult<Buffer, Buffer> transformImpl(AttributeStorage storage, Buffer input) throws TransformationException {
        if (input.remaining() < frameLength) {
            return TransformationResult.createIncompletedResult(input);
        } else {
            Buffer frame = readRetainedSlice(input, frameLength);
            return TransformationResult.createCompletedResult(frame, input);
        }
    }

    @Override
    public String getName() {
        return "FixedLengthFrameDecoder";
    }

    @Override
    public boolean hasInputRemaining(AttributeStorage storage, Buffer input) {
        return input != null && input.hasRemaining();
    }

    public Buffer readRetainedSlice(Buffer buffer, int length) {
        int readerIndex = buffer.position();
        // 返回 slice 共享内存视图,内存生命周期由父 buffer 管理
        // 调用方不应 dispose,Grizzly 框架会处理
        Buffer output = buffer.slice(readerIndex, readerIndex + length);
        buffer.position(readerIndex + length);
        return output;
    }
}

LengthFieldBasedFrameDecoder

import com.xkind.demo.socket.grizzly.internal.ObjectUtil;
import org.glassfish.grizzly.*;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.attributes.AttributeStorage;

import java.nio.ByteOrder;
import java.util.function.Supplier;
import java.util.logging.Logger;

public class LengthFieldBasedFrameDecoder extends AbstractTransformer<Buffer, Buffer> {

    private static final Logger logger = Grizzly.logger(LengthFieldBasedFrameDecoder.class);

    private final ByteOrder byteOrder;
    private final int maxFrameLength;
    private final int lengthFieldOffset;
    private final int lengthFieldLength;
    private final int lengthFieldEndOffset;
    private final int lengthAdjustment;
    private final int initialBytesToStrip;
    private final boolean failFast;

    // 连接级解码状态(存储在AttributeStorage中,每个连接独立)
    private final Attribute<DecoderState> decoderStateAttr;

    private static class DecoderState {
        boolean discardingTooLongFrame;
        long tooLongFrameLength;
        long bytesToDiscard;
    }

    public LengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
        this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
    }

    public LengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
        this(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, true);
    }

    public LengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        this(ByteOrder.BIG_ENDIAN, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
    }

    public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        this.byteOrder = ObjectUtil.checkNotNull(byteOrder, "byteOrder");
        ObjectUtil.checkPositive(maxFrameLength, "maxFrameLength");
        ObjectUtil.checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset");
        ObjectUtil.checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip");
        if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
            throw new IllegalArgumentException("maxFrameLength (" + maxFrameLength + ") must be equal to or greater than lengthFieldOffset (" + lengthFieldOffset + ") + lengthFieldLength (" + lengthFieldLength + ").");
        } else {
            this.maxFrameLength = maxFrameLength;
            this.lengthFieldOffset = lengthFieldOffset;
            this.lengthFieldLength = lengthFieldLength;
            this.lengthAdjustment = lengthAdjustment;
            this.lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
            this.initialBytesToStrip = initialBytesToStrip;
            this.failFast = failFast;
            this.decoderStateAttr = this.attributeBuilder.createAttribute(
                    this.getNamePrefix() + ".DecoderState"
            );
        }
    }

    @Override
    protected TransformationResult<Buffer, Buffer> transformImpl(AttributeStorage storage, Buffer input) throws TransformationException {
        Buffer decoded = decode(storage, input);
        if (decoded == null) {
            return TransformationResult.createIncompletedResult(input);
        }
        return TransformationResult.createCompletedResult(decoded, input);
    }

    @Override
    public String getName() {
        return "LengthFieldBasedFrameDecoder";
    }

    @Override
    public boolean hasInputRemaining(AttributeStorage storage, Buffer input) {
        return input != null && input.hasRemaining();
    }

    @Override
    public void release(AttributeStorage storage) {
        this.decoderStateAttr.remove(storage);
        super.release(storage);
    }

    private <T> T computeIfAbsent(AttributeStorage storage, Attribute<T> attribute, Supplier<T> supplier) {
        T value = attribute.get(storage);
        if (value == null) {
            value = supplier.get();
            attribute.set(storage, value);
        }
        return value;
    }

    protected Buffer decode(AttributeStorage storage, Buffer input) throws TransformationException {
        DecoderState state = computeIfAbsent(storage, this.decoderStateAttr, DecoderState::new);

        if (state.discardingTooLongFrame) {
            this.discardingTooLongFrame(input, state);
        }

        if (input.remaining() < this.lengthFieldEndOffset) {
            return null;
        } else {
            int actualLengthFieldOffset = input.position() + this.lengthFieldOffset;
            long frameLength = this.getUnadjustedFrameLength(input, actualLengthFieldOffset, this.lengthFieldLength, this.byteOrder);
            if (frameLength < 0L) {
                failOnNegativeLengthField(input, frameLength, this.lengthFieldEndOffset);
            }

            frameLength += (long) (this.lengthAdjustment + this.lengthFieldEndOffset);
            if (frameLength < (long) this.lengthFieldEndOffset) {
                failOnFrameLengthLessThanLengthFieldEndOffset(input, frameLength, this.lengthFieldEndOffset);
            }

            if (frameLength > (long) this.maxFrameLength) {
                this.exceededFrameLength(input, frameLength, state);
                return null;
            } else {
                int frameLengthInt = (int) frameLength;
                if (input.remaining() < frameLengthInt) {
                    return null;
                } else {
                    if (this.initialBytesToStrip > frameLengthInt) {
                        failOnFrameLengthLessThanInitialBytesToStrip(input, frameLength, this.initialBytesToStrip);
                    }

                    skipBytes(input, this.initialBytesToStrip);
                    int readerIndex = input.position();
                    int actualFrameLength = frameLengthInt - this.initialBytesToStrip;
                    Buffer frame = extractFrame(input, readerIndex, actualFrameLength);
                    readerIndex(input, readerIndex + actualFrameLength);
                    return frame;
                }
            }
        }
    }

    private void discardingTooLongFrame(Buffer in, DecoderState state) {
        long bytesToDiscard = state.bytesToDiscard;
        int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.remaining());
        skipBytes(in, localBytesToDiscard);
        bytesToDiscard -= (long) localBytesToDiscard;
        state.bytesToDiscard = bytesToDiscard;
        this.failIfNecessary(false, state);
    }

    private static void failOnNegativeLengthField(Buffer in, long frameLength, int lengthFieldEndOffset) {
        skipBytes(in, lengthFieldEndOffset);
        throw new TransformationException("negative pre-adjustment length field: " + frameLength);
    }

    private static void failOnFrameLengthLessThanLengthFieldEndOffset(Buffer in, long frameLength, int lengthFieldEndOffset) {
        skipBytes(in, lengthFieldEndOffset);
        throw new TransformationException("Adjusted frame length (" + frameLength + ") is less than lengthFieldEndOffset: " + lengthFieldEndOffset);
    }

    private void exceededFrameLength(Buffer in, long frameLength, DecoderState state) {
        long discard = frameLength - (long) in.remaining();
        state.tooLongFrameLength = frameLength;
        if (discard < 0L) {
            skipBytes(in, (int) frameLength);
        } else {
            state.discardingTooLongFrame = true;
            state.bytesToDiscard = discard;
            skipBytes(in, in.remaining());
        }

        this.failIfNecessary(true, state);
    }

    private static void failOnFrameLengthLessThanInitialBytesToStrip(Buffer in, long frameLength, int initialBytesToStrip) {
        skipBytes(in, (int) frameLength);
        throw new TransformationException("Adjusted frame length (" + frameLength + ") is less than initialBytesToStrip: " + initialBytesToStrip);
    }

    protected long getUnadjustedFrameLength(Buffer buf, int offset, int length, ByteOrder order) {
        long frameLength;
        switch (length) {
            case 1:
                frameLength = orderView(buf, order).get(offset) & 0xff;
                break;
            case 2:
                frameLength = orderView(buf, order).getShort(offset) & 0xffff;
                break;
            case 3:
                frameLength = readMedium(buf, offset, order);
                break;
            case 4:
                frameLength = orderView(buf, order).getInt(offset) & 0xffffffffL;
                break;
            case 8:
                frameLength = orderView(buf, order).getLong(offset);
                break;
            default:
                throw new TransformationException("unsupported lengthFieldLength: " + this.lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
        }
        return frameLength;
    }

    private int readMedium(Buffer buf, int offset, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            return ((buf.get(offset) & 0xFF) << 16)             // 第1字节:bit23~bit16
                    | ((buf.get(offset + 1) & 0xFF) << 8)       // 第2字节:bit15~bit8
                    | (buf.get(offset + 2) & 0xFF);             // 第3字节:bit7~bit0
        } else {
            return (buf.get(offset) & 0xFF)                     // 第1字节:bit7~bit0
                    | ((buf.get(offset + 1) & 0xFF) << 8)       // 第2字节:bit15~bit8
                    | ((buf.get(offset + 2) & 0xFF) << 16);     // 第3字节:bit23~bit16
        }
    }

    private Buffer orderView(Buffer buf, ByteOrder order) {
        if (order == buf.order()) {
            return buf;
        } else {
            return buf.duplicate().order(order);
        }
    }

    private void failIfNecessary(boolean firstDetectionOfTooLongFrame, DecoderState state) {
        if (state.bytesToDiscard == 0L) {
            long tooLongFrameLength = state.tooLongFrameLength;
            state.tooLongFrameLength = 0L;
            state.discardingTooLongFrame = false;
            if (!this.failFast || firstDetectionOfTooLongFrame) {
                this.fail(tooLongFrameLength);
            }
        } else if (this.failFast && firstDetectionOfTooLongFrame) {
            this.fail(state.tooLongFrameLength);
        }
    }

    protected Buffer extractFrame(Buffer buffer, int index, int length) {
        // 返回 slice 共享内存视图,内存生命周期由父 buffer 管理
        // 调用方不应 dispose,Grizzly 框架会处理
        return buffer.slice(index, index + length);
    }

    private static Buffer skipBytes(Buffer input, int offset) {
        int readerIndex = input.position();
        input.position(readerIndex + offset);
        return input;
    }

    private static Buffer readerIndex(Buffer input, int position) {
        input.position(position);
        return input;
    }

    private void fail(long frameLength) {
        if (frameLength > 0L) {
            throw new TransformationException("Adjusted frame length exceeds " + this.maxFrameLength + ": " + frameLength + " - discarded");
        } else {
            throw new TransformationException("Adjusted frame length exceeds " + this.maxFrameLength + " - discarding");
        }
    }
}

LengthFieldPrepender

import com.xkind.demo.socket.grizzly.internal.ObjectUtil;
import org.glassfish.grizzly.AbstractTransformer;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.TransformationException;
import org.glassfish.grizzly.TransformationResult;
import org.glassfish.grizzly.attributes.AttributeStorage;

import java.nio.ByteOrder;

public class LengthFieldPrepender extends AbstractTransformer<Buffer, Buffer> {

    private final ByteOrder byteOrder;
    private final int lengthFieldLength;
    private final boolean lengthIncludesLengthFieldLength;
    private final int lengthAdjustment;

    public LengthFieldPrepender(int lengthFieldLength) {
        this(lengthFieldLength, false);
    }

    public LengthFieldPrepender(int lengthFieldLength, boolean lengthIncludesLengthFieldLength) {
        this(lengthFieldLength, 0, lengthIncludesLengthFieldLength);
    }

    public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment) {
        this(lengthFieldLength, lengthAdjustment, false);
    }

    public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
        this(ByteOrder.BIG_ENDIAN, lengthFieldLength, lengthAdjustment, lengthIncludesLengthFieldLength);
    }

    public LengthFieldPrepender(ByteOrder byteOrder, int lengthFieldLength, int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
        if (lengthFieldLength != 1 && lengthFieldLength != 2 && lengthFieldLength != 3
                && lengthFieldLength != 4 && lengthFieldLength != 8) {
            throw new IllegalArgumentException("lengthFieldLength must be either 1, 2, 3, 4, or 8: " + lengthFieldLength);
        } else {
            this.byteOrder = ObjectUtil.checkNotNull(byteOrder, "byteOrder");
            this.lengthFieldLength = lengthFieldLength;
            this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;
            this.lengthAdjustment = lengthAdjustment;
        }
    }

    @Override
    protected TransformationResult<Buffer, Buffer> transformImpl(AttributeStorage storage, Buffer input) throws TransformationException {
        final int payloadLength = input.remaining();               // 实际 payload 长度
        int frameLength = payloadLength + this.lengthAdjustment;   // 长度字段要写入的值

        if (this.lengthIncludesLengthFieldLength) {
            frameLength += this.lengthFieldLength;
        }

        ObjectUtil.checkPositiveOrZero(frameLength, "length");
        final int totalSize = payloadLength + this.lengthFieldLength;
        Buffer output = this.obtainMemoryManager(storage).allocate(totalSize);
        // 保存原始字节序
        ByteOrder originalOrder = output.order();
        try {
            output.order(this.byteOrder);
            switch (this.lengthFieldLength) {
                case 1:
                    if (frameLength >= 256) {
                        throw new IllegalArgumentException("length does not fit into a byte: " + frameLength);
                    }

                    output.put((byte) frameLength);
                    break;
                case 2:
                    if (frameLength >= 65536) {
                        throw new IllegalArgumentException("length does not fit into a short integer: " + frameLength);
                    }

                    output.putShort((short) frameLength);
                    break;
                case 3:
                    if (frameLength >= 16777216) {  // 2^24 = 16,777,216
                        throw new IllegalArgumentException("length does not fit into a medium integer: " + frameLength);
                    }

                    writeMedium(output, frameLength, this.byteOrder);
                    break;
                case 4:
                    output.putInt(frameLength);
                    break;
                case 8:
                    output.putLong(frameLength);
                    break;
                default:
                    throw new Error("Should not reach here, lengthFieldLength validated in constructor");
            }
        } finally {
            // 恢复原字节序(关键!避免污染后续操作)
            output.order(originalOrder);
        }
        output.put(input).flip();
        output.allowBufferDispose(true);
        return TransformationResult.createCompletedResult(output, null);
    }

    @Override
    public String getName() {
        return "LengthFieldPrepender";
    }

    @Override
    public boolean hasInputRemaining(AttributeStorage attributeStorage, Buffer input) {
        return input != null && input.hasRemaining();
    }

    /**
     * 向 Buffer 写入 3 字节(24-bit)无符号整数。
     * <p>
     * Grizzly 的 Buffer 没有原生 writeMedium() 方法,需手动拆分 3 个字节。
     * 24-bit 范围:0 ~ 16,777,215 (0x000000 ~ 0xFFFFFF)
     *
     * @param buf   目标 Buffer
     * @param value 要写入的值(调用前已校验 < 2^24)
     * @param order 字节序
     */
    private void writeMedium(Buffer buf, int value, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            // 大端:高字节在前
            buf.put((byte) ((value >> 16) & 0xFF));  // value 的 bit23~bit16
            buf.put((byte) ((value >> 8) & 0xFF));   // value 的 bit15~bit8
            buf.put((byte) (value & 0xFF));          // value 的 bit7~bit0
        } else {
            // 小端:低字节在前
            buf.put((byte) (value & 0xFF));
            buf.put((byte) ((value >> 8) & 0xFF));
            buf.put((byte) ((value >> 16) & 0xFF));
        }
    }
}

ObjectUtil

package com.xkind.demo.socket.grizzly.internal;

import java.util.Collection;
import java.util.Map;

public final class ObjectUtil {
    private static final float FLOAT_ZERO = 0.0F;
    private static final double DOUBLE_ZERO = 0.0;
    private static final long LONG_ZERO = 0L;
    private static final int INT_ZERO = 0;

    private ObjectUtil() {
    }

    public static <T> T checkNotNull(T arg, String text) {
        if (arg == null) {
            throw new NullPointerException(text);
        } else {
            return arg;
        }
    }

    public static <T> T checkNotNullWithIAE(T arg, String paramName) throws IllegalArgumentException {
        if (arg == null) {
            throw new IllegalArgumentException("Param '" + paramName + "' must not be null");
        } else {
            return arg;
        }
    }

    public static <T> T checkNotNullArrayParam(T value, int index, String name) throws IllegalArgumentException {
        if (value == null) {
            throw new IllegalArgumentException("Array index " + index + " of parameter '" + name + "' must not be null");
        } else {
            return value;
        }
    }

    public static int checkPositive(int i, String name) {
        if (i <= 0) {
            throw new IllegalArgumentException(name + " : " + i + " (expected: > 0)");
        } else {
            return i;
        }
    }

    public static long checkPositive(long l, String name) {
        if (l <= 0L) {
            throw new IllegalArgumentException(name + " : " + l + " (expected: > 0)");
        } else {
            return l;
        }
    }

    public static double checkPositive(double d, String name) {
        if (d <= 0.0) {
            throw new IllegalArgumentException(name + " : " + d + " (expected: > 0)");
        } else {
            return d;
        }
    }

    public static float checkPositive(float f, String name) {
        if (f <= 0.0F) {
            throw new IllegalArgumentException(name + " : " + f + " (expected: > 0)");
        } else {
            return f;
        }
    }

    public static int checkPositiveOrZero(int i, String name) {
        if (i < 0) {
            throw new IllegalArgumentException(name + " : " + i + " (expected: >= 0)");
        } else {
            return i;
        }
    }

    public static long checkPositiveOrZero(long l, String name) {
        if (l < 0L) {
            throw new IllegalArgumentException(name + " : " + l + " (expected: >= 0)");
        } else {
            return l;
        }
    }

    public static double checkPositiveOrZero(double d, String name) {
        if (d < 0.0) {
            throw new IllegalArgumentException(name + " : " + d + " (expected: >= 0)");
        } else {
            return d;
        }
    }

    public static float checkPositiveOrZero(float f, String name) {
        if (f < 0.0F) {
            throw new IllegalArgumentException(name + " : " + f + " (expected: >= 0)");
        } else {
            return f;
        }
    }

    public static int checkInRange(int i, int start, int end, String name) {
        if (i >= start && i <= end) {
            return i;
        } else {
            throw new IllegalArgumentException(name + ": " + i + " (expected: " + start + "-" + end + ")");
        }
    }

    public static long checkInRange(long l, long start, long end, String name) {
        if (l >= start && l <= end) {
            return l;
        } else {
            throw new IllegalArgumentException(name + ": " + l + " (expected: " + start + "-" + end + ")");
        }
    }

    public static <T> T[] checkNonEmpty(T[] array, String name) {
        if (((Object[])checkNotNull(array, name)).length == 0) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return array;
        }
    }

    public static byte[] checkNonEmpty(byte[] array, String name) {
        if (((byte[])checkNotNull(array, name)).length == 0) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return array;
        }
    }

    public static char[] checkNonEmpty(char[] array, String name) {
        if (((char[])checkNotNull(array, name)).length == 0) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return array;
        }
    }

    public static <T extends Collection<?>> T checkNonEmpty(T collection, String name) {
        if (((Collection)checkNotNull(collection, name)).size() == 0) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return collection;
        }
    }

    public static String checkNonEmpty(String value, String name) {
        if (((String)checkNotNull(value, name)).isEmpty()) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return value;
        }
    }

    public static <K, V, T extends Map<K, V>> T checkNonEmpty(T value, String name) {
        if (((Map)checkNotNull(value, name)).isEmpty()) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return value;
        }
    }

    public static CharSequence checkNonEmpty(CharSequence value, String name) {
        if (((CharSequence)checkNotNull(value, name)).length() == 0) {
            throw new IllegalArgumentException("Param '" + name + "' must not be empty");
        } else {
            return value;
        }
    }

    public static String checkNonEmptyAfterTrim(String value, String name) {
        String trimmed = ((String)checkNotNull(value, name)).trim();
        return checkNonEmpty(trimmed, name);
    }

    public static int intValue(Integer wrapper, int defaultValue) {
        return wrapper != null ? wrapper : defaultValue;
    }

    public static long longValue(Long wrapper, long defaultValue) {
        return wrapper != null ? wrapper : defaultValue;
    }
}
posted @ 2022-09-01 13:28  XKIND  阅读(93)  评论(0)    收藏  举报