【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;
}
}

浙公网安备 33010602011771号