作者 钟来

初始提交

正在显示 93 个修改的文件 包含 4237 行增加0 行删除
/target/
/.idea/
/.settings/
.classpath
.project
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-all:4.1.30.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.6.3" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.6.3" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.6.0" level="project" />
</component>
</module>
\ No newline at end of file
... ...
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.waylau</groupId>
<artifactId>netty4-demos</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>netty4-demos</name>
<url>http://www.waylau.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.jackson.core>2.6.3</version.jackson.core>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<optimize>true</optimize>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.30.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${version.jackson.core}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${version.jackson.core}</version>
</dependency>
</dependencies>
</project>
... ...
package com.waylau.netty;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
... ...
package com.waylau.netty;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* 测试用的 TCP 客户端
*
* @author waylau.com
* @date 2015-2-26
*/
public class TcpClient {
public static void main(String[] args) throws IOException {
Socket socket = null;
OutputStream out = null;
try {
socket = new Socket("localhost", 8023);
out = socket.getOutputStream();
// 请求服务器
String lines = "床前明月光\r\n疑是地上霜\r\n举头望明月\r\n低头思故乡\r\n";
byte[] outputBytes = lines.getBytes("UTF-8");
out.write(outputBytes);
out.flush();
} finally {
// 关闭连接
out.close();
socket.close();
}
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import java.util.List;
import java.util.Map;
/**
* 说明:一个用户 POJO
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日
*/
public class JacksonBean {
private int age;
private String name;
private List<String> sons;
private Map<String, String> addrs;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getSons() {
return sons;
}
public void setSons(List<String> sons) {
this.sons = sons;
}
public Map<String, String> getAddrs() {
return addrs;
}
public void setAddrs(Map<String, String> addrs) {
this.addrs = addrs;
}
/**
*
*/
public JacksonBean() {
// TODO Auto-generated constructor stub
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* 说明:Jackson json-客户端
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class JacksonClient {
public static void main(String[] args) throws Exception{
new JacksonClient("localhost", 8082).run();
}
private final String host;
private final int port;
public JacksonClient(String host, int port){
this.host = host;
this.port = port;
}
public void run() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new JacksonClientHandlerInitializer());
Channel channel = bootstrap.connect(host, port).sync().channel();
// 发送对象
JacksonBean user = new JacksonBean();
user.setAge(27);
user.setName("waylau");
List<String> sons = new ArrayList<String>();
for (int i = 0;i <10; i++) {
sons.add("Lucy"+i);
sons.add("Lily"+i);
}
user.setSons(sons);
Map<String, String> addrs = new HashMap<String, String>();
for (int i = 0;i <10; i++) {
addrs.put("001"+i, "18998366112");
addrs.put("002"+i, "15014965012");
}
user.setAddrs(addrs);
channel.write(user);
channel.flush();
// 等待连接关闭
channel.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.codec.jackcon;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class JacksonClientHandler extends
SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object obj)
throws Exception {
String jsonString = "";
if (obj instanceof JacksonBean) {
JacksonBean user = (JacksonBean) obj;
jsonString = JacksonMapper.getInstance().writeValueAsString(user); // 对象转为json字符串
} else {
jsonString = JacksonMapper.getInstance().writeValueAsString(obj); // 对象转为json字符串
}
System.out.println("Client get msg form Server -" + jsonString);
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
/**
* 说明:处理器初始化
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class JacksonClientHandlerInitializer extends
ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new JacksonDecoder<JacksonBean>(JacksonBean.class));
pipeline.addLast(new JacksonEncoder());
pipeline.addLast(new JacksonClientHandler());
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.codec.jackcon;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
* 说明:Jackson json 解码器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日
*/
public class JacksonDecoder<T> extends ByteToMessageDecoder {
private final Class<T> clazz;
/**
*
*/
public JacksonDecoder(Class<T> clazz) {
this.clazz = clazz;
}
/*
* (non-Javadoc)
*
* @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel.
* ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List)
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
ByteBufInputStream byteBufInputStream = new ByteBufInputStream(in);
out.add(JacksonMapper.getInstance().readValue(byteBufInputStream, clazz));
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.codec.jackcon;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* 说明:Jackson json 编码器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日
*/
public class JacksonEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out)
throws Exception {
ObjectMapper mapper = JacksonMapper.getInstance(); // create once, reuse
// byte[] body = mapper.writeValueAsBytes(msg); // 将对象转换为byte
// out.writeBytes(body); // 消息体中包含我们要发送的数据
ByteBufOutputStream byteBufOutputStream = new ByteBufOutputStream(out);
mapper.writeValue(byteBufOutputStream, msg);
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 说明:ObjectMapper 单例。create once, reuse
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日
*/
public class JacksonMapper {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* create once, reuse
* @return ObjectMapper 单例
*/
public static ObjectMapper getInstance() {
return MAPPER;
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* 说明:序列化服务器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public final class JacksonServer {
static final int PORT = 8082;
public static void main(String[] args) throws Exception {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new JacksonServerHandlerInitializer());
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class JacksonServerHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object obj)
throws Exception {
String jsonString = "";
if (obj instanceof JacksonBean) {
JacksonBean user = (JacksonBean)obj;
ctx.writeAndFlush(user);
jsonString = JacksonMapper.getInstance().writeValueAsString(user); // 对象转为json字符串
} else {
ctx.writeAndFlush(obj);
jsonString = JacksonMapper.getInstance().writeValueAsString(obj); // 对象转为json字符串
}
System.out.println("Server get msg form Client -" + jsonString);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Channel incoming = ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
... ...
package com.waylau.netty.demo.codec.jackcon;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
/**
* 说明:序列化服务器初始化
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class JacksonServerHandlerInitializer extends
ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new JacksonDecoder<JacksonBean>(JacksonBean.class));
pipeline.addLast(new JacksonEncoder());
pipeline.addLast(new JacksonServerHandler());
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.codec.jackcon;
import java.io.File;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
/**
* 说明:
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月8日
*/
public class JsonGeneratorDemo {
private static final String FILE_PATH = "d:\\user.json";
/**
*
*/
public JsonGeneratorDemo() {
// TODO Auto-generated constructor stub
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
try {
JsonFactory jfactory = new JsonFactory();
JsonGenerator jGenerator = jfactory.createJsonGenerator(new File(
FILE_PATH), JsonEncoding.UTF8);
jGenerator.writeStartObject(); // {
jGenerator.writeStringField("name", "mkyong"); // "name" : "mkyong"
jGenerator.writeNumberField("age", 29); // "age" : 29
jGenerator.writeFieldName("messages"); // "messages" :
jGenerator.writeStartArray(); // [
jGenerator.writeString("msg 1"); // "msg 1"
jGenerator.writeString("msg 2"); // "msg 2"
jGenerator.writeString("msg 3"); // "msg 3"
jGenerator.writeEndArray(); // ]
jGenerator.writeEndObject(); // }
jGenerator.close();
} catch (JsonGenerationException e) {
e.printStackTrace();
}
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.codec.jackcon;
import java.io.File;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
/**
* 说明:
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月8日
*/
public class JsonParserDemo {
private static final String FILE_PATH = "d:\\user.json";
/**
*
*/
public JsonParserDemo() {
// TODO Auto-generated constructor stub
}
/**
* @param args
* @throws IOException
* @throws JsonParseException
*/
public static void main(String[] args) throws JsonParseException,
IOException {
JsonFactory jfactory = new JsonFactory();
JsonParser jParser = jfactory.createJsonParser(new File(FILE_PATH));
// loop until token equal to "}"
while (jParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jParser.getCurrentName();
if ("name".equals(fieldname)) {
// current token is "name",
// move to next, which is "name"'s value
jParser.nextToken();
System.out.println(jParser.getText()); // display mkyong
}
if ("age".equals(fieldname)) {
// current token is "age",
// move to next, which is "name"'s value
jParser.nextToken();
System.out.println(jParser.getIntValue()); // display 29
}
if ("messages".equals(fieldname)) {
jParser.nextToken(); // current token is "[", move next
// messages is array, loop until token equal to "]"
while (jParser.nextToken() != JsonToken.END_ARRAY) {
// display msg1, msg2, msg3
System.out.println(jParser.getText());
}
}
}
jParser.close();
}
}
... ...
/**
*
*/
/**
* 说明:使用 Jackson json 来执行序列化
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月8日
*/
package com.waylau.netty.demo.codec.jackcon;
\ No newline at end of file
... ...
package com.waylau.netty.demo.codec.serialization;
import java.io.Serializable;
/**
* 说明:一个用户 POJO
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class SerializationBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 3235432002462705915L;
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
*
*/
public SerializationBean() {
// TODO Auto-generated constructor stub
}
}
... ...
package com.waylau.netty.demo.codec.serialization;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* 说明:序列化-客户端
*
* @author <a href="https://waylau.com">waylau.com</a> 2015年11月7日
*/
public class SerializationClient {
public static void main(String[] args) throws Exception{
new SerializationClient("localhost", 8082).run();
}
private final String host;
private final int port;
public SerializationClient(String host, int port){
this.host = host;
this.port = port;
}
public void run() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new SerializationClientHandlerInitializer());
Channel channel = bootstrap.connect(host, port).sync().channel();
SerializationBean user = new SerializationBean();
for (int i = 0; i < 100; i++) {
user = new SerializationBean();
user.setAge(i);
user.setName("waylau");
channel.write(user);
}
channel.flush();
// 等待连接关闭
channel.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.codec.serialization;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class SerializationClientHandler extends
SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object obj)
throws Exception {
if (obj instanceof SerializationBean) {
SerializationBean user = (SerializationBean) obj;
System.out.println("Client get msg form Server - name:"
+ user.getName() + ";age:" + user.getAge());
}
}
}
... ...
package com.waylau.netty.demo.codec.serialization;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
/**
* 说明:处理器初始化
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class SerializationClientHandlerInitializer extends
ChannelInitializer<Channel> {
private final static int MAX_OBJECT_SIZE = 1024 * 1024;
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ObjectDecoder(MAX_OBJECT_SIZE,
ClassResolvers.weakCachingConcurrentResolver(this.getClass()
.getClassLoader())));
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new SerializationClientHandler());
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.codec.serialization;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* 说明:序列化服务器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public final class SerializationServer {
static final int PORT = 8082;
public static void main(String[] args) throws Exception {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new SerializationServerHandlerInitializer());
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.codec.serialization;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class SerializationServerHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object obj)
throws Exception {
if (obj instanceof SerializationBean) {
SerializationBean user = (SerializationBean)obj;
ctx.writeAndFlush(user);
System.out.println("Server get msg form Client - name:"+ user.getName() + ";age:" + user.getAge());
}
}
}
... ...
package com.waylau.netty.demo.codec.serialization;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
/**
* 说明:序列化服务器初始化
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class SerializationServerHandlerInitializer extends
ChannelInitializer<Channel> {
private final static int MAX_OBJECT_SIZE = 1024 * 1024;
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ObjectDecoder(MAX_OBJECT_SIZE,
ClassResolvers.weakCachingConcurrentResolver(this.getClass()
.getClassLoader())));
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new SerializationServerHandler());
}
}
\ No newline at end of file
... ...
/**
*
*/
/**
* 说明:使用 Netty 自带的序列化工具对 Java 对象进行序列化
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
package com.waylau.netty.demo.codec.serialization;
\ No newline at end of file
... ...
package com.waylau.netty.demo.discard;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 丢弃任何进入的数据
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscardServer(port).run();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.discard;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* 处理服务端 channel.
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
/*
// 默默地丢弃收到的数据
((ByteBuf) msg).release(); // (3)
*/
/*
try {
// Do something with msg
} finally {
ReferenceCountUtil.release(msg);
}
*/
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) { // (1)
System.out.print((char) in.readByte());
System.out.flush();
}
} finally {
ReferenceCountUtil.release(msg); // (2)
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.echo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* Sends one message when a connection is open and echoes back any received
* data to the server. Simply put, the echo client initiates the ping-pong
* traffic between the echo client and server by sending the first message to
* the server.
*/
public final class EchoClient {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8040"));
public static void main(String[] args) throws Exception {
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
p.addLast("decoder", new StringDecoder());
p.addLast("encoder", new StringEncoder());
p.addLast(new EchoClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect(HOST, PORT).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.echo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
private final String firstMessage;
/**
* Creates a client-side handler.
*/
public EchoClientHandler() {
// firstMessage = Unpooled.buffer(EchoClient.SIZE);
// for (int i = 0; i < firstMessage.capacity(); i ++) {
// firstMessage.writeByte((byte) i);
// }
//// firstMessage.writeByte('\n');
firstMessage = "hello\n";
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage);
System.out.println("channel active.");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("channel read from server: " + msg);
ctx.write(msg + "\n");
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
System.out.println("channel read complete");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.echo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* 应答服务器
*/
public class EchoServer {
private int port;
public EchoServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new EchoServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
System.out.println("Server start listen at " + port );
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8040;
}
new EchoServer(port).run();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.echo;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.concurrent.TimeUnit;
/**
* 处理服务端 channel.
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString());
// ctx.write(msg); // (1)
// ctx.flush(); // (2)
// final ChannelFuture future = ctx.writeAndFlush(msg);
final ChannelFuture future = ctx.write(msg + "\n");
// future.addListener(ChannelFutureListener.CLOSE);
// future.addListener(new ChannelFutureListener() {
// @Override
// public void operationComplete(ChannelFuture future) throws Exception {
// System.out.println("close channel");
// future.channel().close();
// }
// });
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("server read complete");
ctx.flush();
TimeUnit.MILLISECONDS.sleep(200);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.factorial;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import java.math.BigInteger;
import java.util.List;
/**
* Decodes the binary representation of a {@link BigInteger} prepended
* with a magic number ('F' or 0x46) and a 32-bit integer length prefix into a
* {@link BigInteger} instance. For example, { 'F', 0, 0, 0, 1, 42 } will be
* decoded into new BigInteger("42").
*/
public class BigIntegerDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
// Wait until the length prefix is available.
if (in.readableBytes() < 5) {
return;
}
in.markReaderIndex();
// Check the magic number.
int magicNumber = in.readUnsignedByte();
if (magicNumber != 'F') {
in.resetReaderIndex();
throw new CorruptedFrameException("Invalid magic number: " + magicNumber);
}
// Wait until the whole data is available.
int dataLength = in.readInt();
if (in.readableBytes() < dataLength) {
in.resetReaderIndex();
return;
}
// Convert the received data into a new BigInteger.
byte[] decoded = new byte[dataLength];
in.readBytes(decoded);
out.add(new BigInteger(decoded));
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
/**
* Sends a sequence of integers to a {@link FactorialServer} to calculate
* the factorial of the specified integer.
*/
public final class FactorialClient {
static final boolean SSL = System.getProperty("ssl") != null;
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8322"));
static final int COUNT = Integer.parseInt(System.getProperty("count", "1000"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
} else {
sslCtx = null;
}
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new FactorialClientInitializer(sslCtx));
// Make a new connection.
ChannelFuture f = b.connect(HOST, PORT).sync();
// Get the handler instance to retrieve the answer.
FactorialClientHandler handler =
(FactorialClientHandler) f.channel().pipeline().last();
// Print out the answer.
System.err.format("Factorial of %,d is: %,d", COUNT, handler.getFactorial());
} finally {
group.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.math.BigInteger;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Handler for a client-side channel. This handler maintains stateful
* information which is specific to a certain channel using member variables.
* Therefore, an instance of this handler can cover only one channel. You have
* to create a new handler instance whenever you create a new channel and insert
* this handler to avoid a race condition.
*/
public class FactorialClientHandler extends SimpleChannelInboundHandler<BigInteger> {
private ChannelHandlerContext ctx;
private int receivedMessages;
private int next = 1;
final BlockingQueue<BigInteger> answer = new LinkedBlockingQueue<BigInteger>();
public BigInteger getFactorial() {
boolean interrupted = false;
try {
for (;;) {
try {
return answer.take();
} catch (InterruptedException ignore) {
interrupted = true;
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
this.ctx = ctx;
sendNumbers();
}
public void messageReceived(ChannelHandlerContext ctx, final BigInteger msg) {
receivedMessages ++;
if (receivedMessages == FactorialClient.COUNT) {
// Offer the answer after closing the connection.
ctx.channel().close().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
boolean offered = answer.offer(msg);
assert offered;
}
});
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
private void sendNumbers() {
// Do not send more than 4096 numbers.
ChannelFuture future = null;
for (int i = 0; i < 4096 && next <= FactorialClient.COUNT; i++) {
future = ctx.write(Integer.valueOf(next));
next++;
}
if (next <= FactorialClient.COUNT) {
assert future != null;
future.addListener(numberSender);
}
ctx.flush();
}
private final ChannelFutureListener numberSender = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
sendNumbers();
} else {
future.cause().printStackTrace();
future.channel().close();
}
}
};
@Override
protected void channelRead0(ChannelHandlerContext arg0, BigInteger arg1)
throws Exception {
// TODO Auto-generated method stub
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a client-side channel.
*/
public class FactorialClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public FactorialClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc(), FactorialClient.HOST, FactorialClient.PORT));
}
// Enable stream compression (you can remove these two if unnecessary)
pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
// Add the number codec first,
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder());
// and then business logic.
pipeline.addLast(new FactorialClientHandler());
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* Receives a sequence of integers from a {@link FactorialClient} to calculate
* the factorial of the specified integer.
*/
public final class FactorialServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8322"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new FactorialServerInitializer(sslCtx));
b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.math.BigInteger;
/**
* Handler for a server-side channel. This handler maintains stateful
* information which is specific to a certain channel using member variables.
* Therefore, an instance of this handler can cover only one channel. You have
* to create a new handler instance whenever you create a new channel and insert
* this handler to avoid a race condition.
*/
public class FactorialServerHandler extends SimpleChannelInboundHandler<BigInteger> {
private BigInteger lastMultiplier = new BigInteger("1");
private BigInteger factorial = new BigInteger("1");
public void messageReceived(ChannelHandlerContext ctx, BigInteger msg) {
// Calculate the cumulative factorial and send it to the client.
lastMultiplier = msg;
factorial = factorial.multiply(msg);
ctx.writeAndFlush(factorial);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.err.printf("Factorial of %,d is: %,d%n", lastMultiplier, factorial);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext arg0, BigInteger arg1)
throws Exception {
// TODO Auto-generated method stub
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a server-side channel.
*/
public class FactorialServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public FactorialServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
}
// Enable stream compression (you can remove these two if unnecessary)
pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
// Add the number codec first,
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder());
// and then business logic.
// Please note we create a handler for every new channel
// because it has stateful properties.
pipeline.addLast(new FactorialServerHandler());
}
}
... ...
package com.waylau.netty.demo.factorial;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.math.BigInteger;
/**
* 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
* will be encoded to { 'F', 0, 0, 0, 1, 42 }.
*/
public class NumberEncoder extends MessageToByteEncoder<Number> {
@Override
protected void encode(ChannelHandlerContext ctx, Number msg, ByteBuf out) {
// Convert to a BigInteger first for easier implementation.
BigInteger v;
if (msg instanceof BigInteger) {
v = (BigInteger) msg;
} else {
v = new BigInteger(String.valueOf(msg));
}
// Convert the number into a byte array.
byte[] data = v.toByteArray();
int dataLength = data.length;
// Write a message.
out.writeByte((byte) 'F'); // magic number
out.writeInt(dataLength); // data length
out.writeBytes(data); // data
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.file;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/**
* 说明:文件客户端
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class FileClient {
private String host;
private int port;
private String dest; // 接收到文件存放的路径
/**
*
*/
public FileClient(String host, int port, String dest) {
this.host = host;
this.port = port;
this.dest = dest;
}
public void run() throws InterruptedException, IOException {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("encoder",
new StringEncoder(CharsetUtil.UTF_8));
ch.pipeline().addLast("decoder",
new StringDecoder(CharsetUtil.UTF_8));
ch.pipeline().addLast(new FileClientHandler(dest));
}
});
// 启动客户端
ChannelFuture f = b.connect(host, port).sync(); // (5)
Channel channel = f.channel();
// 控制台输入请求的文件路径
BufferedReader in = new BufferedReader(new InputStreamReader(
System.in));
while (true) {
channel.writeAndFlush(in.readLine() + "\r\n");
}
// 等待连接关闭
// f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException, IOException {
new FileClient("localhost", 8082, "D:/reciveFile.txt").run();
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.file;
import java.io.File;
import java.io.FileOutputStream;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:文件客户端处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class FileClientHandler extends SimpleChannelInboundHandler<String> {
private String dest;
/**
*
* @param dest 文件生成路径
*/
public FileClientHandler(String dest) {
this.dest = dest;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
File file = new File(dest);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(msg.getBytes());
fos.close();
}
}
... ...
/*
* Copyright 2013-2018 Lilinfeng.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.waylau.netty.demo.file;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/**
* 说明:文件服务器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class FileServer {
public void run(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>() {
/*
* (non-Javadoc)
*
* @see
* io.netty.channel.ChannelInitializer#initChannel(io
* .netty.channel.Channel)
*/
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(
new StringEncoder(CharsetUtil.UTF_8),
new LineBasedFrameDecoder(1024),
new StringDecoder(CharsetUtil.UTF_8),
new FileServerHandler());
}
});
ChannelFuture f = b.bind(port).sync();
System.out.println("Server start at port : " + port);
f.channel().closeFuture().sync();
} finally {
// 优雅停机
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8082;
if (args.length > 0) {
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new FileServer().run(port);
}
}
... ...
/*
* Copyright 2013-2018 Lilinfeng.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.waylau.netty.demo.file;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultFileRegion;
import io.netty.channel.FileRegion;
import io.netty.channel.SimpleChannelInboundHandler;
import java.io.File;
import java.io.RandomAccessFile;
/**
* 说明:文件服务器处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class FileServerHandler extends SimpleChannelInboundHandler<String> {
private static final String CR = System.getProperty("line.separator");
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
File file = new File(msg);
if (file.exists()) {
if (!file.isFile()) {
ctx.writeAndFlush("Not a file : " + file + CR);
return;
}
ctx.write(file + " " + file.length() + CR);
RandomAccessFile randomAccessFile = new RandomAccessFile(msg, "r");
FileRegion region = new DefaultFileRegion(
randomAccessFile.getChannel(), 0, randomAccessFile.length());
ctx.write(region);
ctx.writeAndFlush(CR);
randomAccessFile.close();
} else {
ctx.writeAndFlush("File not found: " + file + CR);
}
}
}
... ...
/**
*
*/
/**
* 说明:本包主要是要演示了文件服务器的功能.
* 客户端启动时,会指定一个文件要保存的路径,本例为“D:/reciveFile.txt”。
* 客户端发送文件的请求,需在控制台输入所请求文件的路径(当然为了简单演示,该文件是服务器上的文件),
* 而后,服务器会将该文件传送给客户端端,客户端将文件内容写入“D:/reciveFile.txt”
*
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
package com.waylau.netty.demo.file;
\ No newline at end of file
... ...
package com.waylau.netty.demo.heartbeat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
/**
* 说明:心跳服务器初始化
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class HeartbeatHandlerInitializer extends ChannelInitializer<Channel> {
private static final int READ_IDEL_TIME_OUT = 4; // 读超时
private static final int WRITE_IDEL_TIME_OUT = 5;// 写超时
private static final int ALL_IDEL_TIME_OUT = 7; // 所有超时
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new IdleStateHandler(READ_IDEL_TIME_OUT,
WRITE_IDEL_TIME_OUT, ALL_IDEL_TIME_OUT, TimeUnit.SECONDS));
pipeline.addLast(new HeartbeatServerHandler());
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.heartbeat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* 说明:心跳服务器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public final class HeartbeatServer {
static final int PORT = 8082;
public static void main(String[] args) throws Exception {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new HeartbeatHandlerInitializer());
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.heartbeat;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
/**
* 说明:心跳服务器处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
public class HeartbeatServerHandler extends ChannelInboundHandlerAdapter {
// Return a unreleasable view on the given ByteBuf
// which will just ignore release and retain calls.
private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled
.unreleasableBuffer(Unpooled.copiedBuffer("Heartbeat",
CharsetUtil.UTF_8));
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
String type = "";
if (event.state() == IdleState.READER_IDLE) {
type = "read idle";
} else if (event.state() == IdleState.WRITER_IDLE) {
type = "write idle";
} else if (event.state() == IdleState.ALL_IDLE) {
type = "all idle";
}
ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener(
ChannelFutureListener.CLOSE_ON_FAILURE);
System.out.println( ctx.channel().remoteAddress()+"超时类型:" + type);
} else {
super.userEventTriggered(ctx, evt);
}
}
}
... ...
/**
*
*/
/**
* 说明:本例子是一个心跳服务器程序。
* 可以设置连接超时时间。如果连接超时,则服务器发送心跳消息给客户端。
* 本例的客户端可以是 Telnet 程序。
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日
*/
package com.waylau.netty.demo.heartbeat;
\ No newline at end of file
... ...
package com.waylau.netty.demo.protocol;
/**
* 说明:
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class ClientTask implements Runnable {
/**
*
*/
public ClientTask() {
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
// TODO Auto-generated method stub
try {
ProtocolClient client = new ProtocolClient("localhost", 8082);
client.run();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.protocol;
/**
* 说明:消息类型
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public enum MsgType {
EMGW_LOGIN_REQ((byte) 0x00),
EMGW_LOGIN_RES((byte) 0x01);
private byte value;
public byte getValue() {
return value;
}
private MsgType(byte value) {
this.value = value;
}
}
... ...
package com.waylau.netty.demo.protocol;
import java.nio.charset.Charset;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* 说明:自定义协议客户端
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class ProtocolClient {
private String host;
private int port;
private static final int MAX_FRAME_LENGTH = 1024 * 1024;
private static final int LENGTH_FIELD_LENGTH = 4;
private static final int LENGTH_FIELD_OFFSET = 6;
private static final int LENGTH_ADJUSTMENT = 0;
private static final int INITIAL_BYTES_TO_STRIP = 0;
/**
*
*/
public ProtocolClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws InterruptedException {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
"decoder",
new ProtocolDecoder(MAX_FRAME_LENGTH,
LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH,
LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
ch.pipeline().addLast("encoder", new ProtocolEncoder());
ch.pipeline().addLast(new ProtocolClientHandler());
}
});
// 启动客户端
ChannelFuture f = b.connect(host, port).sync(); // (5)
while (true) {
// 发送消息给服务器
ProtocolMsg msg = new ProtocolMsg();
ProtocolHeader protocolHeader = new ProtocolHeader();
protocolHeader.setMagic((byte) 0x01);
protocolHeader.setMsgType((byte) 0x01);
protocolHeader.setReserve((short) 0);
protocolHeader.setSn((short) 0);
String body = "床前明月光疑是地上霜";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 2700; i++) {
sb.append(body);
}
byte[] bodyBytes = sb.toString().getBytes(
Charset.forName("utf-8"));
int bodySize = bodyBytes.length;
protocolHeader.setLen(bodySize);
msg.setProtocolHeader(protocolHeader);
msg.setBody(sb.toString());
f.channel().writeAndFlush(msg);
Thread.sleep(2000);
}
// 等待连接关闭
// f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
new ProtocolClient("localhost", 8082).run();
}
}
... ...
package com.waylau.netty.demo.protocol;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class ProtocolClientHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object obj)
throws Exception {
Channel incoming = ctx.channel();
System.out.println("Server->Client:"+incoming.remoteAddress()+obj.toString());
if(obj instanceof ProtocolMsg) {
ProtocolMsg msg = (ProtocolMsg)obj;
System.out.println("Server->Client:"+incoming.remoteAddress()+msg.getBody());
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//ctx.flush();
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.protocol;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* 说明:自定义协议客户端性能测试
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class ProtocolClientTest {
private static final int POOL_SIZE_SEND = 100;
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Executor executor = Executors.newFixedThreadPool(POOL_SIZE_SEND);
for (int i = 0; i < POOL_SIZE_SEND; i++) {
executor.execute(new ClientTask());
Thread.sleep(100);
}
}
}
... ...
package com.waylau.netty.demo.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
/**
* 说明:
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月11日
*/
public class ProtocolDecoder extends LengthFieldBasedFrameDecoder {
private static final int HEADER_SIZE = 10;
private byte magic; // 魔数
private byte msgType; // 消息类型
private short reserve; // 保留字
private short sn; // 序列号
private int len; // 长度
/**
* @param maxFrameLength
* @param lengthFieldOffset
* @param lengthFieldLength
* @param lengthAdjustment
* @param initialBytesToStrip
*/
public ProtocolDecoder(int maxFrameLength, int lengthFieldOffset,
int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength,
lengthAdjustment, initialBytesToStrip);
}
/**
* @param maxFrameLength
* @param lengthFieldOffset
* @param lengthFieldLength
* @param lengthAdjustment
* @param initialBytesToStrip
* @param failFast
*/
public ProtocolDecoder(int maxFrameLength, int lengthFieldOffset,
int lengthFieldLength, int lengthAdjustment,
int initialBytesToStrip, boolean failFast) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength,
lengthAdjustment, initialBytesToStrip, failFast);
// TODO Auto-generated constructor stub
}
@Override
protected ProtocolMsg decode(ChannelHandlerContext ctx, ByteBuf in2) throws Exception {
ByteBuf in = (ByteBuf) super.decode(ctx, in2);
if (in == null) {
return null;
}
if (in.readableBytes() < HEADER_SIZE) {
return null;// response header is 10 bytes
}
magic = in.readByte();
msgType = in.readByte();
reserve = in.readShort();
sn = in.readShort();
len = in.readInt();
if (in.readableBytes() < len) {
return null; // until we have the entire payload return
}
ByteBuf buf = in.readBytes(len);
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
ProtocolMsg msg = new ProtocolMsg();
ProtocolHeader protocolHeader = new ProtocolHeader(magic, msgType,
reserve, sn, len);
msg.setBody(body);
msg.setProtocolHeader(protocolHeader);
return msg;
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.protocol;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
* 说明:
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月10日
*/
public class ProtocolDecoderDeprecation extends ByteToMessageDecoder {
private static final int HEADER_SIZE = 10;
private byte magic; // 魔数
private byte msgType; // 消息类型
private short reserve; // 保留字
private short sn; // 序列号
private int len; // 长度
/**
*
*/
public ProtocolDecoderDeprecation() {
// TODO Auto-generated constructor stub
}
/*
* (non-Javadoc)
*
* @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel.
* ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List)
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
if (in.readableBytes() < HEADER_SIZE) {
return;// response header is 10 bytes
}
magic = in.readByte();
msgType = in.readByte();
reserve = in.readShort();
sn = in.readShort();
len = in.readInt();
if (in.readableBytes() < len) {
return; // until we have the entire payload return
}
ByteBuf buf = in.readBytes(len);
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
ProtocolMsg msg = new ProtocolMsg();
// ProtocolBody body2 = new ProtocolBody();
// body2.setBody(body);
ProtocolHeader protocolHeader = new ProtocolHeader(magic, msgType,
reserve, sn, len);
//msg.setProtocolBody(body2);
msg.setBody(body);
msg.setProtocolHeader(protocolHeader);
out.add(msg);
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.protocol;
import java.nio.charset.Charset;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* 说明:编码器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月10日
*/
public class ProtocolEncoder extends MessageToByteEncoder<ProtocolMsg> {
/**
*
*/
public ProtocolEncoder() {
// TODO Auto-generated constructor stub
}
/**
* @param outboundMessageType
*/
public ProtocolEncoder(Class<? extends ProtocolMsg> outboundMessageType) {
super(outboundMessageType);
// TODO Auto-generated constructor stub
}
/**
* @param preferDirect
*/
public ProtocolEncoder(boolean preferDirect) {
super(preferDirect);
// TODO Auto-generated constructor stub
}
/**
* @param outboundMessageType
* @param preferDirect
*/
public ProtocolEncoder(Class<? extends ProtocolMsg> outboundMessageType,
boolean preferDirect) {
super(outboundMessageType, preferDirect);
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* @see io.netty.handler.codec.MessageToByteEncoder#encode(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.buffer.ByteBuf)
*/
@Override
protected void encode(ChannelHandlerContext ctx, ProtocolMsg msg,
ByteBuf out) throws Exception {
if (msg == null | msg.getProtocolHeader() == null) {
throw new Exception("The encode message is null");
}
ProtocolHeader header = msg.getProtocolHeader();
String body = msg.getBody();
byte[] bodyBytes = body.getBytes(Charset.forName("utf-8"));
int bodySize = bodyBytes.length;
out.writeByte(header.getMagic());
out.writeByte(header.getMsgType());
out.writeShort(header.getReserve());
out.writeShort(header.getSn());
out.writeInt(bodySize);
out.writeBytes(bodyBytes);
}
}
... ...
package com.waylau.netty.demo.protocol;
/**
* 说明:协议消息头
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月4日
*/
public class ProtocolHeader{
private byte magic; // 魔数
private byte msgType; // 消息类型
private short reserve; // 保留字
private short sn; // 序列号
private int len; // 长度
public byte getMagic() {
return magic;
}
public void setMagic(byte magic) {
this.magic = magic;
}
public byte getMsgType() {
return msgType;
}
public void setMsgType(byte msgType) {
this.msgType = msgType;
}
public short getReserve() {
return reserve;
}
public void setReserve(short reserve) {
this.reserve = reserve;
}
public short getSn() {
return sn;
}
public void setSn(short sn) {
this.sn = sn;
}
public int getLen() {
return len;
}
public void setLen(int len) {
this.len = len;
}
public ProtocolHeader() {
}
/**
*
*/
public ProtocolHeader(byte magic, byte msgType,short reserve,short sn,int len) {
this.magic = magic;
this.msgType = msgType;
this.reserve = reserve;
this.sn = sn;
this.len = len;
}
}
... ...
package com.waylau.netty.demo.protocol;
/**
* 说明:消息对象
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class ProtocolMsg {
private ProtocolHeader protocolHeader = new ProtocolHeader();
private String body;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
/**
*
*/
public ProtocolMsg() {
// TODO Auto-generated constructor stub
}
public ProtocolHeader getProtocolHeader() {
return protocolHeader;
}
public void setProtocolHeader(ProtocolHeader protocolHeader) {
this.protocolHeader = protocolHeader;
}
}
... ...
/**
*
*/
package com.waylau.netty.demo.protocol;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 说明:自定义协议服务端
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日
*/
public class ProtocolServer {
private int port;
private static final int MAX_FRAME_LENGTH = 1024 * 1024;
private static final int LENGTH_FIELD_LENGTH = 4;
private static final int LENGTH_FIELD_OFFSET = 6;
private static final int LENGTH_ADJUSTMENT = 0;
private static final int INITIAL_BYTES_TO_STRIP = 0;
/**
*
*/
public ProtocolServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder",
new ProtocolDecoder(MAX_FRAME_LENGTH,
LENGTH_FIELD_OFFSET,LENGTH_FIELD_LENGTH,
LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
ch.pipeline().addLast("encoder", new ProtocolEncoder());
ch.pipeline().addLast(new ProtocolServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
System.out.println("Server start listen at " + port );
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8082;
}
new ProtocolServer(port).run();
}
}
... ...
package com.waylau.netty.demo.protocol;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 说明:处理器
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日
*/
public class ProtocolServerHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object obj)
throws Exception {
Channel incoming = ctx.channel();
if(obj instanceof ProtocolMsg) {
ProtocolMsg msg = (ProtocolMsg)obj;
System.out.println("Client->Server:"+incoming.remoteAddress()+msg.getBody());
incoming.write(obj);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
}
... ...
/**
* 说明:该包下的例子,主要是实现了自定义二进制协议
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月4日
*/
package com.waylau.netty.demo.protocol;
... ...
## 消息格式
类型 | 名称 | 字节序列 | 取值范围 | 备注
--- | ----- | ---------| --------- |----
消息头 | magic | 0 |0x80、0x81 |帧头
| msgType | 1 |0x00-0xff |消息类型
| reserve | 2-3 |0x00 |保留字,以备扩展
| sn |4-5 |0-32767 |序列号。是一个事务标识,从0开始,每次递增1,响应消息中的序列号是从请求消息中拷贝来的。当序列号达到最大值时(32767),则又从0开始。
| len |6-9 |0-2147483647 |消息体长度。
消息体 | body |变长 |0- |消息体。格式和消息类型相关,不同的消息类型有不同的消息体格式。消息大小不应超过2G
... ...
package com.waylau.netty.demo.securechat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* Simple SSL chat client
*/
public final class SecureChatClient {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8992"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new SecureChatClientInitializer(sslCtx));
// Start the connection attempt.
Channel ch = b.connect(HOST, PORT).sync().channel();
// Read commands from the stdin.
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
break;
}
// Sends the received line to the server.
lastWriteFuture = ch.writeAndFlush(line + "\r\n");
// If user typed the 'bye' command, wait until the server closes
// the connection.
if ("bye".equals(line.toLowerCase())) {
ch.closeFuture().sync();
break;
}
}
// Wait until all messages are flushed before closing the channel.
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
} finally {
// The connection is closed automatically on shutdown.
group.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.securechat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* Handles a client-side channel.
*/
public class SecureChatClientHandler extends SimpleChannelInboundHandler<String> {
public void messageReceived(ChannelHandlerContext ctx, String msg) {
System.err.println(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
// TODO Auto-generated method stub
}
}
... ...
package com.waylau.netty.demo.securechat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public SecureChatClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.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.
pipeline.addLast(sslCtx.newHandler(ch.alloc(), SecureChatClient.HOST, SecureChatClient.PORT));
// On top of the SSL handler, add the text line codec.
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
// and then business logic.
pipeline.addLast(new SecureChatClientHandler());
}
}
... ...
package com.waylau.netty.demo.securechat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* Simple SSL chat server
*/
public final class SecureChatServer {
static final int PORT = Integer.parseInt(System.getProperty("port", "8992"));
public static void main(String[] args) throws Exception {
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new SecureChatServerInitializer(sslCtx));
b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.securechat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.net.InetAddress;
/**
* Handles a server-side channel.
*/
public class SecureChatServerHandler extends SimpleChannelInboundHandler<String> {
static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelActive(final ChannelHandlerContext ctx) {
// Once session is secured, send a greeting and register the channel to the global channel
// list so the channel received the messages from others.
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(Future<Channel> future) throws Exception {
ctx.writeAndFlush(
"Welcome to " + InetAddress.getLocalHost().getHostName() + " secure chat service!\n");
ctx.writeAndFlush(
"Your session is protected by " +
ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
" cipher suite.\n");
channels.add(ctx.channel());
}
});
}
public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
// Send the received message to all channels but the current one.
for (Channel c: channels) {
if (c != ctx.channel()) {
c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n');
} else {
c.writeAndFlush("[you] " + msg + '\n');
}
}
// Close the connection if the client has sent 'bye'.
if ("bye".equals(msg.toLowerCase())) {
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
// TODO Auto-generated method stub
}
}
... ...
package com.waylau.netty.demo.securechat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public SecureChatServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.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.
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
// On top of the SSL handler, add the text line codec.
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
// and then business logic.
pipeline.addLast(new SecureChatServerHandler());
}
}
... ...
package com.waylau.netty.demo.simplechat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 简单聊天服务器-客户端
*
* @author waylau.com
* @date 2015-2-26
*/
public class SimpleChatClient {
public static void main(String[] args) throws Exception{
new SimpleChatClient("localhost", 8080).run();
}
private final String host;
private final int port;
public SimpleChatClient(String host, int port){
this.host = host;
this.port = port;
}
public void run() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new SimpleChatClientInitializer());
Channel channel = bootstrap.connect(host, port).sync().channel();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true){
channel.writeAndFlush(in.readLine() + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.simplechat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 客户端 channel
*
* @author waylau.com
* @date 2015-2-26
*/
public class SimpleChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
System.out.println(s);
}
}
... ...
package com.waylau.netty.demo.simplechat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* 客户端 ChannelInitializer
*
* @author waylau.com
* @date 2015-2-26
*/
public class SimpleChatClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SimpleChatClientHandler());
}
}
... ...
package com.waylau.netty.demo.simplechat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 简单聊天服务器-服务端
*
* @author waylau.com
* @date 2015-2-16
*/
public class SimpleChatServer {
private int port;
public SimpleChatServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new SimpleChatServerInitializer()) //(4)
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
System.out.println("SimpleChatServer 启动了");
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("SimpleChatServer 关闭了");
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new SimpleChatServer(port).run();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.simplechat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
/**
* 服务端 channel
*
* @author waylau.com
* @date 2015-2-16
*/
public class SimpleChatServerHandler extends SimpleChannelInboundHandler<String> { // (1)
/**
* A thread-safe Set Using ChannelGroup, you can categorize Channels into a meaningful group.
* A closed Channel is automatically removed from the collection,
*/
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2)
Channel incoming = ctx.channel();
// Broadcast a message to multiple Channels
channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
channels.add(ctx.channel());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel incoming = ctx.channel();
// Broadcast a message to multiple Channels
channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
// A closed Channel is automatically removed from ChannelGroup,
// so there is no need to do "channels.remove(ctx.channel());"
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { // (4)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
if (channel != incoming){
channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + s + "\n");
} else {
channel.writeAndFlush("[you]" + s + "\n");
}
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
Channel incoming = ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"在线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
Channel incoming = ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"掉线");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Channel incoming = ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.simplechat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* 服务端 ChannelInitializer
*
* @author waylau.com
* @date 2015-2-26
*/
public class SimpleChatServerInitializer extends
ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SimpleChatServerHandler());
System.out.println("SimpleChatClient:"+ch.remoteAddress() +"连接上");
}
}
... ...
package com.waylau.netty.demo.telnet;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* Simplistic telnet client.
*/
public final class TelnetClient {
static final boolean SSL = System.getProperty("ssl") != null;
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
} else {
sslCtx = null;
}
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new TelnetClientInitializer(sslCtx));
// Start the connection attempt.
Channel ch = b.connect(HOST, PORT).sync().channel();
// Read commands from the stdin.
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
break;
}
// Sends the received line to the server.
lastWriteFuture = ch.writeAndFlush(line + "\r\n");
// If user typed the 'bye' command, wait until the server closes
// the connection.
if ("bye".equals(line.toLowerCase())) {
ch.closeFuture().sync();
break;
}
}
// Wait until all messages are flushed before closing the channel.
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
} finally {
group.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.telnet;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* Handles a client-side channel.
*/
@Sharable
public class TelnetClientHandler extends SimpleChannelInboundHandler<String> {
protected void messageReceived(ChannelHandlerContext ctx, String msg) {
System.err.println(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
// TODO Auto-generated method stub
}
}
... ...
package com.waylau.netty.demo.telnet;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class TelnetClientInitializer extends ChannelInitializer<SocketChannel> {
private static final StringDecoder DECODER = new StringDecoder();
private static final StringEncoder ENCODER = new StringEncoder();
private static final TelnetClientHandler CLIENT_HANDLER = new TelnetClientHandler();
private final SslContext sslCtx;
public TelnetClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc(), TelnetClient.HOST, TelnetClient.PORT));
}
// Add the text line codec combination first,
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(ENCODER);
// and then business logic.
pipeline.addLast(CLIENT_HANDLER);
}
}
... ...
package com.waylau.netty.demo.telnet;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* Simplistic telnet server.
*/
public final class TelnetServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new TelnetServerInitializer(sslCtx));
b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.telnet;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.InetAddress;
import java.util.Date;
/**
* Handles a server-side channel.
*/
@Sharable
public class TelnetServerHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Send greeting for a new connection.
ctx.write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n");
ctx.write("It is " + new Date() + " now.\r\n");
ctx.flush();
}
public void messageReceived(ChannelHandlerContext ctx, String request) {
// Generate and write a response.
String response;
boolean close = false;
if (request.isEmpty()) {
response = "Please type something.\r\n";
} else if ("bye".equals(request.toLowerCase())) {
response = "Have a good day!\r\n";
close = true;
} else {
response = "Did you say '" + request + "'?\r\n";
}
// We do not need to write a ChannelBuffer here.
// We know the encoder inserted at TelnetPipelineFactory will do the conversion.
ChannelFuture future = ctx.write(response);
// Close the connection after sending 'Have a good day!'
// if the client has sent 'bye'.
if (close) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
// TODO Auto-generated method stub
}
}
... ...
package com.waylau.netty.demo.telnet;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class TelnetServerInitializer extends ChannelInitializer<SocketChannel> {
private static final StringDecoder DECODER = new StringDecoder();
private static final StringEncoder ENCODER = new StringEncoder();
private static final TelnetServerHandler SERVER_HANDLER = new TelnetServerHandler();
private final SslContext sslCtx;
public TelnetServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
}
// Add the text line codec combination first,
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
// the encoder and decoder are static as these are sharable
pipeline.addLast(DECODER);
pipeline.addLast(ENCODER);
// and then business logic.
pipeline.addLast(SERVER_HANDLER);
}
}
... ...
package com.waylau.netty.demo.time;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class TimeClient {
public static void main(String[] args) throws Exception {
String host = "127.0.0.1";// args[0];
int port = 8080;//Integer.parseInt(args[1]);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
// 启动客户端
ChannelFuture f = b.connect(host, port).sync(); // (5)
// 等待连接关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
... ...
package com.waylau.netty.demo.time;
import java.util.Date;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf m = (ByteBuf) msg; // (1)
try {
long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
ctx.close();
} finally {
m.release();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
... ...
package com.waylau.netty.demo.time;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 时间服务器
*/
public class TimeServer {
private int port;
public TimeServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new TimeServer(port).run();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.time;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(final ChannelHandlerContext ctx) { // (1)
final ByteBuf time = ctx.alloc().buffer(4); // (2)
time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time); // (3)
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
assert f == future;
ctx.close();
}
}); // (4)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
... ...
package com.waylau.netty.demo.websocketchat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultFileRegion;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedNioFile;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.URISyntaxException;
import java.net.URL;
/**
* 处理 Http 请求
* @author waylau.com
* @date 2015-3-26
*/
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1
private final String wsUri;
private static final File INDEX;
static {
URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation();
try {
String path = location.toURI() + "WebsocketChatClient.html";
path = !path.contains("file:") ? path : path.substring(5);
INDEX = new File(path);
} catch (URISyntaxException e) {
throw new IllegalStateException("Unable to locate WebsocketChatClient.html", e);
}
}
public HttpRequestHandler(String wsUri) {
this.wsUri = wsUri;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (wsUri.equalsIgnoreCase(request.getUri())) {
ctx.fireChannelRead(request.retain()); //2
} else {
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx); //3
}
RandomAccessFile file = new RandomAccessFile(INDEX, "r");//4
HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK);
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8");
boolean keepAlive = HttpHeaders.isKeepAlive(request);
if (keepAlive) { //5
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length());
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
}
ctx.write(response); //6
if (ctx.pipeline().get(SslHandler.class) == null) { //7
ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
} else {
ctx.write(new ChunkedNioFile(file.getChannel()));
}
ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); //8
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE); //9
}
file.close();
}
}
private static void send100Continue(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
ctx.writeAndFlush(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
Channel incoming = ctx.channel();
System.out.println("Client:"+incoming.remoteAddress()+"异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
... ...
package com.waylau.netty.demo.websocketchat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
/**
* 处理TextWebSocketFrame
*
* @author waylau.com
* 2015年3月26日
*/
public class TextWebSocketFrameHandler extends
SimpleChannelInboundHandler<TextWebSocketFrame> {
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext ctx,
TextWebSocketFrame msg) throws Exception { // (1)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
if (channel != incoming){
channel.writeAndFlush(new TextWebSocketFrame("[" + incoming.remoteAddress() + "]" + msg.text()));
} else {
channel.writeAndFlush(new TextWebSocketFrame("[you]" + msg.text() ));
}
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2)
Channel incoming = ctx.channel();
// Broadcast a message to multiple Channels
channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入"));
channels.add(incoming);
System.out.println("Client:"+incoming.remoteAddress() +"加入");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel incoming = ctx.channel();
// Broadcast a message to multiple Channels
channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开"));
System.out.println("Client:"+incoming.remoteAddress() +"离开");
// A closed Channel is automatically removed from ChannelGroup,
// so there is no need to do "channels.remove(ctx.channel());"
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
Channel incoming = ctx.channel();
System.out.println("Client:"+incoming.remoteAddress()+"在线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
Channel incoming = ctx.channel();
System.out.println("Client:"+incoming.remoteAddress()+"掉线");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) // (7)
throws Exception {
Channel incoming = ctx.channel();
System.out.println("Client:"+incoming.remoteAddress()+"异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
... ...
package com.waylau.netty.demo.websocketchat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* Websocket 聊天服务器-服务端
*
* @author waylau.com
* @date 2015-3-7
*/
public class WebsocketChatServer {
private int port;
public WebsocketChatServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new WebsocketChatServerInitializer()) //(4)
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
System.out.println("WebsocketChatServer 启动了" + port);
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("WebsocketChatServer 关闭了");
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new WebsocketChatServer(port).run();
}
}
\ No newline at end of file
... ...
package com.waylau.netty.demo.websocketchat;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* 服务端 ChannelInitializer
*
* @author waylau.com
* @date 2015-3-13
*/
public class WebsocketChatServerInitializer extends
ChannelInitializer<SocketChannel> { //1
@Override
public void initChannel(SocketChannel ch) throws Exception {//2
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(64*1024));
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpRequestHandler("/ws"));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(new TextWebSocketFrameHandler());
}
}
... ...
/**
*
*/
package com.waylau.netty.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 说明:
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日
*/
public class ByteObjConverter {
public static Object ByteToObject(byte[] bytes) {
Object obj = null;
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
ObjectInputStream oi = null;
try {
oi = new ObjectInputStream(bi);
obj = oi.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bi.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
oi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return obj;
}
public static byte[] ObjectToByte(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = null;
try {
oo = new ObjectOutputStream(bo);
oo.writeObject(obj);
bytes = bo.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
oo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return (bytes);
}
}
... ...
/**
*
*/
/**
* 说明:工具包
*
* @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月10日
*/
package com.waylau.netty.util;
\ No newline at end of file
... ...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
</head>
<body>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/ws");
socket.onmessage = function(event) {
var ta = document.getElementById('responseText');
ta.value = ta.value + '\n' + event.data
};
socket.onopen = function(event) {
var ta = document.getElementById('responseText');
ta.value = "连接开启!";
};
socket.onclose = function(event) {
var ta = document.getElementById('responseText');
ta.value = ta.value + "连接被关闭";
};
} else {
alert("你的浏览器不支持 WebSocket!");
}
function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("连接没有开启.");
}
}
</script>
<form onsubmit="return false;">
<h3>WebSocket 聊天室:</h3>
<textarea id="responseText" style="width: 500px; height: 300px;"></textarea>
<br>
<input type="text" name="message" style="width: 300px" value="Welcome to www.waylau.com">
<input type="button" value="发送消息" onclick="send(this.form.message.value)">
<input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空聊天记录">
</form>
<br>
<br>
<a href="https://waylau.com/" >更多例子请访问 waylau.com</a>
</body>
</html>
\ No newline at end of file
... ...
package com.waylau.netty;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}
... ...