为学习Netty,先做个EchoServer练练手吧。
目标效果:CMD tenlet上去,任意输入字符串后回车,服务端返回相同字符串
ChannelHandlerAdapter.java
package com.skymr.netty.echo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class EchoHandler extends ChannelHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println(buf.toString(io.netty.util.CharsetUtil.US_ASCII));
ByteBuf outBuf = ctx.alloc().buffer();
//收到什么消息,就回什么消息,cmd控制台是gbk编码,这里的中文要转GBK
outBuf.writeBytes("你说:".getBytes("GBK"));
outBuf.writeBytes(buf);
outBuf.writeBytes("\r\n".getBytes());
ctx.writeAndFlush(outBuf).sync();
outBuf.release();
}
}
假设收到的消息msg就已经处理过了,是这种格式:字符串+\r\n;
因为我想使用临时变量outBuf后,对它进行释放,所以在writeAndFlush调用后再调用sync方法,等发送消息后调用release释放临时ByteBuf,这样做是不是不太好呢,应该给它加个listener,release方法在listener方法中调用,这样的话,outBuf就要声明成final的了,不太喜欢这样做。
那怎么样处理消息协议,让收到的msg格式正确呢?
在中间加一个ByteToMessageDecoder
package com.skymr.netty.echo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* 解码器
* @author skymr
*
*/
public class EchoDecoder extends ByteToMessageDecoder{
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
int len = in.readableBytes();
if(len < 2){
return;
}
//取得末尾两个字节,若是\r\n,则处理 缓存中的数据
//若不是\r\n,则保留不处理
ByteBuf copyBuf = in.copy(len-2, 2);
byte byteFirst = copyBuf.readByte();
byte byteSecond = copyBuf.readByte();
if(byteFirst=='\r' && byteSecond=='\n'){
out.add(in.readBytes(len - 2));
in.skipBytes(2);
}
copyBuf.release();
}
}
ByteToMessageDecoder也是个ChannelHandler,所以在注册handler是一样的
.childHandler(new ChannelInitializer<SocketChannel>() {
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoDecoder(),new EchoHandler());
}
})
public class EchoServer {
private int port = 8888;
public void startServer() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//添加解码器与消息处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoDecoder(),new EchoHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = bootstrap.bind(port).sync();
System.out.println("bind completed.");
f.channel().closeFuture().sync();
System.out.println("channel closed.");
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("关闭了");
}
}
public static void main(String[] args) throws Exception{
new EchoServer().startServer();
}
}
就这个例子就可再出mina与netty的一点区别了
netty没有IoSession,此例子若用mina来做,字符串是可以放到session中的