* More comments on examples
* Added JavaDoc for ChannelPipelineCoverage which is somewhat difficult to understand.
This commit is contained in:
parent
5e99787df6
commit
93f76ce594
@ -30,6 +30,31 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies if the same instance of the annotated {@link ChannelHandler} type
|
||||
* can be added to more than one {@link ChannelPipeline}.
|
||||
* <p>
|
||||
* All handler types are expected to specify this annotation. Otherwise you
|
||||
* will be warned in runtime. Only two values are allowed for this annotation:
|
||||
* {@code "all"} and {@code "one"}.
|
||||
* <p>
|
||||
* Please note that this annotation doesn't prevent a handler annotated with
|
||||
* the value {@code "one"} from being added to more than one pipeline. This
|
||||
* annotation is used for documentation purpose only.
|
||||
*
|
||||
* <h3>{@code ChannelPipelineCoverage("all")}</h3>
|
||||
*
|
||||
* {@code "all"} means you can add the same instance of the annotated handler
|
||||
* type to more than one {@link ChannelPipeline}. It means the member
|
||||
* variables of the handler instance is shared among multiple channels and it
|
||||
* is OK to do so.
|
||||
*
|
||||
* <h3>{@code ChannelPipelineCoverage("one")}</h3>
|
||||
*
|
||||
* {@code "one"} means you must create a new instance of the annotated handler
|
||||
* type for each new channel. It means the member variables of the handler
|
||||
* instance can not be shared at all, and violating this contract will lead
|
||||
* the handler to a race condition.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
@ -42,8 +67,19 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ChannelPipelineCoverage {
|
||||
|
||||
/**
|
||||
* {@code "all"}
|
||||
*/
|
||||
public static final String ALL = "all";
|
||||
|
||||
/**
|
||||
* {@code "one"}
|
||||
*/
|
||||
public static final String ONE = "one";
|
||||
|
||||
/**
|
||||
* The value of this annotation
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public class DiscardClient {
|
||||
firstMessageSize = 256;
|
||||
}
|
||||
|
||||
// Start client.
|
||||
// Configure the client.
|
||||
ChannelFactory factory =
|
||||
new NioClientSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -72,6 +72,7 @@ public class DiscardClient {
|
||||
bootstrap.setOption("tcpNoDelay", true);
|
||||
bootstrap.setOption("keepAlive", true);
|
||||
|
||||
// Start the connection attempt.
|
||||
bootstrap.connect(new InetSocketAddress(host, port));
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
/**
|
||||
* Handles a client-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
@ -106,6 +107,10 @@ public class DiscardClientHandler extends SimpleChannelHandler {
|
||||
}
|
||||
|
||||
private void generateTraffic(ChannelStateEvent e) {
|
||||
// Keep generating traffic until the channel is unwritable.
|
||||
// A channel becomes unwritable when its internal buffer is full.
|
||||
// If you keep writing messages ignoring this property,
|
||||
// you will end up with an OutOfMemoryError.
|
||||
Channel channel = e.getChannel();
|
||||
while (channel.isWritable()) {
|
||||
ChannelBuffer m = nextMessage();
|
||||
|
@ -40,7 +40,7 @@ import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
public class DiscardServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Start server.
|
||||
// Configure the server.
|
||||
ChannelFactory factory =
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -53,6 +53,7 @@ public class DiscardServer {
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(8080));
|
||||
|
||||
// Start performance monitor.
|
||||
|
@ -36,6 +36,7 @@ import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
/**
|
||||
* Handles a server-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
|
@ -22,12 +22,20 @@
|
||||
*/
|
||||
package org.jboss.netty.example.discard;
|
||||
|
||||
/**
|
||||
* Measures and prints the current throughput every 3 seconds.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class ThroughputMonitor extends Thread {
|
||||
|
||||
private final DiscardServerHandler handler;
|
||||
|
||||
public ThroughputMonitor(DiscardServerHandler echoHandler) {
|
||||
this.handler = echoHandler;
|
||||
public ThroughputMonitor(DiscardServerHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,6 +29,16 @@ import org.jboss.netty.bootstrap.ClientBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
|
||||
/**
|
||||
* Sends one message when a connection is open and echoes back any received
|
||||
* data to the server.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class EchoClient {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -51,7 +61,7 @@ public class EchoClient {
|
||||
firstMessageSize = 256;
|
||||
}
|
||||
|
||||
// Start client.
|
||||
// Configure the client.
|
||||
ChannelFactory factory =
|
||||
new NioClientSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -64,6 +74,7 @@ public class EchoClient {
|
||||
bootstrap.setOption("tcpNoDelay", true);
|
||||
bootstrap.setOption("keepAlive", true);
|
||||
|
||||
// Start the connection attempt.
|
||||
bootstrap.connect(new InetSocketAddress(host, port));
|
||||
|
||||
// Start performance monitor.
|
||||
|
@ -36,6 +36,15 @@ import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
/**
|
||||
* Handles both client-side and server-side handler depending on which
|
||||
* constructor was called.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("all")
|
||||
public class EchoHandler extends SimpleChannelHandler {
|
||||
|
||||
@ -45,12 +54,18 @@ public class EchoHandler extends SimpleChannelHandler {
|
||||
private final ChannelBuffer firstMessage;
|
||||
private final AtomicLong transferredBytes = new AtomicLong();
|
||||
|
||||
/**
|
||||
* Creates a server-side handler.
|
||||
*/
|
||||
public EchoHandler() {
|
||||
this(0);
|
||||
firstMessage = ChannelBuffer.EMPTY_BUFFER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client-side handler.
|
||||
*/
|
||||
public EchoHandler(int firstMessageSize) {
|
||||
if (firstMessageSize < 0) {
|
||||
if (firstMessageSize <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"firstMessageSize: " + firstMessageSize);
|
||||
}
|
||||
@ -70,18 +85,23 @@ public class EchoHandler extends SimpleChannelHandler {
|
||||
if (e instanceof ChannelStateEvent) {
|
||||
logger.info(e.toString());
|
||||
}
|
||||
|
||||
// Let SimpleChannelHandler call actual event handler methods below.
|
||||
super.handleUpstream(ctx, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelConnected(
|
||||
ChannelHandlerContext ctx, ChannelStateEvent e) {
|
||||
// Send the first message. Server will not send anything here
|
||||
// because the firstMessage's capacity is 0.
|
||||
e.getChannel().write(firstMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(
|
||||
ChannelHandlerContext ctx, MessageEvent e) {
|
||||
// Send back the received message to the remote peer.
|
||||
transferredBytes.addAndGet(((ChannelBuffer) e.getMessage()).readableBytes());
|
||||
e.getChannel().write(e.getMessage());
|
||||
}
|
||||
@ -89,6 +109,7 @@ public class EchoHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void exceptionCaught(
|
||||
ChannelHandlerContext ctx, ExceptionEvent e) {
|
||||
// Close the connection when an exception is raised.
|
||||
logger.log(
|
||||
Level.WARNING,
|
||||
"Unexpected exception from downstream.",
|
||||
|
@ -29,10 +29,19 @@ import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
|
||||
/**
|
||||
* Echoes back any received data from a client.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class EchoServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Start server.
|
||||
// Configure the server.
|
||||
ChannelFactory factory =
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -45,6 +54,7 @@ public class EchoServer {
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(8080));
|
||||
|
||||
// Start performance monitor.
|
||||
|
@ -22,17 +22,25 @@
|
||||
*/
|
||||
package org.jboss.netty.example.echo;
|
||||
|
||||
/**
|
||||
* Measures and prints the current throughput every 3 seconds.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class ThroughputMonitor extends Thread {
|
||||
|
||||
private final EchoHandler echoHandler;
|
||||
private final EchoHandler handler;
|
||||
|
||||
public ThroughputMonitor(EchoHandler echoHandler) {
|
||||
this.echoHandler = echoHandler;
|
||||
public ThroughputMonitor(EchoHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long oldCounter = echoHandler.getTransferredBytes();
|
||||
long oldCounter = handler.getTransferredBytes();
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (;;) {
|
||||
try {
|
||||
@ -42,7 +50,7 @@ public class ThroughputMonitor extends Thread {
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long newCounter = echoHandler.getTransferredBytes();
|
||||
long newCounter = handler.getTransferredBytes();
|
||||
System.err.format(
|
||||
"%4.3f MiB/s%n",
|
||||
(newCounter - oldCounter) * 1000 / (endTime - startTime) /
|
||||
|
@ -30,28 +30,35 @@ import org.jboss.netty.channel.ChannelHandlerContext;
|
||||
import org.jboss.netty.handler.codec.frame.FrameDecoder;
|
||||
|
||||
/**
|
||||
* Decodes the binary representation of a {@link BigInteger} with 32-bit
|
||||
* integer length prefix into a Java {@link BigInteger} instance. For example,
|
||||
* { 0, 0, 0, 1, 42 } will be decoded into new BigInteger("42").
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class BigIntegerDecoder extends FrameDecoder {
|
||||
|
||||
@Override
|
||||
protected Object decode(
|
||||
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
|
||||
// Wait until the length prefix is available.
|
||||
if (buffer.readableBytes() < 4) {
|
||||
return null;
|
||||
}
|
||||
int dataLength = buffer.getInt(buffer.readerIndex());
|
||||
|
||||
// Wait until the whole data is available.
|
||||
if (buffer.readableBytes() < dataLength + 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Skip the length field because we know it already.
|
||||
buffer.skipBytes(4);
|
||||
|
||||
// Convert the received data into a new BigInteger.
|
||||
byte[] decoded = new byte[dataLength];
|
||||
buffer.readBytes(decoded);
|
||||
|
||||
|
@ -30,6 +30,15 @@ import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.ChannelFuture;
|
||||
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
|
||||
/**
|
||||
* Sends a sequence of integers to a {@link FactorialServer} to calculate
|
||||
* the factorial of the specified integer.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class FactorialClient {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
@ -39,12 +39,27 @@ import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
@ChannelPipelineCoverage("one")
|
||||
/**
|
||||
* Handler for a client-side channel. Please note that this handler's
|
||||
* {@link ChannelPipelineCoverage} annotation value is "one". It means
|
||||
* this handler maintains some stateful information which is specific to
|
||||
* a certain channel. Therefore, an instance of this handler can
|
||||
* cover only one ChannelPipeline and Channel pair. You have to create
|
||||
* a new handler instance whenever you create a new channel and insert
|
||||
* this handler to avoid a race condition.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("one") // <-- HERE
|
||||
public class FactorialClientHandler extends SimpleChannelHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(
|
||||
FactorialClientHandler.class.getName());
|
||||
|
||||
// Stateful properties
|
||||
private int i = 1;
|
||||
private int receivedMessages = 0;
|
||||
private final int count;
|
||||
|
@ -28,11 +28,12 @@ import org.jboss.netty.channel.ChannelPipeline;
|
||||
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||
|
||||
/**
|
||||
* Creates a newly configured {@link ChannelPipeline} for a client-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class FactorialClientPipelineFactory implements
|
||||
ChannelPipelineFactory {
|
||||
|
@ -23,11 +23,12 @@
|
||||
package org.jboss.netty.example.factorial;
|
||||
|
||||
/**
|
||||
* Thrown when there was a protocol violation during communication.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class FactorialProtocolException extends Exception {
|
||||
|
||||
|
@ -29,10 +29,19 @@ import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
|
||||
/**
|
||||
* Receives a sequence of integers from a {@link FactorialClient} to calculate
|
||||
* the factorial of the specified integer.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class FactorialServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Start server.
|
||||
// Configure the server.
|
||||
ChannelFactory factory =
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -44,6 +53,7 @@ public class FactorialServer {
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(8080));
|
||||
}
|
||||
}
|
||||
|
@ -35,12 +35,27 @@ import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
@ChannelPipelineCoverage("one")
|
||||
/**
|
||||
* Handler for a server-side channel. Please note that this handler's
|
||||
* {@link ChannelPipelineCoverage} annotation value is "one". It means
|
||||
* this handler maintains some stateful information which is specific to
|
||||
* a certain channel. Therefore, an instance of this handler can
|
||||
* cover only one ChannelPipeline and Channel pair. You have to create
|
||||
* a new handler instance whenever you create a new channel and insert
|
||||
* this handler to avoid a race condition.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("one") // <-- HERE
|
||||
public class FactorialServerHandler extends SimpleChannelHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(
|
||||
FactorialServerHandler.class.getName());
|
||||
|
||||
// Stateful properties.
|
||||
private int lastMultiplier = 1;
|
||||
private BigInteger factorial = new BigInteger(new byte[] { 1 });
|
||||
|
||||
|
@ -28,6 +28,8 @@ import org.jboss.netty.channel.ChannelPipeline;
|
||||
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||
|
||||
/**
|
||||
* Creates a newly configured {@link ChannelPipeline} for a server-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
@ -45,6 +47,8 @@ public class FactorialServerPipelineFactory implements
|
||||
pipeline.addLast("encoder", new NumberEncoder());
|
||||
|
||||
// and then business logic.
|
||||
// Please note we create a handler for every new channel
|
||||
// because it has stateful properties.
|
||||
pipeline.addLast("handler", new FactorialServerHandler());
|
||||
|
||||
return pipeline;
|
||||
|
@ -35,6 +35,9 @@ import org.jboss.netty.channel.ChannelPipelineCoverage;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
|
||||
/**
|
||||
* Encodes a {@link Number} into the binary representation with a 32-bit length
|
||||
* prefix. For example, 42 will be encoded to { 0, 0, 0, 1, 42 }.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
|
@ -28,7 +28,16 @@ import java.util.concurrent.Executors;
|
||||
import org.jboss.netty.bootstrap.ClientBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
import org.jboss.netty.example.echo.EchoClient;
|
||||
|
||||
/**
|
||||
* Modification of {@link EchoClient} which utilizes Java object serialization.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class ObjectEchoClient {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -51,7 +60,7 @@ public class ObjectEchoClient {
|
||||
firstMessageSize = 256;
|
||||
}
|
||||
|
||||
// Start client.
|
||||
// Configure the client.
|
||||
ChannelFactory factory =
|
||||
new NioClientSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -64,6 +73,7 @@ public class ObjectEchoClient {
|
||||
bootstrap.setOption("tcpNoDelay", true);
|
||||
bootstrap.setOption("keepAlive", true);
|
||||
|
||||
// Start the connection attempt.
|
||||
bootstrap.connect(new InetSocketAddress(host, port));
|
||||
|
||||
// Start performance monitor.
|
||||
|
@ -39,6 +39,15 @@ import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
import org.jboss.netty.handler.codec.serialization.ObjectDecoder;
|
||||
import org.jboss.netty.handler.codec.serialization.ObjectEncoder;
|
||||
|
||||
/**
|
||||
* Handles both client-side and server-side handler depending on which
|
||||
* constructor was called.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("all")
|
||||
public class ObjectEchoHandler extends SimpleChannelHandler {
|
||||
|
||||
@ -48,12 +57,18 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
|
||||
private final List<Integer> firstMessage;
|
||||
private final AtomicLong transferredMessages = new AtomicLong();
|
||||
|
||||
/**
|
||||
* Creates a server-side handler.
|
||||
*/
|
||||
public ObjectEchoHandler() {
|
||||
this(0);
|
||||
firstMessage = new ArrayList<Integer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client-side handler.
|
||||
*/
|
||||
public ObjectEchoHandler(int firstMessageSize) {
|
||||
if (firstMessageSize < 0) {
|
||||
if (firstMessageSize <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"firstMessageSize: " + firstMessageSize);
|
||||
}
|
||||
@ -80,6 +95,8 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void channelOpen(ChannelHandlerContext ctx,
|
||||
ChannelStateEvent e) throws Exception {
|
||||
// Add encoder and decoder as soon as a new channel is created so that
|
||||
// a Java object is serialized and deserialized.
|
||||
e.getChannel().getPipeline().addFirst("encoder", new ObjectEncoder());
|
||||
e.getChannel().getPipeline().addFirst("decoder", new ObjectDecoder());
|
||||
}
|
||||
@ -87,6 +104,7 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void channelConnected(
|
||||
ChannelHandlerContext ctx, ChannelStateEvent e) {
|
||||
// Send the first message if this handler is a client-side handler.
|
||||
if (!firstMessage.isEmpty()) {
|
||||
e.getChannel().write(firstMessage);
|
||||
}
|
||||
@ -95,6 +113,7 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void messageReceived(
|
||||
ChannelHandlerContext ctx, MessageEvent e) {
|
||||
// Echo back the received object to the client.
|
||||
transferredMessages.incrementAndGet();
|
||||
e.getChannel().write(e.getMessage());
|
||||
}
|
||||
|
@ -28,11 +28,20 @@ import java.util.concurrent.Executors;
|
||||
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.jboss.netty.example.echo.EchoServer;
|
||||
|
||||
/**
|
||||
* Modification of {@link EchoServer} which utilizes Java object serialization.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class ObjectEchoServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Start server.
|
||||
// Configure the server.
|
||||
ChannelFactory factory =
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -45,6 +54,7 @@ public class ObjectEchoServer {
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(8080));
|
||||
|
||||
// Start performance monitor.
|
||||
|
@ -22,17 +22,25 @@
|
||||
*/
|
||||
package org.jboss.netty.example.objectecho;
|
||||
|
||||
/**
|
||||
* Measures and prints the current throughput every 3 seconds.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class ThroughputMonitor extends Thread {
|
||||
|
||||
private final ObjectEchoHandler echoHandler;
|
||||
private final ObjectEchoHandler handler;
|
||||
|
||||
public ThroughputMonitor(ObjectEchoHandler echoHandler) {
|
||||
this.echoHandler = echoHandler;
|
||||
public ThroughputMonitor(ObjectEchoHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long oldCounter = echoHandler.getTransferredMessages();
|
||||
long oldCounter = handler.getTransferredMessages();
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (;;) {
|
||||
try {
|
||||
@ -42,7 +50,7 @@ public class ThroughputMonitor extends Thread {
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long newCounter = echoHandler.getTransferredMessages();
|
||||
long newCounter = handler.getTransferredMessages();
|
||||
System.err.format(
|
||||
"%4.3f msgs/s%n",
|
||||
(newCounter - oldCounter) * 1000 / (double) (endTime - startTime));
|
||||
|
@ -32,7 +32,17 @@ import org.jboss.netty.channel.Channel;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.ChannelFuture;
|
||||
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
import org.jboss.netty.example.telnet.TelnetClient;
|
||||
|
||||
/**
|
||||
* Simple SSL chat client modified from {@link TelnetClient}.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class SecureChatClient {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -48,7 +58,7 @@ public class SecureChatClient {
|
||||
String host = args[0];
|
||||
int port = Integer.parseInt(args[1]);
|
||||
|
||||
// Start client.
|
||||
// Configure the client.
|
||||
ChannelFactory factory =
|
||||
new NioClientSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -61,6 +71,7 @@ public class SecureChatClient {
|
||||
bootstrap.setOption("tcpNoDelay", true);
|
||||
bootstrap.setOption("keepAlive", true);
|
||||
|
||||
// Start the connection attempt.
|
||||
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
|
||||
|
||||
// Wait until the connection attempt succeeds or fails.
|
||||
@ -86,6 +97,8 @@ public class SecureChatClient {
|
||||
lastWriteFuture.awaitUninterruptibly();
|
||||
}
|
||||
|
||||
// Close the connection. Make sure the close operation ends because
|
||||
// all I/O operations are asynchronous in Netty.
|
||||
channel.close().awaitUninterruptibly();
|
||||
|
||||
// We should shut down all thread pools here to exit normally.
|
||||
|
@ -34,6 +34,14 @@ import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
import org.jboss.netty.handler.ssl.SslHandler;
|
||||
|
||||
/**
|
||||
* Handles a client-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("all")
|
||||
public class SecureChatClientHandler extends SimpleChannelHandler {
|
||||
|
||||
@ -52,9 +60,11 @@ public class SecureChatClientHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void channelConnected(
|
||||
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
|
||||
|
||||
// Get the SslHandler and begin handshake ASAP.
|
||||
// Get the SslHandler from the pipeline
|
||||
// which were added in SecureChatPipelineFactory.
|
||||
SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
|
||||
|
||||
// Begin handshake.
|
||||
sslHandler.handshake(e.getChannel());
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,13 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A bogus key store which provides all the required information to
|
||||
* create an example SSL connection.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class SecureChatKeyStore {
|
||||
private static final short[] DATA = new short[] {
|
||||
|
@ -36,6 +36,8 @@ import org.jboss.netty.handler.codec.string.StringEncoder;
|
||||
import org.jboss.netty.handler.ssl.SslHandler;
|
||||
|
||||
/**
|
||||
* Creates a newly configured {@link ChannelPipeline} for a new channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
@ -55,8 +57,11 @@ public class SecureChatPipelineFactory implements
|
||||
ChannelPipeline pipeline = pipeline();
|
||||
|
||||
// Add SSL handler first to encrypt and decrypt everything.
|
||||
// In this example, we use a bogus certificate in the server side
|
||||
// and accept any invalid certificates in the client side.
|
||||
// You will need something more complicated to identify both
|
||||
// and server in the real world.
|
||||
|
||||
SSLEngine engine;
|
||||
if (handler instanceof SecureChatClientHandler) {
|
||||
engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();
|
||||
|
@ -28,11 +28,20 @@ import java.util.concurrent.Executors;
|
||||
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.jboss.netty.example.telnet.TelnetServer;
|
||||
|
||||
/**
|
||||
* Simple SSL chat server modified from {@link TelnetServer}.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class SecureChatServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Start server.
|
||||
// Configure the server.
|
||||
ChannelFactory factory =
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -45,6 +54,7 @@ public class SecureChatServer {
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(8080));
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,14 @@ import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
import org.jboss.netty.handler.ssl.SslHandler;
|
||||
import org.jboss.netty.util.MapBackedSet;
|
||||
|
||||
/**
|
||||
* Handles a server-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("all")
|
||||
public class SecureChatServerHandler extends SimpleChannelHandler {
|
||||
|
||||
@ -64,6 +72,7 @@ public class SecureChatServerHandler extends SimpleChannelHandler {
|
||||
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
|
||||
|
||||
// Get the SslHandler in the current pipeline.
|
||||
// We added it in SecureChatPipelineFactory.
|
||||
final SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
|
||||
|
||||
// Get notified when SSL handshake is done.
|
||||
@ -113,7 +122,7 @@ public class SecureChatServerHandler extends SimpleChannelHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Close the connection if the client sent 'bye'.
|
||||
// Close the connection if the client has sent 'bye'.
|
||||
if (request.toLowerCase().equals("bye")) {
|
||||
e.getChannel().close();
|
||||
}
|
||||
|
@ -29,11 +29,16 @@ import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
* Creates a bogus {@link SSLContext}. A client-side context created by this
|
||||
* factory accepts any certificate even if it's invalid. A server-side context
|
||||
* created by this factory sends a bogus certificate defined in {@link SecureChatKeyStore}.
|
||||
*
|
||||
* You will have to create your context differently in a real world application.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class SecureChatSslContextFactory {
|
||||
|
||||
|
@ -34,11 +34,13 @@ import javax.net.ssl.TrustManagerFactorySpi;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
* Bogus {@link TrustManagerFactorySpi} which accepts any certificate
|
||||
* even if it's invalid.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*
|
||||
*/
|
||||
public class SecureChatTrustManagerFactory extends TrustManagerFactorySpi {
|
||||
|
||||
|
@ -33,6 +33,14 @@ import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.ChannelFuture;
|
||||
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
|
||||
/**
|
||||
* Simplistic telnet client.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class TelnetClient {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -48,7 +56,7 @@ public class TelnetClient {
|
||||
String host = args[0];
|
||||
int port = Integer.parseInt(args[1]);
|
||||
|
||||
// Start client.
|
||||
// Configure the client.
|
||||
ChannelFactory factory =
|
||||
new NioClientSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -61,6 +69,7 @@ public class TelnetClient {
|
||||
bootstrap.setOption("tcpNoDelay", true);
|
||||
bootstrap.setOption("keepAlive", true);
|
||||
|
||||
// Start the connection attempt.
|
||||
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
|
||||
|
||||
// Wait until the connection attempt succeeds or fails.
|
||||
@ -78,6 +87,8 @@ public class TelnetClient {
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Sends the received line to the server.
|
||||
lastWriteFuture = channel.write(line + '\n');
|
||||
}
|
||||
|
||||
@ -86,6 +97,8 @@ public class TelnetClient {
|
||||
lastWriteFuture.awaitUninterruptibly();
|
||||
}
|
||||
|
||||
// Close the connection. Make sure the close operation ends because
|
||||
// all I/O operations are asynchronous in Netty.
|
||||
channel.close().awaitUninterruptibly();
|
||||
|
||||
// We should shut down all thread pools here to exit normally.
|
||||
|
@ -33,6 +33,14 @@ import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
/**
|
||||
* Handles a client-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("all")
|
||||
public class TelnetClientHandler extends SimpleChannelHandler {
|
||||
|
||||
@ -51,6 +59,7 @@ public class TelnetClientHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void messageReceived(
|
||||
ChannelHandlerContext ctx, MessageEvent e) {
|
||||
// Print out the line received from the server.
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ import org.jboss.netty.handler.codec.string.StringDecoder;
|
||||
import org.jboss.netty.handler.codec.string.StringEncoder;
|
||||
|
||||
/**
|
||||
* Creates a newly configured {@link ChannelPipeline} for a new channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
@ -49,9 +51,10 @@ public class TelnetPipelineFactory implements
|
||||
}
|
||||
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
// Create a default pipeline implementation.
|
||||
ChannelPipeline pipeline = pipeline();
|
||||
|
||||
// Add the text line codec first,
|
||||
// Add the text line codec combination first,
|
||||
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
|
||||
8192, Delimiters.lineDelimiter()));
|
||||
pipeline.addLast("decoder", new StringDecoder());
|
||||
|
@ -29,10 +29,18 @@ import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
|
||||
/**
|
||||
* Simplistic telnet server.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
public class TelnetServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Start server.
|
||||
// Configure the server.
|
||||
ChannelFactory factory =
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool(),
|
||||
@ -45,6 +53,7 @@ public class TelnetServer {
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(8080));
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,14 @@ import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||
|
||||
/**
|
||||
* Handles a server-side channel.
|
||||
*
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @version $Rev$, $Date$
|
||||
*/
|
||||
@ChannelPipelineCoverage("all")
|
||||
public class TelnetServerHandler extends SimpleChannelHandler {
|
||||
|
||||
@ -55,7 +63,7 @@ public class TelnetServerHandler extends SimpleChannelHandler {
|
||||
@Override
|
||||
public void channelConnected(
|
||||
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
|
||||
// Send greeting.
|
||||
// Send greeting for a new connection.
|
||||
e.getChannel().write(
|
||||
"Welcome to " + InetAddress.getLocalHost().getHostName() + "!\n");
|
||||
e.getChannel().write("It's " + new Date() + " now.\n");
|
||||
@ -65,7 +73,8 @@ public class TelnetServerHandler extends SimpleChannelHandler {
|
||||
public void messageReceived(
|
||||
ChannelHandlerContext ctx, MessageEvent e) {
|
||||
|
||||
// Convert to a String first.
|
||||
// Cast to a String first.
|
||||
// We know it's a String because we put some codec in TelnetPipelineFactory.
|
||||
String request = (String) e.getMessage();
|
||||
|
||||
// Generate and write a response.
|
||||
@ -80,9 +89,12 @@ public class TelnetServerHandler extends SimpleChannelHandler {
|
||||
response = "Did you say '" + request + "'?\n";
|
||||
}
|
||||
|
||||
// We don't need to write a ChannelBuffer here.
|
||||
// We know the encoder inserted at TelnetPipelineFactory will do the conversion.
|
||||
ChannelFuture future = e.getChannel().write(response);
|
||||
|
||||
// Close the connection after sending 'Have a good day!'
|
||||
// if the client has sent 'bye'.
|
||||
if (close) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user