Fix bugs in ZlibDecoder / Port the factorial example
- Fixed IndexOutOfBoundsException in ZlibDecoder - Fixed a bug where ZlibDecoder raises an exception when a connection is closed
This commit is contained in:
parent
e00f303e4f
commit
81e8c49931
@ -52,13 +52,11 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
throw new NullPointerException("wrapper");
|
throw new NullPointerException("wrapper");
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (z) {
|
|
||||||
int resultCode = z.inflateInit(ZlibUtil.convertWrapperType(wrapper));
|
int resultCode = z.inflateInit(ZlibUtil.convertWrapperType(wrapper));
|
||||||
if (resultCode != JZlib.Z_OK) {
|
if (resultCode != JZlib.Z_OK) {
|
||||||
ZlibUtil.fail(z, "initialization failure", resultCode);
|
ZlibUtil.fail(z, "initialization failure", resultCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance with the specified preset dictionary. The wrapper
|
* Creates a new instance with the specified preset dictionary. The wrapper
|
||||||
@ -73,14 +71,12 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
}
|
}
|
||||||
this.dictionary = dictionary;
|
this.dictionary = dictionary;
|
||||||
|
|
||||||
synchronized (z) {
|
|
||||||
int resultCode;
|
int resultCode;
|
||||||
resultCode = z.inflateInit(JZlib.W_ZLIB);
|
resultCode = z.inflateInit(JZlib.W_ZLIB);
|
||||||
if (resultCode != JZlib.Z_OK) {
|
if (resultCode != JZlib.Z_OK) {
|
||||||
ZlibUtil.fail(z, "initialization failure", resultCode);
|
ZlibUtil.fail(z, "initialization failure", resultCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if and only if the end of the compressed stream
|
* Returns {@code true} if and only if the end of the compressed stream
|
||||||
@ -95,7 +91,10 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
ChannelInboundHandlerContext<Byte> ctx,
|
ChannelInboundHandlerContext<Byte> ctx,
|
||||||
ChannelBuffer in, ChannelBuffer out) throws Exception {
|
ChannelBuffer in, ChannelBuffer out) throws Exception {
|
||||||
|
|
||||||
synchronized (z) {
|
if (!in.readable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Configure input.
|
// Configure input.
|
||||||
int inputLength = in.readableBytes();
|
int inputLength = in.readableBytes();
|
||||||
@ -119,6 +118,7 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
z.next_out = new byte[maxOutputLength];
|
z.next_out = new byte[maxOutputLength];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
loop: for (;;) {
|
loop: for (;;) {
|
||||||
z.avail_out = maxOutputLength;
|
z.avail_out = maxOutputLength;
|
||||||
if (outHasArray) {
|
if (outHasArray) {
|
||||||
@ -131,14 +131,7 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
int oldNextOutIndex = z.next_out_index;
|
int oldNextOutIndex = z.next_out_index;
|
||||||
|
|
||||||
// Decompress 'in' into 'out'
|
// Decompress 'in' into 'out'
|
||||||
int resultCode;
|
int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH);
|
||||||
try {
|
|
||||||
resultCode = z.inflate(JZlib.Z_SYNC_FLUSH);
|
|
||||||
} finally {
|
|
||||||
if (inHasArray) {
|
|
||||||
in.skipBytes(z.next_in_index - oldNextInIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int outputLength = z.next_out_index - oldNextOutIndex;
|
int outputLength = z.next_out_index - oldNextOutIndex;
|
||||||
if (outputLength > 0) {
|
if (outputLength > 0) {
|
||||||
if (outHasArray) {
|
if (outHasArray) {
|
||||||
@ -174,6 +167,11 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
ZlibUtil.fail(z, "decompression failure", resultCode);
|
ZlibUtil.fail(z, "decompression failure", resultCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
if (inHasArray) {
|
||||||
|
in.skipBytes(z.next_in_index - oldNextInIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Deference the external references explicitly to tell the VM that
|
// Deference the external references explicitly to tell the VM that
|
||||||
// the allocated byte arrays are temporary so that the call stack
|
// the allocated byte arrays are temporary so that the call stack
|
||||||
@ -183,5 +181,4 @@ public class ZlibDecoder extends StreamToStreamDecoder {
|
|||||||
z.next_out = null;
|
z.next_out = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
import io.netty.buffer.ChannelBuffer;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
import io.netty.handler.codec.FrameDecoder;
|
import io.netty.handler.codec.StreamToMessageDecoder;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the binary representation of a {@link BigInteger} prepended
|
* Decodes the binary representation of a {@link BigInteger} prepended
|
||||||
@ -29,36 +28,35 @@ import io.netty.handler.codec.FrameDecoder;
|
|||||||
* {@link BigInteger} instance. For example, { 'F', 0, 0, 0, 1, 42 } will be
|
* {@link BigInteger} instance. For example, { 'F', 0, 0, 0, 1, 42 } will be
|
||||||
* decoded into new BigInteger("42").
|
* decoded into new BigInteger("42").
|
||||||
*/
|
*/
|
||||||
public class BigIntegerDecoder extends FrameDecoder {
|
public class BigIntegerDecoder extends StreamToMessageDecoder<BigInteger> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object decode(
|
public BigInteger decode(ChannelInboundHandlerContext<Byte> ctx, ChannelBuffer in) {
|
||||||
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
|
|
||||||
// Wait until the length prefix is available.
|
// Wait until the length prefix is available.
|
||||||
if (buffer.readableBytes() < 5) {
|
if (in.readableBytes() < 5) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.markReaderIndex();
|
in.markReaderIndex();
|
||||||
|
|
||||||
// Check the magic number.
|
// Check the magic number.
|
||||||
int magicNumber = buffer.readUnsignedByte();
|
int magicNumber = in.readUnsignedByte();
|
||||||
if (magicNumber != 'F') {
|
if (magicNumber != 'F') {
|
||||||
buffer.resetReaderIndex();
|
in.resetReaderIndex();
|
||||||
throw new CorruptedFrameException(
|
throw new CorruptedFrameException(
|
||||||
"Invalid magic number: " + magicNumber);
|
"Invalid magic number: " + magicNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until the whole data is available.
|
// Wait until the whole data is available.
|
||||||
int dataLength = buffer.readInt();
|
int dataLength = in.readInt();
|
||||||
if (buffer.readableBytes() < dataLength) {
|
if (in.readableBytes() < dataLength) {
|
||||||
buffer.resetReaderIndex();
|
in.resetReaderIndex();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the received data into a new BigInteger.
|
// Convert the received data into a new BigInteger.
|
||||||
byte[] decoded = new byte[dataLength];
|
byte[] decoded = new byte[dataLength];
|
||||||
buffer.readBytes(decoded);
|
in.readBytes(decoded);
|
||||||
|
|
||||||
return new BigInteger(decoded);
|
return new BigInteger(decoded);
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import io.netty.channel.ChannelBootstrap;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import io.netty.bootstrap.ClientBootstrap;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
import io.netty.channel.socket.nio.NioEventLoop;
|
||||||
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a sequence of integers to a {@link FactorialServer} to calculate
|
* Sends a sequence of integers to a {@link FactorialServer} to calculate
|
||||||
@ -39,32 +36,28 @@ public class FactorialClient {
|
|||||||
this.count = count;
|
this.count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() throws Exception {
|
||||||
// Configure the client.
|
ChannelBootstrap b = new ChannelBootstrap();
|
||||||
ClientBootstrap bootstrap = new ClientBootstrap(
|
try {
|
||||||
new NioClientSocketChannelFactory(
|
b.eventLoop(new NioEventLoop())
|
||||||
Executors.newCachedThreadPool()));
|
.channel(new NioSocketChannel())
|
||||||
|
.remoteAddress(host, port)
|
||||||
// Set up the event pipeline factory.
|
.initializer(new FactorialClientInitializer(count));
|
||||||
bootstrap.setPipelineFactory(new FactorialClientPipelineFactory(count));
|
|
||||||
|
|
||||||
// Make a new connection.
|
// Make a new connection.
|
||||||
ChannelFuture connectFuture =
|
ChannelFuture f = b.connect().sync();
|
||||||
bootstrap.connect(new InetSocketAddress(host, port));
|
|
||||||
|
|
||||||
// Wait until the connection is made successfully.
|
|
||||||
Channel channel = connectFuture.awaitUninterruptibly().channel();
|
|
||||||
|
|
||||||
// Get the handler instance to retrieve the answer.
|
// Get the handler instance to retrieve the answer.
|
||||||
FactorialClientHandler handler =
|
FactorialClientHandler handler =
|
||||||
(FactorialClientHandler) channel.pipeline().last();
|
(FactorialClientHandler) f.channel().pipeline().last();
|
||||||
|
|
||||||
// Print out the answer.
|
// Print out the answer.
|
||||||
System.err.format(
|
System.err.format(
|
||||||
"Factorial of %,d is: %,d", count, handler.getFactorial());
|
"Factorial of %,d is: %,d", count, handler.getFactorial());
|
||||||
|
|
||||||
// Shut down all thread pools to exit.
|
} finally {
|
||||||
bootstrap.releaseExternalResources();
|
b.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -15,22 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.ExceptionEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for a client-side channel. This handler maintains stateful
|
* Handler for a client-side channel. This handler maintains stateful
|
||||||
* information which is specific to a certain channel using member variables.
|
* information which is specific to a certain channel using member variables.
|
||||||
@ -38,12 +34,13 @@ import io.netty.channel.SimpleChannelUpstreamHandler;
|
|||||||
* to create a new handler instance whenever you create a new channel and insert
|
* to create a new handler instance whenever you create a new channel and insert
|
||||||
* this handler to avoid a race condition.
|
* this handler to avoid a race condition.
|
||||||
*/
|
*/
|
||||||
public class FactorialClientHandler extends SimpleChannelUpstreamHandler {
|
public class FactorialClientHandler extends ChannelInboundMessageHandlerAdapter<BigInteger> {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(
|
private static final Logger logger = Logger.getLogger(
|
||||||
FactorialClientHandler.class.getName());
|
FactorialClientHandler.class.getName());
|
||||||
|
|
||||||
// Stateful properties
|
private ChannelInboundHandlerContext<BigInteger> ctx;
|
||||||
|
private Queue<Object> out;
|
||||||
private int i = 1;
|
private int i = 1;
|
||||||
private int receivedMessages;
|
private int receivedMessages;
|
||||||
private final int count;
|
private final int count;
|
||||||
@ -69,34 +66,23 @@ public class FactorialClientHandler extends SimpleChannelUpstreamHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleUpstream(
|
public void channelActive(ChannelInboundHandlerContext<BigInteger> ctx) {
|
||||||
ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
|
this.ctx = ctx;
|
||||||
if (e instanceof ChannelStateEvent) {
|
out = ctx.out().messageBuffer();
|
||||||
logger.info(e.toString());
|
sendNumbers();
|
||||||
}
|
|
||||||
super.handleUpstream(ctx, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
|
|
||||||
sendNumbers(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) {
|
|
||||||
sendNumbers(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(
|
public void messageReceived(
|
||||||
ChannelHandlerContext ctx, final MessageEvent e) {
|
ChannelInboundHandlerContext<BigInteger> ctx, final BigInteger msg) {
|
||||||
receivedMessages ++;
|
receivedMessages ++;
|
||||||
if (receivedMessages == count) {
|
if (receivedMessages == count) {
|
||||||
// Offer the answer after closing the connection.
|
// Offer the answer after closing the connection.
|
||||||
e.channel().close().addListener(new ChannelFutureListener() {
|
ctx.channel().close().addListener(new ChannelFutureListener() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(ChannelFuture future) {
|
public void operationComplete(ChannelFuture future) {
|
||||||
boolean offered = answer.offer((BigInteger) e.getMessage());
|
boolean offered = answer.offer(msg);
|
||||||
assert offered;
|
assert offered;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -105,23 +91,38 @@ public class FactorialClientHandler extends SimpleChannelUpstreamHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(
|
public void exceptionCaught(
|
||||||
ChannelHandlerContext ctx, ExceptionEvent e) {
|
ChannelInboundHandlerContext<BigInteger> ctx, Throwable cause) throws Exception {
|
||||||
logger.log(
|
logger.log(
|
||||||
Level.WARNING,
|
Level.WARNING,
|
||||||
"Unexpected exception from downstream.",
|
"Unexpected exception from downstream.", cause);
|
||||||
e.cause());
|
ctx.close();
|
||||||
e.channel().close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendNumbers(ChannelStateEvent e) {
|
private void sendNumbers() {
|
||||||
Channel channel = e.channel();
|
// Do not send more than 4096 numbers.
|
||||||
while (channel.isWritable()) {
|
boolean finished = false;
|
||||||
|
while (out.size() < 4096) {
|
||||||
if (i <= count) {
|
if (i <= count) {
|
||||||
channel.write(Integer.valueOf(i));
|
out.add(Integer.valueOf(i));
|
||||||
i ++;
|
i ++;
|
||||||
} else {
|
} else {
|
||||||
|
finished = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChannelFuture f = ctx.flush();
|
||||||
|
if (!finished) {
|
||||||
|
f.addListener(SEND_NUMBERS);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ChannelFutureListener SEND_NUMBERS = new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future) throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
sendNumbers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.handler.codec.compression.ZlibDecoder;
|
import io.netty.handler.codec.compression.ZlibDecoder;
|
||||||
import io.netty.handler.codec.compression.ZlibEncoder;
|
import io.netty.handler.codec.compression.ZlibEncoder;
|
||||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||||
@ -26,18 +25,17 @@ import io.netty.handler.codec.compression.ZlibWrapper;
|
|||||||
/**
|
/**
|
||||||
* Creates a newly configured {@link ChannelPipeline} for a client-side channel.
|
* Creates a newly configured {@link ChannelPipeline} for a client-side channel.
|
||||||
*/
|
*/
|
||||||
public class FactorialClientPipelineFactory implements
|
public class FactorialClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
ChannelPipelineFactory {
|
|
||||||
|
|
||||||
private final int count;
|
private final int count;
|
||||||
|
|
||||||
public FactorialClientPipelineFactory(int count) {
|
public FactorialClientInitializer(int count) {
|
||||||
this.count = count;
|
this.count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline pipeline = pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
|
||||||
// Enable stream compression (you can remove these two if unnecessary)
|
// Enable stream compression (you can remove these two if unnecessary)
|
||||||
pipeline.addLast("deflater", new ZlibEncoder(ZlibWrapper.GZIP));
|
pipeline.addLast("deflater", new ZlibEncoder(ZlibWrapper.GZIP));
|
||||||
@ -49,7 +47,5 @@ public class FactorialClientPipelineFactory implements
|
|||||||
|
|
||||||
// and then business logic.
|
// and then business logic.
|
||||||
pipeline.addLast("handler", new FactorialClientHandler(count));
|
pipeline.addLast("handler", new FactorialClientHandler(count));
|
||||||
|
|
||||||
return pipeline;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,11 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import io.netty.channel.ServerChannelBootstrap;
|
||||||
import java.util.concurrent.Executors;
|
import io.netty.channel.socket.nio.NioEventLoop;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives a sequence of integers from a {@link FactorialClient} to calculate
|
* Receives a sequence of integers from a {@link FactorialClient} to calculate
|
||||||
@ -33,17 +31,18 @@ public class FactorialServer {
|
|||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() throws Exception {
|
||||||
// Configure the server.
|
ServerChannelBootstrap b = new ServerChannelBootstrap();
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap(
|
try {
|
||||||
new NioServerSocketChannelFactory(
|
b.eventLoop(new NioEventLoop(), new NioEventLoop())
|
||||||
Executors.newCachedThreadPool()));
|
.channel(new NioServerSocketChannel())
|
||||||
|
.localAddress(port)
|
||||||
|
.childInitializer(new FactorialServerInitializer());
|
||||||
|
|
||||||
// Set up the event pipeline factory.
|
b.bind().sync().channel().closeFuture().sync();
|
||||||
bootstrap.setPipelineFactory(new FactorialServerPipelineFactory());
|
} finally {
|
||||||
|
b.shutdown();
|
||||||
// Bind and start to accept incoming connections.
|
}
|
||||||
bootstrap.bind(new InetSocketAddress(port));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -15,18 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.ExceptionEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for a server-side channel. This handler maintains stateful
|
* Handler for a server-side channel. This handler maintains stateful
|
||||||
* information which is specific to a certain channel using member variables.
|
* information which is specific to a certain channel using member variables.
|
||||||
@ -34,54 +30,36 @@ import io.netty.channel.SimpleChannelUpstreamHandler;
|
|||||||
* to create a new handler instance whenever you create a new channel and insert
|
* to create a new handler instance whenever you create a new channel and insert
|
||||||
* this handler to avoid a race condition.
|
* this handler to avoid a race condition.
|
||||||
*/
|
*/
|
||||||
public class FactorialServerHandler extends SimpleChannelUpstreamHandler {
|
public class FactorialServerHandler extends ChannelInboundMessageHandlerAdapter<BigInteger> {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(
|
private static final Logger logger = Logger.getLogger(
|
||||||
FactorialServerHandler.class.getName());
|
FactorialServerHandler.class.getName());
|
||||||
|
|
||||||
// Stateful properties.
|
private BigInteger lastMultiplier = new BigInteger("1");
|
||||||
private int lastMultiplier = 1;
|
private BigInteger factorial = new BigInteger("1");
|
||||||
private BigInteger factorial = new BigInteger(new byte[] { 1 });
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleUpstream(
|
|
||||||
ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
|
|
||||||
if (e instanceof ChannelStateEvent) {
|
|
||||||
logger.info(e.toString());
|
|
||||||
}
|
|
||||||
super.handleUpstream(ctx, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(
|
public void messageReceived(
|
||||||
ChannelHandlerContext ctx, MessageEvent e) {
|
ChannelInboundHandlerContext<BigInteger> ctx, BigInteger msg) throws Exception {
|
||||||
|
|
||||||
// Calculate the cumulative factorial and send it to the client.
|
// Calculate the cumulative factorial and send it to the client.
|
||||||
BigInteger number;
|
lastMultiplier = msg;
|
||||||
if (e.getMessage() instanceof BigInteger) {
|
factorial = factorial.multiply(msg);
|
||||||
number = (BigInteger) e.getMessage();
|
ctx.write(factorial);
|
||||||
} else {
|
|
||||||
number = new BigInteger(e.getMessage().toString());
|
|
||||||
}
|
|
||||||
lastMultiplier = number.intValue();
|
|
||||||
factorial = factorial.multiply(number);
|
|
||||||
e.channel().write(factorial);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelDisconnected(ChannelHandlerContext ctx,
|
public void channelInactive(
|
||||||
ChannelStateEvent e) throws Exception {
|
ChannelInboundHandlerContext<BigInteger> ctx) throws Exception {
|
||||||
logger.info(new Formatter().format(
|
logger.info(new Formatter().format(
|
||||||
"Factorial of %,d is: %,d", lastMultiplier, factorial).toString());
|
"Factorial of %,d is: %,d", lastMultiplier, factorial).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(
|
public void exceptionCaught(
|
||||||
ChannelHandlerContext ctx, ExceptionEvent e) {
|
ChannelInboundHandlerContext<BigInteger> ctx, Throwable cause) throws Exception {
|
||||||
logger.log(
|
logger.log(
|
||||||
Level.WARNING,
|
Level.WARNING,
|
||||||
"Unexpected exception from downstream.",
|
"Unexpected exception from downstream.", cause);
|
||||||
e.cause());
|
ctx.close();
|
||||||
e.channel().close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.handler.codec.compression.ZlibDecoder;
|
import io.netty.handler.codec.compression.ZlibDecoder;
|
||||||
import io.netty.handler.codec.compression.ZlibEncoder;
|
import io.netty.handler.codec.compression.ZlibEncoder;
|
||||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||||
@ -26,12 +25,10 @@ import io.netty.handler.codec.compression.ZlibWrapper;
|
|||||||
/**
|
/**
|
||||||
* Creates a newly configured {@link ChannelPipeline} for a server-side channel.
|
* Creates a newly configured {@link ChannelPipeline} for a server-side channel.
|
||||||
*/
|
*/
|
||||||
public class FactorialServerPipelineFactory implements
|
public class FactorialServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
ChannelPipelineFactory {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline pipeline = pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
|
||||||
// Enable stream compression (you can remove these two if unnecessary)
|
// Enable stream compression (you can remove these two if unnecessary)
|
||||||
pipeline.addLast("deflater", new ZlibEncoder(ZlibWrapper.GZIP));
|
pipeline.addLast("deflater", new ZlibEncoder(ZlibWrapper.GZIP));
|
||||||
@ -45,7 +42,5 @@ public class FactorialServerPipelineFactory implements
|
|||||||
// Please note we create a handler for every new channel
|
// Please note we create a handler for every new channel
|
||||||
// because it has stateful properties.
|
// because it has stateful properties.
|
||||||
pipeline.addLast("handler", new FactorialServerHandler());
|
pipeline.addLast("handler", new FactorialServerHandler());
|
||||||
|
|
||||||
return pipeline;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,29 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.factorial;
|
package io.netty.example.factorial;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
import io.netty.buffer.ChannelBuffer;
|
||||||
import io.netty.buffer.ChannelBuffers;
|
import io.netty.channel.ChannelOutboundHandlerContext;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.handler.codec.MessageToStreamEncoder;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.oneone.OneToOneEncoder;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes a {@link Number} into the binary representation prepended with
|
* Encodes a {@link Number} into the binary representation prepended with
|
||||||
* a magic number ('F' or 0x46) and a 32-bit length prefix. For example, 42
|
* a magic number ('F' or 0x46) and a 32-bit length prefix. For example, 42
|
||||||
* will be encoded to { 'F', 0, 0, 0, 1, 42 }.
|
* will be encoded to { 'F', 0, 0, 0, 1, 42 }.
|
||||||
*/
|
*/
|
||||||
public class NumberEncoder extends OneToOneEncoder {
|
public class NumberEncoder extends MessageToStreamEncoder<Number> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object encode(
|
public void encode(
|
||||||
ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
|
ChannelOutboundHandlerContext<Number> ctx, Number msg, ChannelBuffer out) throws Exception {
|
||||||
if (!(msg instanceof Number)) {
|
|
||||||
// Ignore what this encoder can't encode.
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to a BigInteger first for easier implementation.
|
// Convert to a BigInteger first for easier implementation.
|
||||||
BigInteger v;
|
BigInteger v;
|
||||||
if (msg instanceof BigInteger) {
|
if (msg instanceof BigInteger) {
|
||||||
@ -50,13 +43,9 @@ public class NumberEncoder extends OneToOneEncoder {
|
|||||||
byte[] data = v.toByteArray();
|
byte[] data = v.toByteArray();
|
||||||
int dataLength = data.length;
|
int dataLength = data.length;
|
||||||
|
|
||||||
// Construct a message.
|
// Write a message.
|
||||||
ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
|
out.writeByte((byte) 'F'); // magic number
|
||||||
buf.writeByte((byte) 'F'); // magic number
|
out.writeInt(dataLength); // data length
|
||||||
buf.writeInt(dataLength); // data length
|
out.writeBytes(data); // data
|
||||||
buf.writeBytes(data); // data
|
|
||||||
|
|
||||||
// Return the constructed message.
|
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user